API¶
init module.
in_this_repo
module-attribute
¶
in_this_repo = Path.cwd().name == snake_to_kebab_case(
winidjango.__name__
)
tests_package
module-attribute
¶
tests_package = import_module_with_file_fallback(
path=ProjectTester.I.package_root(),
name=ProjectTester.I.package_name(),
)
ProjectTester ¶
Bases: ProjectTester
Subclass of ProjectTester for customizing pyrig behavior.
core ¶
src package.
This package exposes the project's internal modules used by the
command-line utilities and database helpers. It exists primarily so
that code under winidjango/src can be imported using the
winidjango.core package path in other modules and tests.
The package itself contains the following subpackages:
- commands - management command helpers and base classes
- db - database utilities and model helpers
Consumers should import the specific submodules they need rather than relying on side effects from this package's import-time execution.
commands ¶
Utilities and base classes for management commands.
The commands package contains base command classes and helpers used to
implement Django management commands in the project. Subpackages and
modules under commands provide reusable patterns for argument handling,
logging and common command behaviors so that individual commands can focus
on their business logic.
base ¶
Base helpers and abstractions for management commands.
This package provides a common abstract base class used by project management commands. The base class centralizes argument handling, standard options (dry-run, batching, timeouts, etc.) and integrates logging behavior used throughout the commands package.
base ¶
Utilities and an abstract base class for Django management commands.
This module defines :class:ABCBaseCommand, a reusable abstract base
that combines Django's BaseCommand with the project's logging
mixins and standard argument handling. The base class implements a
template method pattern so concrete commands only need to implement the
abstract extension points for providing command-specific arguments and
business logic.
ABCBaseCommand ¶
Bases: ABCLoggingMixin, BaseCommand
Abstract base class for management commands with logging and standard options.
The class wires common behavior such as base arguments (dry-run,
batching, timeouts) and provides extension points that concrete
commands must implement: :meth:add_command_arguments and
:meth:handle_command.
Notes
- Inheritance order matters: the logging mixin must precede
BaseCommandso mixin initialization occurs as expected. - The base class follows the template method pattern; concrete
commands should not override :meth:
add_argumentsor :meth:handlebut implement the abstract hooks instead.
Just a container class for hard coding the option keys.
add_arguments(parser: ArgumentParser) -> None
Configure command-line arguments for the command.
Adds common base arguments (dry-run, force, delete, timeout,
batching and concurrency options) and then delegates to
:meth:add_command_arguments for command-specific options.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parser
|
ArgumentParser
|
The argument parser passed by Django. |
required |
Source code in src/winidjango/core/commands/base/base.py
abstractmethod
¶add_command_arguments(parser: ArgumentParser) -> None
Define command-specific arguments.
Implement this hook to add options and positional arguments that are specific to the concrete management command.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parser
|
ArgumentParser
|
The argument parser passed by Django. |
required |
Source code in src/winidjango/core/commands/base/base.py
base_add_arguments(parser: ArgumentParser) -> None
Add the project's standard command-line arguments to parser.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parser
|
ArgumentParser
|
The argument parser passed by Django. |
required |
Source code in src/winidjango/core/commands/base/base.py
Perform common pre-processing for commands.
Stores the incoming arguments and options on the instance for use
by :meth:handle_command and subclasses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*args
|
Any
|
Positional arguments forwarded from Django. |
()
|
**options
|
Any
|
Parsed command-line options. |
{}
|
Source code in src/winidjango/core/commands/base/base.py
Retrieve a parsed command option by key.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
option
|
str
|
The option key to retrieve from |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Any |
Any
|
The value for the requested option. If the option is not
present a |
Source code in src/winidjango/core/commands/base/base.py
Orchestrate command execution.
Performs shared pre-processing by calling :meth:base_handle and
then delegates to :meth:handle_command which must be implemented
by subclasses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*args
|
Any
|
Positional arguments forwarded from Django. |
()
|
**options
|
Any
|
Parsed command-line options. |
{}
|
Source code in src/winidjango/core/commands/base/base.py
abstractmethod
¶Run the command-specific behavior.
This abstract hook should be implemented by concrete commands to
perform the command's main work. Implementations should read
self.args and self.options which were set in
:meth:base_handle.
Source code in src/winidjango/core/commands/base/base.py
import_data ¶
Import command base class and utilities.
This module defines a reusable base command for importing tabular data
into Django models. Implementations should provide a concrete source
ingestion (for example, reading from CSV or an external API), a
cleaning/normalization step implemented by a CleaningDF subclass, and
mapping logic that groups cleaned data into model instances that can be
bulk-created.
The base command centralizes the typical flow:
1. Read raw data (handle_import)
2. Wrap and clean the data using a CleaningDF subclass
3. Convert the cleaned frame into per-model bulks
4. Persist bulks using the project's bulk create helpers
Using this base class ensures a consistent import lifecycle and reduces duplicated boilerplate across different import implementations.
ImportDataBaseCommand ¶
Bases: ABCBaseCommand
Abstract base for data-import Django management commands.
Subclasses must implement the ingestion, cleaning-class selection, and mapping of cleaned rows to Django model instances. The base implementation wires these pieces together and calls the project's bulk creation helper to persist the data.
Implementors typically only need to override the three abstract methods documented below.
get_bulks_by_model
abstractmethod
¶
Map the cleaned DataFrame to model-instance bulks.
The implementation should inspect the cleaned DataFrame and return a mapping where keys are Django model classes and values are iterables of unsaved model instances (or dataclass-like objects accepted by the project's bulk-creation utility).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
The cleaned and normalized DataFrame. |
required |
Returns:
| Type | Description |
|---|---|
dict[type[Model], Iterable[Model]]
|
dict[type[Model], Iterable[Model]]: Mapping from model classes to iterables of instances that should be created. |
Source code in src/winidjango/core/commands/import_data.py
get_cleaning_df_cls
abstractmethod
¶
get_cleaning_df_cls() -> type[CleaningDF]
Return the CleaningDF subclass used to normalize the data.
The returned class will be instantiated with the raw DataFrame
returned from :meth:handle_import and must provide the
transformations required to prepare data for mapping into model
instances.
Returns:
| Type | Description |
|---|---|
type[CleaningDF]
|
type[CleaningDF]: A subclass of |
Source code in src/winidjango/core/commands/import_data.py
handle_command ¶
Execute the full import lifecycle.
This template method reads raw data via :meth:handle_import,
wraps it with the cleaning class returned by
:meth:get_cleaning_df_cls and then persists the resulting
model bulks returned by :meth:get_bulks_by_model.
Source code in src/winidjango/core/commands/import_data.py
handle_import
abstractmethod
¶
Read raw data from the import source.
This method should read data from whatever source the concrete
command targets (files, remote APIs, etc.) and return it as a
polars.DataFrame. No cleaning should be performed here;
cleaning is handled by the cleaning CleaningDF returned from
get_cleaning_df_cls.
Returns:
| Type | Description |
|---|---|
DataFrame
|
pl.DataFrame: Raw (uncleaned) tabular data to be cleaned and mapped to model instances. |
Source code in src/winidjango/core/commands/import_data.py
import_to_db ¶
Persist prepared model bulks to the database.
Calls the project's bulk_create_bulks_in_steps helper with the
mapping returned from :meth:get_bulks_by_model.
Source code in src/winidjango/core/commands/import_data.py
db ¶
Database utilities and common model helpers used in the project.
This package contains helpers for working with Django models, such as
topological sorting of model classes according to foreign-key
dependencies, a lightweight model hashing helper, and a project-wide
BaseModel that adds common timestamp fields.
bulk ¶
Utilities for performing bulk operations on Django models.
This module centralizes helpers used by importers and maintenance commands to create, update and delete large collections of model instances efficiently. It provides batching, optional concurrent execution, dependency-aware ordering and simulation helpers for previewing cascade deletions.
bulk_create_bulks_in_steps ¶
bulk_create_bulks_in_steps(
bulk_by_class: dict[type[Model], Iterable[Model]],
step: int = STANDARD_BULK_SIZE,
) -> dict[type[Model], list[Model]]
Create multiple model-type bulks in dependency order.
The function topologically sorts the provided model classes so that
models referenced by foreign keys are created before models that
reference them. Each class' instances are created in batches using
:func:bulk_create_in_steps.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bulk_by_class
|
dict[type[Model], Iterable[Model]]
|
Mapping from model class to iterable of instances to create. |
required |
step
|
int
|
Chunk size for each model's batched creation. |
STANDARD_BULK_SIZE
|
Returns:
| Type | Description |
|---|---|
dict[type[Model], list[Model]]
|
Mapping from model class to list of created instances. |
Source code in src/winidjango/core/db/bulk.py
bulk_create_in_steps ¶
bulk_create_in_steps[TModel: Model](
model: type[TModel],
bulk: Iterable[TModel],
step: int = STANDARD_BULK_SIZE,
) -> list[TModel]
Create objects in batches and return created instances.
Breaks bulk into chunks of size step and calls the project's
batched bulk-create helper for each chunk. Execution is performed
using the concurrent utility where configured for throughput.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[TModel]
|
Django model class to create instances for. |
required |
bulk
|
Iterable[TModel]
|
Iterable of unsaved model instances. |
required |
step
|
int
|
Number of instances to create per chunk. |
STANDARD_BULK_SIZE
|
Returns:
| Type | Description |
|---|---|
list[TModel]
|
List of created model instances (flattened across chunks). |
Source code in src/winidjango/core/db/bulk.py
bulk_delete ¶
Delete the provided objects and return Django's delete summary.
Accepts either a QuerySet or an iterable of model instances. When an
iterable of instances is provided it is converted to a QuerySet by
filtering on primary keys before calling QuerySet.delete().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[Model]
|
Django model class. |
required |
objs
|
Iterable[Model]
|
Iterable of model instances or a QuerySet. |
required |
Returns:
| Type | Description |
|---|---|
tuple[int, dict[str, int]]
|
Tuple(total_deleted, per_model_counts) as returned by |
Source code in src/winidjango/core/db/bulk.py
bulk_delete_in_steps ¶
bulk_delete_in_steps[TModel: Model](
model: type[TModel],
bulk: Iterable[TModel],
step: int = STANDARD_BULK_SIZE,
) -> tuple[int, dict[str, int]]
Delete objects in batches and return deletion statistics.
Each chunk is deleted using Django's QuerySet delete which
returns a (count, per-model-counts) tuple. Results are aggregated
across chunks and returned as a consolidated tuple.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[TModel]
|
Django model class. |
required |
bulk
|
Iterable[TModel]
|
Iterable of model instances to delete. |
required |
step
|
int
|
Chunk size for deletions. |
STANDARD_BULK_SIZE
|
Returns:
| Type | Description |
|---|---|
int
|
A tuple containing the total number of deleted objects and a |
dict[str, int]
|
mapping from model label to deleted count (including cascaded |
tuple[int, dict[str, int]]
|
deletions). |
Source code in src/winidjango/core/db/bulk.py
bulk_method_in_steps ¶
bulk_method_in_steps[TModel: Model](
model: type[TModel],
bulk: Iterable[TModel],
step: int,
mode: Literal["create"],
**kwargs: Any,
) -> list[TModel]
bulk_method_in_steps[TModel: Model](
model: type[TModel],
bulk: Iterable[TModel],
step: int,
mode: MODE_TYPES,
**kwargs: Any,
) -> int | tuple[int, dict[str, int]] | list[TModel]
Run a batched bulk operation (create/update/delete) on bulk.
This wrapper warns if called from within an existing transaction and
delegates actual work to :func:bulk_method_in_steps_atomic which is
executed inside an atomic transaction. The return type depends on
mode (see :mod:winidjango.core.db.bulk constants).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[TModel]
|
Django model class to operate on. |
required |
bulk
|
Iterable[TModel]
|
Iterable of model instances. |
required |
step
|
int
|
Chunk size for processing. |
required |
mode
|
MODE_TYPES
|
One of |
required |
**kwargs
|
Any
|
Additional keyword arguments forwarded to the underlying
bulk methods (for example |
{}
|
Returns:
| Type | Description |
|---|---|
int | tuple[int, dict[str, int]] | list[TModel]
|
For |
int | tuple[int, dict[str, int]] | list[TModel]
|
For |
int | tuple[int, dict[str, int]] | list[TModel]
|
For |
Source code in src/winidjango/core/db/bulk.py
bulk_method_in_steps_atomic ¶
bulk_method_in_steps_atomic[TModel: Model](
model: type[TModel],
bulk: Iterable[TModel],
step: int,
mode: Literal["create"],
**kwargs: Any,
) -> list[TModel]
bulk_method_in_steps_atomic[TModel: Model](
model: type[TModel],
bulk: Iterable[TModel],
step: int,
mode: MODE_TYPES,
**kwargs: Any,
) -> int | tuple[int, dict[str, int]] | list[TModel]
Atomic implementation of the batched bulk operation.
This function is decorated with transaction.atomic so each call
to this function runs in a database transaction. Note that nesting
additional, outer atomic blocks that also include dependent bulk
operations can cause integrity issues for operations that depend on
each other's side-effects; callers should be careful about atomic
decorator placement in higher-level code.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[TModel]
|
Django model class. |
required |
bulk
|
Iterable[TModel]
|
Iterable of model instances. |
required |
step
|
int
|
Chunk size for processing. |
required |
mode
|
MODE_TYPES
|
One of |
required |
**kwargs
|
Any
|
Forwarded to the underlying bulk method. |
{}
|
Returns:
| Name | Type | Description |
|---|---|---|
See |
int | tuple[int, dict[str, int]] | list[TModel]
|
func: |
Source code in src/winidjango/core/db/bulk.py
bulk_update_in_steps ¶
bulk_update_in_steps[TModel: Model](
model: type[TModel],
bulk: Iterable[TModel],
update_fields: list[str],
step: int = STANDARD_BULK_SIZE,
) -> int
Update objects in batches and return total updated count.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[TModel]
|
Django model class. |
required |
bulk
|
Iterable[TModel]
|
Iterable of model instances to update (must have PKs set). |
required |
update_fields
|
list[str]
|
Fields to update on each instance when calling
|
required |
step
|
int
|
Chunk size for batched updates. |
STANDARD_BULK_SIZE
|
Returns:
| Type | Description |
|---|---|
int
|
Total number of rows updated across all chunks. |
Source code in src/winidjango/core/db/bulk.py
flatten_bulk_in_steps_result ¶
flatten_bulk_in_steps_result[TModel: Model](
result: list[int]
| list[tuple[int, dict[str, int]]]
| list[list[TModel]],
mode: str,
) -> int | tuple[int, dict[str, int]] | list[TModel]
Aggregate per-chunk results returned by concurrent bulk execution.
Depending on mode the function reduces a list of per-chunk
results into a single consolidated return value:
create: flattens a list of lists into a single list of objectsupdate: sums integer counts returned per chunkdelete: aggregates (count, per-model-dict) tuples into a single total and combined per-model counts
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
list[int] | list[tuple[int, dict[str, int]]] | list[list[TModel]]
|
List of per-chunk results returned by the chunk function. |
required |
mode
|
str
|
One of the supported modes. |
required |
Returns:
| Type | Description |
|---|---|
int | tuple[int, dict[str, int]] | list[TModel]
|
Aggregated result corresponding to |
Source code in src/winidjango/core/db/bulk.py
get_bulk_method ¶
get_bulk_method(
model: type[Model],
mode: Literal["create"],
**kwargs: Any,
) -> Callable[[list[Model]], list[Model]]
get_bulk_method(
model: type[Model], mode: MODE_TYPES, **kwargs: Any
) -> Callable[
[list[Model]],
list[Model] | int | tuple[int, dict[str, int]],
]
Return a callable that performs the requested bulk operation on a chunk.
The returned function accepts a single argument (a list of model instances) and returns the per-chunk result for the chosen mode.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[Model]
|
Django model class. |
required |
mode
|
MODE_TYPES
|
One of |
required |
**kwargs
|
Any
|
Forwarded to the underlying ORM bulk methods. |
{}
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Returns:
| Type | Description |
|---|---|
Callable[[list[Model]], list[Model] | int | tuple[int, dict[str, int]]]
|
Callable that accepts a list of model instances and returns the |
Callable[[list[Model]], list[Model] | int | tuple[int, dict[str, int]]]
|
result for that chunk. |
Source code in src/winidjango/core/db/bulk.py
get_differences_between_bulks ¶
get_differences_between_bulks[
TModel1: Model,
TModel2: Model,
](
bulk1: list[TModel1],
bulk2: list[TModel2],
fields: list[
Field[Any, Any]
| ForeignObjectRel
| GenericForeignKey
],
) -> tuple[
list[TModel1],
list[TModel2],
list[TModel1],
list[TModel2],
]
Return differences and intersections between two bulks of the same model.
Instances are compared using :func:hash_model_instance over the
provided fields. The function maintains the original ordering
for returned lists so that callers can preserve deterministic
ordering when applying diffs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bulk1
|
list[TModel1]
|
First list of model instances. |
required |
bulk2
|
list[TModel2]
|
Second list of model instances. |
required |
fields
|
list[Field[Any, Any] | ForeignObjectRel | GenericForeignKey]
|
Fields to include when hashing instances. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If bulks are empty or contain different model types. |
Returns:
| Type | Description |
|---|---|
tuple[list[TModel1], list[TModel2], list[TModel1], list[TModel2]]
|
Four lists in the order: (in_1_not_2, in_2_not_1, in_both_from_1, in_both_from_2). |
Source code in src/winidjango/core/db/bulk.py
get_step_chunks ¶
Yield consecutive chunks of at most step items from bulk.
The function yields a single-tuple containing the chunk (a list of model instances) because the concurrent execution helper expects a tuple of positional arguments for the target function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bulk
|
Iterable[Model]
|
Iterable of model instances. |
required |
step
|
int
|
Maximum number of instances per yielded chunk. |
required |
Yields:
| Type | Description |
|---|---|
tuple[list[Model]]
|
Tuples where the first element is a list of model instances. |
Source code in src/winidjango/core/db/bulk.py
multi_simulate_bulk_deletion ¶
multi_simulate_bulk_deletion(
entries: dict[type[Model], list[Model]],
) -> dict[type[Model], set[Model]]
Simulate deletions for multiple model classes and merge results.
Runs :func:simulate_bulk_deletion for each provided model and
returns a unified mapping of all models that would be deleted.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
entries
|
dict[type[Model], list[Model]]
|
Mapping from model class to list of instances to simulate. |
required |
Returns:
| Type | Description |
|---|---|
dict[type[Model], set[Model]]
|
Mapping from model class to set of instances that would be deleted. |
Source code in src/winidjango/core/db/bulk.py
simulate_bulk_deletion ¶
simulate_bulk_deletion[TModel: Model](
model_class: type[TModel], entries: list[TModel]
) -> dict[type[Model], set[Model]]
Simulate Django's delete cascade and return affected objects.
Uses :class:django.db.models.deletion.Collector to determine which
objects (including cascaded related objects) would be removed if the
provided entries were deleted. No database writes are performed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_class
|
type[TModel]
|
Model class of the provided entries. |
required |
entries
|
list[TModel]
|
Instances to simulate deletion for. |
required |
Returns:
| Type | Description |
|---|---|
dict[type[Model], set[Model]]
|
Mapping from model class to set of instances that would be deleted. |
Source code in src/winidjango/core/db/bulk.py
fields ¶
Utilities for inspecting Django model fields.
This module provides small helpers that make it easier to introspect Django model fields and metadata in a type-friendly way. The helpers are used across the project's database utilities to implement operations like topological sorting and deterministic hashing of model instances.
get_field_names ¶
get_field_names(
fields: list[
Field[Any, Any]
| ForeignObjectRel
| GenericForeignKey
],
) -> list[str]
Return the name attribute for a list of Django field objects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fields
|
list[Field | ForeignObjectRel | GenericForeignKey]
|
Field objects obtained from a model's |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
list[str]: List of field names in the same order as |
Source code in src/winidjango/core/db/fields.py
get_fields ¶
get_fields[TModel: Model](
model: type[TModel],
) -> list[
Field[Any, Any] | ForeignObjectRel | GenericForeignKey
]
Return all field objects for a Django model.
This wraps model._meta.get_fields() and is typed to include
relationship fields so callers can handle both regular and related
fields uniformly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[Model]
|
Django model class. |
required |
Returns:
| Type | Description |
|---|---|
list[Field[Any, Any] | ForeignObjectRel | GenericForeignKey]
|
list[Field | ForeignObjectRel | GenericForeignKey]: All field objects associated with the model. |
Source code in src/winidjango/core/db/fields.py
get_model_meta ¶
get_model_meta(model: type[Model]) -> Options[Model]
Return a model class' _meta options object.
This small wrapper exists to make typing clearer at call sites where the code needs the model Options object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[Model]
|
Django model class. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Options |
Options[Model]
|
The model's |
Source code in src/winidjango/core/db/fields.py
models ¶
Database utilities and lightweight model helpers.
This module provides helpers used across the project when manipulating
Django models: ordering models by foreign-key dependencies, creating a
deterministic hash for unsaved instances, and a project-wide
BaseModel that exposes common timestamp fields.
BaseModel ¶
Bases: Model
Abstract base model containing common fields and helpers.
Concrete models can inherit from this class to get consistent
created_at and updated_at timestamp fields and convenient
string representations.
meta
property
¶
meta: Options[Self]
Return the model's _meta options object.
This property is a small convenience wrapper used to make access sites slightly more explicit in code and improve typing in callers.
Meta ¶
Mark the model as abstract.
__str__ ¶
__str__() -> str
Return a concise human-readable representation.
The default shows the model class name and primary key which is useful for logging and interactive debugging.
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
Short representation, e.g. |
Source code in src/winidjango/core/db/models.py
hash_model_instance ¶
hash_model_instance(
instance: Model,
fields: list[
Field[Any, Any]
| ForeignObjectRel
| GenericForeignKey
],
) -> int
Compute a deterministic hash for a model instance.
The function returns a hash suitable for comparing unsaved model
instances by their field values. If the instance has a primary key
(instance.pk) that key is hashed and returned immediately; this
keeps comparisons cheap for persisted objects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
instance
|
Model
|
The Django model instance to hash. |
required |
fields
|
list[Field | ForeignObjectRel | GenericForeignKey]
|
Field objects that should be included when computing the hash. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
Deterministic integer hash of the instance. For persisted
instances this is |
Notes
- The returned hash is intended for heuristic comparisons (e.g. deduplication in import pipelines) and is not cryptographically secure. Use with care when relying on absolute uniqueness.
Source code in src/winidjango/core/db/models.py
topological_sort_models ¶
Sort Django models in dependency order using topological sorting.
Analyzes foreign key relationships between Django models and returns them in an order where dependencies come before dependents. This ensures that when performing operations like bulk creation or deletion, models are processed in the correct order to avoid foreign key constraint violations.
The function uses Python's graphlib.TopologicalSorter to perform the sorting based on ForeignKey relationships between the provided models. Only relationships between models in the input list are considered.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
models
|
list[type[Model]]
|
A list of Django model classes to sort based on their foreign key dependencies. |
required |
Returns:
| Type | Description |
|---|---|
list[type[Model]]
|
list[type[Model]]: The input models sorted in dependency order, where models that are referenced by foreign keys appear before models that reference them. Self-referential relationships are ignored. |
Raises:
| Type | Description |
|---|---|
CycleError
|
If there are circular dependencies between models that cannot be resolved. |
Example
Assuming Author model has no dependencies¶
and Book model has ForeignKey to Author¶
models = [Book, Author] sorted_models = topological_sort_models(models) sorted_models [
, ]
Note
- Only considers ForeignKey relationships, not other field types
- Self-referential foreign keys are ignored to avoid self-loops
- Only relationships between models in the input list are considered
Source code in src/winidjango/core/db/models.py
setup ¶
Concurrency-safe Django database setup helpers.
Django's migration recorder checks whether its bookkeeping table exists
and, if not, creates it. That check-then-create is not atomic, so when
multiple processes call migrate against the same database at once
(for example, several concurrent invocations of the same pre-commit
hook), more than one process can decide the table is missing and race
to create it, crashing with "table already exists". migrate_safely
serializes migration across processes with a file lock so only one
process ever runs it at a time; the rest simply find the schema already
up to date once they acquire the lock.
migrate_safely ¶
migrate_safely(db_path: Path) -> None
Apply Django migrations without racing other concurrent processes.
Must be called after django.setup(). Serializes access with a
file lock next to db_path so concurrent processes targeting the
same database apply migrations one at a time instead of racing to
create the migration bookkeeping table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
db_path
|
Path
|
Path to the database file the migrations apply to. |
required |
Source code in src/winidjango/core/db/setup.py
sql ¶
Low-level helper to execute raw SQL against Django's database.
This module exposes :func:execute_sql which runs a parameterized SQL
query using Django's database connection and returns column names and
rows. It is intended for one-off queries where ORM abstractions are
insufficient or when reading complex reports from the database.
The helper uses Django's connection cursor context manager to ensure resources are cleaned up correctly. Results are fetched into memory so avoid using it for very large result sets.
execute_sql ¶
execute_sql(
sql: str, params: dict[str, Any] | None = None
) -> tuple[list[str], list[tuple[Any, ...]]]
Execute a SQL statement and return column names and rows.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sql
|
str
|
SQL statement possibly containing named placeholders
( |
required |
params
|
dict[str, Any] | None
|
Optional mapping of parameters to bind to the query. |
None
|
Returns:
| Type | Description |
|---|---|
list[str]
|
Tuple[List[str], List[Tuple[Any, ...]]]: A tuple where the first |
list[tuple[Any, ...]]
|
element is the list of column names (empty list if the statement |
tuple[list[str], list[tuple[Any, ...]]]
|
returned no rows) and the second element is a list of row tuples. |
Raises:
| Type | Description |
|---|---|
Error
|
Propagates underlying database errors raised by Django's database backend. |
Source code in src/winidjango/core/db/sql.py
rig ¶
init module.
configs ¶
Package initialization.
pyproject ¶
Manage pyproject.toml (PEP 518, 621, 660).
Handles metadata, dependencies, build config (uv), tool configs (ruff, ty, pytest, bandit, rumdl). Enforces opinionated defaults: all ruff rules (except D203, D213, COM812, ANN401), Google docstrings, strict ty, bandit security, coverage threshold. Validation uses subset checking (users can add extra configs). Priority 20 (created early for other configs to read).
Utility methods: project info, dependencies, Python versions, license detection, classifiers.
DjangoPyprojectConfigFile ¶
Bases: PyprojectConfigFile, PyprojectConfigFile
Overrides base to resolve conflicts.
Only needed when developing Winidjango itself.
PyprojectConfigFile ¶
Bases: PyprojectConfigFile
You can override methods from the base class to customize behavior.
resources ¶
init module.
tools ¶
Tool wrappers for CLI tools used in development workflows.
Tools are subclasses of Tool providing methods that return Args objects
for type-safe command construction and execution.
winidjango ¶
init module.
core ¶
src package.
This package exposes the project's internal modules used by the
command-line utilities and database helpers. It exists primarily so
that code under winidjango/src can be imported using the
winidjango.core package path in other modules and tests.
The package itself contains the following subpackages:
- commands - management command helpers and base classes
- db - database utilities and model helpers
Consumers should import the specific submodules they need rather than relying on side effects from this package's import-time execution.
commands ¶
Utilities and base classes for management commands.
The commands package contains base command classes and helpers used to
implement Django management commands in the project. Subpackages and
modules under commands provide reusable patterns for argument handling,
logging and common command behaviors so that individual commands can focus
on their business logic.
base ¶
Base helpers and abstractions for management commands.
This package provides a common abstract base class used by project management commands. The base class centralizes argument handling, standard options (dry-run, batching, timeouts, etc.) and integrates logging behavior used throughout the commands package.
base ¶
Utilities and an abstract base class for Django management commands.
This module defines :class:ABCBaseCommand, a reusable abstract base
that combines Django's BaseCommand with the project's logging
mixins and standard argument handling. The base class implements a
template method pattern so concrete commands only need to implement the
abstract extension points for providing command-specific arguments and
business logic.
Bases: ABCLoggingMixin, BaseCommand
Abstract base class for management commands with logging and standard options.
The class wires common behavior such as base arguments (dry-run,
batching, timeouts) and provides extension points that concrete
commands must implement: :meth:add_command_arguments and
:meth:handle_command.
Notes
- Inheritance order matters: the logging mixin must precede
BaseCommandso mixin initialization occurs as expected. - The base class follows the template method pattern; concrete
commands should not override :meth:
add_argumentsor :meth:handlebut implement the abstract hooks instead.
Just a container class for hard coding the option keys.
add_arguments(parser: ArgumentParser) -> None
Configure command-line arguments for the command.
Adds common base arguments (dry-run, force, delete, timeout,
batching and concurrency options) and then delegates to
:meth:add_command_arguments for command-specific options.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parser
|
ArgumentParser
|
The argument parser passed by Django. |
required |
Source code in src/winidjango/core/commands/base/base.py
abstractmethod
¶add_command_arguments(parser: ArgumentParser) -> None
Define command-specific arguments.
Implement this hook to add options and positional arguments that are specific to the concrete management command.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parser
|
ArgumentParser
|
The argument parser passed by Django. |
required |
Source code in src/winidjango/core/commands/base/base.py
base_add_arguments(parser: ArgumentParser) -> None
Add the project's standard command-line arguments to parser.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parser
|
ArgumentParser
|
The argument parser passed by Django. |
required |
Source code in src/winidjango/core/commands/base/base.py
Perform common pre-processing for commands.
Stores the incoming arguments and options on the instance for use
by :meth:handle_command and subclasses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*args
|
Any
|
Positional arguments forwarded from Django. |
()
|
**options
|
Any
|
Parsed command-line options. |
{}
|
Source code in src/winidjango/core/commands/base/base.py
Retrieve a parsed command option by key.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
option
|
str
|
The option key to retrieve from |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Any |
Any
|
The value for the requested option. If the option is not
present a |
Source code in src/winidjango/core/commands/base/base.py
Orchestrate command execution.
Performs shared pre-processing by calling :meth:base_handle and
then delegates to :meth:handle_command which must be implemented
by subclasses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*args
|
Any
|
Positional arguments forwarded from Django. |
()
|
**options
|
Any
|
Parsed command-line options. |
{}
|
Source code in src/winidjango/core/commands/base/base.py
abstractmethod
¶Run the command-specific behavior.
This abstract hook should be implemented by concrete commands to
perform the command's main work. Implementations should read
self.args and self.options which were set in
:meth:base_handle.
Source code in src/winidjango/core/commands/base/base.py
import_data ¶
Import command base class and utilities.
This module defines a reusable base command for importing tabular data
into Django models. Implementations should provide a concrete source
ingestion (for example, reading from CSV or an external API), a
cleaning/normalization step implemented by a CleaningDF subclass, and
mapping logic that groups cleaned data into model instances that can be
bulk-created.
The base command centralizes the typical flow:
1. Read raw data (handle_import)
2. Wrap and clean the data using a CleaningDF subclass
3. Convert the cleaned frame into per-model bulks
4. Persist bulks using the project's bulk create helpers
Using this base class ensures a consistent import lifecycle and reduces duplicated boilerplate across different import implementations.
ImportDataBaseCommand ¶
Bases: ABCBaseCommand
Abstract base for data-import Django management commands.
Subclasses must implement the ingestion, cleaning-class selection, and mapping of cleaned rows to Django model instances. The base implementation wires these pieces together and calls the project's bulk creation helper to persist the data.
Implementors typically only need to override the three abstract methods documented below.
abstractmethod
¶Map the cleaned DataFrame to model-instance bulks.
The implementation should inspect the cleaned DataFrame and return a mapping where keys are Django model classes and values are iterables of unsaved model instances (or dataclass-like objects accepted by the project's bulk-creation utility).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
The cleaned and normalized DataFrame. |
required |
Returns:
| Type | Description |
|---|---|
dict[type[Model], Iterable[Model]]
|
dict[type[Model], Iterable[Model]]: Mapping from model classes to iterables of instances that should be created. |
Source code in src/winidjango/core/commands/import_data.py
abstractmethod
¶get_cleaning_df_cls() -> type[CleaningDF]
Return the CleaningDF subclass used to normalize the data.
The returned class will be instantiated with the raw DataFrame
returned from :meth:handle_import and must provide the
transformations required to prepare data for mapping into model
instances.
Returns:
| Type | Description |
|---|---|
type[CleaningDF]
|
type[CleaningDF]: A subclass of |
Source code in src/winidjango/core/commands/import_data.py
Execute the full import lifecycle.
This template method reads raw data via :meth:handle_import,
wraps it with the cleaning class returned by
:meth:get_cleaning_df_cls and then persists the resulting
model bulks returned by :meth:get_bulks_by_model.
Source code in src/winidjango/core/commands/import_data.py
abstractmethod
¶Read raw data from the import source.
This method should read data from whatever source the concrete
command targets (files, remote APIs, etc.) and return it as a
polars.DataFrame. No cleaning should be performed here;
cleaning is handled by the cleaning CleaningDF returned from
get_cleaning_df_cls.
Returns:
| Type | Description |
|---|---|
DataFrame
|
pl.DataFrame: Raw (uncleaned) tabular data to be cleaned and mapped to model instances. |
Source code in src/winidjango/core/commands/import_data.py
Persist prepared model bulks to the database.
Calls the project's bulk_create_bulks_in_steps helper with the
mapping returned from :meth:get_bulks_by_model.
Source code in src/winidjango/core/commands/import_data.py
db ¶
Database utilities and common model helpers used in the project.
This package contains helpers for working with Django models, such as
topological sorting of model classes according to foreign-key
dependencies, a lightweight model hashing helper, and a project-wide
BaseModel that adds common timestamp fields.
bulk ¶
Utilities for performing bulk operations on Django models.
This module centralizes helpers used by importers and maintenance commands to create, update and delete large collections of model instances efficiently. It provides batching, optional concurrent execution, dependency-aware ordering and simulation helpers for previewing cascade deletions.
bulk_create_bulks_in_steps ¶
bulk_create_bulks_in_steps(
bulk_by_class: dict[type[Model], Iterable[Model]],
step: int = STANDARD_BULK_SIZE,
) -> dict[type[Model], list[Model]]
Create multiple model-type bulks in dependency order.
The function topologically sorts the provided model classes so that
models referenced by foreign keys are created before models that
reference them. Each class' instances are created in batches using
:func:bulk_create_in_steps.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bulk_by_class
|
dict[type[Model], Iterable[Model]]
|
Mapping from model class to iterable of instances to create. |
required |
step
|
int
|
Chunk size for each model's batched creation. |
STANDARD_BULK_SIZE
|
Returns:
| Type | Description |
|---|---|
dict[type[Model], list[Model]]
|
Mapping from model class to list of created instances. |
Source code in src/winidjango/core/db/bulk.py
bulk_create_in_steps ¶
bulk_create_in_steps[TModel: Model](
model: type[TModel],
bulk: Iterable[TModel],
step: int = STANDARD_BULK_SIZE,
) -> list[TModel]
Create objects in batches and return created instances.
Breaks bulk into chunks of size step and calls the project's
batched bulk-create helper for each chunk. Execution is performed
using the concurrent utility where configured for throughput.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[TModel]
|
Django model class to create instances for. |
required |
bulk
|
Iterable[TModel]
|
Iterable of unsaved model instances. |
required |
step
|
int
|
Number of instances to create per chunk. |
STANDARD_BULK_SIZE
|
Returns:
| Type | Description |
|---|---|
list[TModel]
|
List of created model instances (flattened across chunks). |
Source code in src/winidjango/core/db/bulk.py
bulk_delete ¶
Delete the provided objects and return Django's delete summary.
Accepts either a QuerySet or an iterable of model instances. When an
iterable of instances is provided it is converted to a QuerySet by
filtering on primary keys before calling QuerySet.delete().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[Model]
|
Django model class. |
required |
objs
|
Iterable[Model]
|
Iterable of model instances or a QuerySet. |
required |
Returns:
| Type | Description |
|---|---|
tuple[int, dict[str, int]]
|
Tuple(total_deleted, per_model_counts) as returned by |
Source code in src/winidjango/core/db/bulk.py
bulk_delete_in_steps ¶
bulk_delete_in_steps[TModel: Model](
model: type[TModel],
bulk: Iterable[TModel],
step: int = STANDARD_BULK_SIZE,
) -> tuple[int, dict[str, int]]
Delete objects in batches and return deletion statistics.
Each chunk is deleted using Django's QuerySet delete which
returns a (count, per-model-counts) tuple. Results are aggregated
across chunks and returned as a consolidated tuple.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[TModel]
|
Django model class. |
required |
bulk
|
Iterable[TModel]
|
Iterable of model instances to delete. |
required |
step
|
int
|
Chunk size for deletions. |
STANDARD_BULK_SIZE
|
Returns:
| Type | Description |
|---|---|
int
|
A tuple containing the total number of deleted objects and a |
dict[str, int]
|
mapping from model label to deleted count (including cascaded |
tuple[int, dict[str, int]]
|
deletions). |
Source code in src/winidjango/core/db/bulk.py
bulk_method_in_steps ¶
bulk_method_in_steps[TModel: Model](
model: type[TModel],
bulk: Iterable[TModel],
step: int,
mode: Literal["create"],
**kwargs: Any,
) -> list[TModel]
bulk_method_in_steps[TModel: Model](
model: type[TModel],
bulk: Iterable[TModel],
step: int,
mode: MODE_TYPES,
**kwargs: Any,
) -> int | tuple[int, dict[str, int]] | list[TModel]
Run a batched bulk operation (create/update/delete) on bulk.
This wrapper warns if called from within an existing transaction and
delegates actual work to :func:bulk_method_in_steps_atomic which is
executed inside an atomic transaction. The return type depends on
mode (see :mod:winidjango.core.db.bulk constants).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[TModel]
|
Django model class to operate on. |
required |
bulk
|
Iterable[TModel]
|
Iterable of model instances. |
required |
step
|
int
|
Chunk size for processing. |
required |
mode
|
MODE_TYPES
|
One of |
required |
**kwargs
|
Any
|
Additional keyword arguments forwarded to the underlying
bulk methods (for example |
{}
|
Returns:
| Type | Description |
|---|---|
int | tuple[int, dict[str, int]] | list[TModel]
|
For |
int | tuple[int, dict[str, int]] | list[TModel]
|
For |
int | tuple[int, dict[str, int]] | list[TModel]
|
For |
Source code in src/winidjango/core/db/bulk.py
bulk_method_in_steps_atomic ¶
bulk_method_in_steps_atomic[TModel: Model](
model: type[TModel],
bulk: Iterable[TModel],
step: int,
mode: Literal["create"],
**kwargs: Any,
) -> list[TModel]
bulk_method_in_steps_atomic[TModel: Model](
model: type[TModel],
bulk: Iterable[TModel],
step: int,
mode: MODE_TYPES,
**kwargs: Any,
) -> int | tuple[int, dict[str, int]] | list[TModel]
Atomic implementation of the batched bulk operation.
This function is decorated with transaction.atomic so each call
to this function runs in a database transaction. Note that nesting
additional, outer atomic blocks that also include dependent bulk
operations can cause integrity issues for operations that depend on
each other's side-effects; callers should be careful about atomic
decorator placement in higher-level code.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[TModel]
|
Django model class. |
required |
bulk
|
Iterable[TModel]
|
Iterable of model instances. |
required |
step
|
int
|
Chunk size for processing. |
required |
mode
|
MODE_TYPES
|
One of |
required |
**kwargs
|
Any
|
Forwarded to the underlying bulk method. |
{}
|
Returns:
| Name | Type | Description |
|---|---|---|
See |
int | tuple[int, dict[str, int]] | list[TModel]
|
func: |
Source code in src/winidjango/core/db/bulk.py
bulk_update_in_steps ¶
bulk_update_in_steps[TModel: Model](
model: type[TModel],
bulk: Iterable[TModel],
update_fields: list[str],
step: int = STANDARD_BULK_SIZE,
) -> int
Update objects in batches and return total updated count.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[TModel]
|
Django model class. |
required |
bulk
|
Iterable[TModel]
|
Iterable of model instances to update (must have PKs set). |
required |
update_fields
|
list[str]
|
Fields to update on each instance when calling
|
required |
step
|
int
|
Chunk size for batched updates. |
STANDARD_BULK_SIZE
|
Returns:
| Type | Description |
|---|---|
int
|
Total number of rows updated across all chunks. |
Source code in src/winidjango/core/db/bulk.py
flatten_bulk_in_steps_result ¶
flatten_bulk_in_steps_result[TModel: Model](
result: list[int]
| list[tuple[int, dict[str, int]]]
| list[list[TModel]],
mode: str,
) -> int | tuple[int, dict[str, int]] | list[TModel]
Aggregate per-chunk results returned by concurrent bulk execution.
Depending on mode the function reduces a list of per-chunk
results into a single consolidated return value:
create: flattens a list of lists into a single list of objectsupdate: sums integer counts returned per chunkdelete: aggregates (count, per-model-dict) tuples into a single total and combined per-model counts
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
list[int] | list[tuple[int, dict[str, int]]] | list[list[TModel]]
|
List of per-chunk results returned by the chunk function. |
required |
mode
|
str
|
One of the supported modes. |
required |
Returns:
| Type | Description |
|---|---|
int | tuple[int, dict[str, int]] | list[TModel]
|
Aggregated result corresponding to |
Source code in src/winidjango/core/db/bulk.py
get_bulk_method ¶
get_bulk_method(
model: type[Model],
mode: Literal["create"],
**kwargs: Any,
) -> Callable[[list[Model]], list[Model]]
get_bulk_method(
model: type[Model], mode: MODE_TYPES, **kwargs: Any
) -> Callable[
[list[Model]],
list[Model] | int | tuple[int, dict[str, int]],
]
Return a callable that performs the requested bulk operation on a chunk.
The returned function accepts a single argument (a list of model instances) and returns the per-chunk result for the chosen mode.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[Model]
|
Django model class. |
required |
mode
|
MODE_TYPES
|
One of |
required |
**kwargs
|
Any
|
Forwarded to the underlying ORM bulk methods. |
{}
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Returns:
| Type | Description |
|---|---|
Callable[[list[Model]], list[Model] | int | tuple[int, dict[str, int]]]
|
Callable that accepts a list of model instances and returns the |
Callable[[list[Model]], list[Model] | int | tuple[int, dict[str, int]]]
|
result for that chunk. |
Source code in src/winidjango/core/db/bulk.py
get_differences_between_bulks ¶
get_differences_between_bulks[
TModel1: Model,
TModel2: Model,
](
bulk1: list[TModel1],
bulk2: list[TModel2],
fields: list[
Field[Any, Any]
| ForeignObjectRel
| GenericForeignKey
],
) -> tuple[
list[TModel1],
list[TModel2],
list[TModel1],
list[TModel2],
]
Return differences and intersections between two bulks of the same model.
Instances are compared using :func:hash_model_instance over the
provided fields. The function maintains the original ordering
for returned lists so that callers can preserve deterministic
ordering when applying diffs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bulk1
|
list[TModel1]
|
First list of model instances. |
required |
bulk2
|
list[TModel2]
|
Second list of model instances. |
required |
fields
|
list[Field[Any, Any] | ForeignObjectRel | GenericForeignKey]
|
Fields to include when hashing instances. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If bulks are empty or contain different model types. |
Returns:
| Type | Description |
|---|---|
tuple[list[TModel1], list[TModel2], list[TModel1], list[TModel2]]
|
Four lists in the order: (in_1_not_2, in_2_not_1, in_both_from_1, in_both_from_2). |
Source code in src/winidjango/core/db/bulk.py
get_step_chunks ¶
Yield consecutive chunks of at most step items from bulk.
The function yields a single-tuple containing the chunk (a list of model instances) because the concurrent execution helper expects a tuple of positional arguments for the target function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bulk
|
Iterable[Model]
|
Iterable of model instances. |
required |
step
|
int
|
Maximum number of instances per yielded chunk. |
required |
Yields:
| Type | Description |
|---|---|
tuple[list[Model]]
|
Tuples where the first element is a list of model instances. |
Source code in src/winidjango/core/db/bulk.py
multi_simulate_bulk_deletion ¶
multi_simulate_bulk_deletion(
entries: dict[type[Model], list[Model]],
) -> dict[type[Model], set[Model]]
Simulate deletions for multiple model classes and merge results.
Runs :func:simulate_bulk_deletion for each provided model and
returns a unified mapping of all models that would be deleted.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
entries
|
dict[type[Model], list[Model]]
|
Mapping from model class to list of instances to simulate. |
required |
Returns:
| Type | Description |
|---|---|
dict[type[Model], set[Model]]
|
Mapping from model class to set of instances that would be deleted. |
Source code in src/winidjango/core/db/bulk.py
simulate_bulk_deletion ¶
simulate_bulk_deletion[TModel: Model](
model_class: type[TModel], entries: list[TModel]
) -> dict[type[Model], set[Model]]
Simulate Django's delete cascade and return affected objects.
Uses :class:django.db.models.deletion.Collector to determine which
objects (including cascaded related objects) would be removed if the
provided entries were deleted. No database writes are performed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_class
|
type[TModel]
|
Model class of the provided entries. |
required |
entries
|
list[TModel]
|
Instances to simulate deletion for. |
required |
Returns:
| Type | Description |
|---|---|
dict[type[Model], set[Model]]
|
Mapping from model class to set of instances that would be deleted. |
Source code in src/winidjango/core/db/bulk.py
fields ¶
Utilities for inspecting Django model fields.
This module provides small helpers that make it easier to introspect Django model fields and metadata in a type-friendly way. The helpers are used across the project's database utilities to implement operations like topological sorting and deterministic hashing of model instances.
get_field_names ¶
get_field_names(
fields: list[
Field[Any, Any]
| ForeignObjectRel
| GenericForeignKey
],
) -> list[str]
Return the name attribute for a list of Django field objects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fields
|
list[Field | ForeignObjectRel | GenericForeignKey]
|
Field objects obtained from a model's |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
list[str]: List of field names in the same order as |
Source code in src/winidjango/core/db/fields.py
get_fields ¶
get_fields[TModel: Model](
model: type[TModel],
) -> list[
Field[Any, Any] | ForeignObjectRel | GenericForeignKey
]
Return all field objects for a Django model.
This wraps model._meta.get_fields() and is typed to include
relationship fields so callers can handle both regular and related
fields uniformly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[Model]
|
Django model class. |
required |
Returns:
| Type | Description |
|---|---|
list[Field[Any, Any] | ForeignObjectRel | GenericForeignKey]
|
list[Field | ForeignObjectRel | GenericForeignKey]: All field objects associated with the model. |
Source code in src/winidjango/core/db/fields.py
get_model_meta ¶
get_model_meta(model: type[Model]) -> Options[Model]
Return a model class' _meta options object.
This small wrapper exists to make typing clearer at call sites where the code needs the model Options object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[Model]
|
Django model class. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Options |
Options[Model]
|
The model's |
Source code in src/winidjango/core/db/fields.py
models ¶
Database utilities and lightweight model helpers.
This module provides helpers used across the project when manipulating
Django models: ordering models by foreign-key dependencies, creating a
deterministic hash for unsaved instances, and a project-wide
BaseModel that exposes common timestamp fields.
BaseModel ¶
Bases: Model
Abstract base model containing common fields and helpers.
Concrete models can inherit from this class to get consistent
created_at and updated_at timestamp fields and convenient
string representations.
property
¶meta: Options[Self]
Return the model's _meta options object.
This property is a small convenience wrapper used to make access sites slightly more explicit in code and improve typing in callers.
Mark the model as abstract.
__str__() -> str
Return a concise human-readable representation.
The default shows the model class name and primary key which is useful for logging and interactive debugging.
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
Short representation, e.g. |
Source code in src/winidjango/core/db/models.py
hash_model_instance ¶
hash_model_instance(
instance: Model,
fields: list[
Field[Any, Any]
| ForeignObjectRel
| GenericForeignKey
],
) -> int
Compute a deterministic hash for a model instance.
The function returns a hash suitable for comparing unsaved model
instances by their field values. If the instance has a primary key
(instance.pk) that key is hashed and returned immediately; this
keeps comparisons cheap for persisted objects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
instance
|
Model
|
The Django model instance to hash. |
required |
fields
|
list[Field | ForeignObjectRel | GenericForeignKey]
|
Field objects that should be included when computing the hash. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
Deterministic integer hash of the instance. For persisted
instances this is |
Notes
- The returned hash is intended for heuristic comparisons (e.g. deduplication in import pipelines) and is not cryptographically secure. Use with care when relying on absolute uniqueness.
Source code in src/winidjango/core/db/models.py
topological_sort_models ¶
Sort Django models in dependency order using topological sorting.
Analyzes foreign key relationships between Django models and returns them in an order where dependencies come before dependents. This ensures that when performing operations like bulk creation or deletion, models are processed in the correct order to avoid foreign key constraint violations.
The function uses Python's graphlib.TopologicalSorter to perform the sorting based on ForeignKey relationships between the provided models. Only relationships between models in the input list are considered.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
models
|
list[type[Model]]
|
A list of Django model classes to sort based on their foreign key dependencies. |
required |
Returns:
| Type | Description |
|---|---|
list[type[Model]]
|
list[type[Model]]: The input models sorted in dependency order, where models that are referenced by foreign keys appear before models that reference them. Self-referential relationships are ignored. |
Raises:
| Type | Description |
|---|---|
CycleError
|
If there are circular dependencies between models that cannot be resolved. |
Example
Assuming Author model has no dependencies¶
and Book model has ForeignKey to Author¶
models = [Book, Author] sorted_models = topological_sort_models(models) sorted_models [
, ]
Note
- Only considers ForeignKey relationships, not other field types
- Self-referential foreign keys are ignored to avoid self-loops
- Only relationships between models in the input list are considered
Source code in src/winidjango/core/db/models.py
setup ¶
Concurrency-safe Django database setup helpers.
Django's migration recorder checks whether its bookkeeping table exists
and, if not, creates it. That check-then-create is not atomic, so when
multiple processes call migrate against the same database at once
(for example, several concurrent invocations of the same pre-commit
hook), more than one process can decide the table is missing and race
to create it, crashing with "table already exists". migrate_safely
serializes migration across processes with a file lock so only one
process ever runs it at a time; the rest simply find the schema already
up to date once they acquire the lock.
migrate_safely ¶
migrate_safely(db_path: Path) -> None
Apply Django migrations without racing other concurrent processes.
Must be called after django.setup(). Serializes access with a
file lock next to db_path so concurrent processes targeting the
same database apply migrations one at a time instead of racing to
create the migration bookkeeping table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
db_path
|
Path
|
Path to the database file the migrations apply to. |
required |
Source code in src/winidjango/core/db/setup.py
sql ¶
Low-level helper to execute raw SQL against Django's database.
This module exposes :func:execute_sql which runs a parameterized SQL
query using Django's database connection and returns column names and
rows. It is intended for one-off queries where ORM abstractions are
insufficient or when reading complex reports from the database.
The helper uses Django's connection cursor context manager to ensure resources are cleaned up correctly. Results are fetched into memory so avoid using it for very large result sets.
execute_sql ¶
execute_sql(
sql: str, params: dict[str, Any] | None = None
) -> tuple[list[str], list[tuple[Any, ...]]]
Execute a SQL statement and return column names and rows.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sql
|
str
|
SQL statement possibly containing named placeholders
( |
required |
params
|
dict[str, Any] | None
|
Optional mapping of parameters to bind to the query. |
None
|
Returns:
| Type | Description |
|---|---|
list[str]
|
Tuple[List[str], List[Tuple[Any, ...]]]: A tuple where the first |
list[tuple[Any, ...]]
|
element is the list of column names (empty list if the statement |
tuple[list[str], list[tuple[Any, ...]]]
|
returned no rows) and the second element is a list of row tuples. |
Raises:
| Type | Description |
|---|---|
Error
|
Propagates underlying database errors raised by Django's database backend. |
Source code in src/winidjango/core/db/sql.py
rig ¶
init module.
configs ¶
Package initialization.
pyproject ¶
Manage pyproject.toml (PEP 518, 621, 660).
Handles metadata, dependencies, build config (uv), tool configs (ruff, ty, pytest, bandit, rumdl). Enforces opinionated defaults: all ruff rules (except D203, D213, COM812, ANN401), Google docstrings, strict ty, bandit security, coverage threshold. Validation uses subset checking (users can add extra configs). Priority 20 (created early for other configs to read).
Utility methods: project info, dependencies, Python versions, license detection, classifiers.
DjangoPyprojectConfigFile ¶
Bases: PyprojectConfigFile, PyprojectConfigFile
Overrides base to resolve conflicts.
Only needed when developing Winidjango itself.
PyprojectConfigFile ¶
Bases: PyprojectConfigFile
You can override methods from the base class to customize behavior.
resources ¶
init module.
tools ¶
Tool wrappers for CLI tools used in development workflows.
Tools are subclasses of Tool providing methods that return Args objects
for type-safe command construction and execution.