winidjango 1.0.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,101 @@
1
+ """Fields module.
2
+
3
+ Utils for working with Django model fields.
4
+ """
5
+
6
+ from typing import TYPE_CHECKING, Any
7
+
8
+ from django.db.models import Field, Model
9
+
10
+ if TYPE_CHECKING:
11
+ from django.contrib.contenttypes.fields import GenericForeignKey
12
+ from django.db.models.fields.related import ForeignObjectRel
13
+ from django.db.models.options import Options
14
+
15
+
16
+ def get_field_names(
17
+ fields: "list[Field[Any, Any] | ForeignObjectRel | GenericForeignKey]",
18
+ ) -> list[str]:
19
+ """Get the names of all fields from a Django model including relationships.
20
+
21
+ Retrieves the names of all field objects from a Django model, including
22
+ regular fields, foreign key relationships, reverse foreign key relationships,
23
+ and generic foreign keys. This provides a comprehensive view of all model
24
+ attributes that can be used for introspection, validation, or bulk operations.
25
+
26
+ Args:
27
+ fields (list[Field | ForeignObjectRel | GenericForeignKey]):
28
+ The list of field objects to get names from.
29
+
30
+ Returns:
31
+ list[str]: A list containing the names of all fields.
32
+
33
+ Example:
34
+ >>> from django.contrib.auth.models import User
35
+ >>> fields = get_fields(User)
36
+ >>> field_names = get_field_names(fields)
37
+ >>> 'username' in field_names
38
+ True
39
+ >>> 'email' in field_names
40
+ True
41
+ """
42
+ return [field.name for field in fields]
43
+
44
+
45
+ def get_model_meta(model: type[Model]) -> "Options[Model]":
46
+ """Get the Django model metadata options object.
47
+
48
+ Retrieves the _meta attribute from a Django model class, which contains
49
+ metadata about the model including field definitions, table name, and
50
+ other model configuration options. This is a convenience wrapper around
51
+ accessing the private _meta attribute directly.
52
+
53
+ Args:
54
+ model (type[Model]): The Django model class to get metadata from.
55
+
56
+ Returns:
57
+ Options[Model]: The model's metadata options object containing
58
+ field definitions, table information, and other model configuration.
59
+
60
+ Example:
61
+ >>> from django.contrib.auth.models import User
62
+ >>> meta = get_model_meta(User)
63
+ >>> meta.db_table
64
+ 'auth_user'
65
+ >>> len(meta.get_fields())
66
+ 11
67
+ """
68
+ return model._meta # noqa: SLF001
69
+
70
+
71
+ def get_fields[TModel: Model](
72
+ model: type[TModel],
73
+ ) -> "list[Field[Any, Any] | ForeignObjectRel | GenericForeignKey]":
74
+ """Get all fields from a Django model including relationships.
75
+
76
+ Retrieves all field objects from a Django model, including regular fields,
77
+ foreign key relationships, reverse foreign key relationships, and generic
78
+ foreign keys. This provides a comprehensive view of all model attributes
79
+ that can be used for introspection, validation, or bulk operations.
80
+
81
+ Args:
82
+ model (type[Model]): The Django model class to get fields from.
83
+
84
+ Returns:
85
+ list[Field | ForeignObjectRel | GenericForeignKey]: A list
86
+ containing all field objects associated with the model, including:
87
+ - Regular model fields (CharField, IntegerField, etc.)
88
+ - Foreign key fields (ForeignKey, OneToOneField, etc.)
89
+ - Reverse relationship fields (ForeignObjectRel)
90
+ - Generic foreign key fields (GenericForeignKey)
91
+
92
+ Example:
93
+ >>> from django.contrib.auth.models import User
94
+ >>> fields = get_fields(User)
95
+ >>> field_names = [f.name for f in fields if hasattr(f, 'name')]
96
+ >>> 'username' in field_names
97
+ True
98
+ >>> 'email' in field_names
99
+ True
100
+ """
101
+ return get_model_meta(model).get_fields()
@@ -0,0 +1,145 @@
1
+ """Database utilities for Django.
2
+
3
+ This module provides utility functions for working with Django models,
4
+ including hashing, topological sorting, and database operations.
5
+ These utilities help with efficient and safe database interactions.
6
+ """
7
+
8
+ from datetime import datetime
9
+ from graphlib import TopologicalSorter
10
+ from typing import TYPE_CHECKING, Any, Self, cast
11
+
12
+ from django.db.models import DateTimeField, Field, Model
13
+ from django.db.models.fields.related import ForeignKey, ForeignObjectRel
14
+ from django.forms.models import model_to_dict
15
+
16
+ from winidjango.src.db.fields import get_field_names, get_fields
17
+
18
+ if TYPE_CHECKING:
19
+ from django.contrib.contenttypes.fields import GenericForeignKey
20
+ from django.db.models.options import Options
21
+
22
+ import logging
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ def topological_sort_models[TModel: Model](
28
+ models: list[type[TModel]],
29
+ ) -> list[type[TModel]]:
30
+ """Sort Django models in dependency order using topological sorting.
31
+
32
+ Analyzes foreign key relationships between Django models and returns them
33
+ in an order where dependencies come before dependents. This ensures that
34
+ when performing operations like bulk creation or deletion, models are
35
+ processed in the correct order to avoid foreign key constraint violations.
36
+
37
+ The function uses Python's graphlib.TopologicalSorter to perform the sorting
38
+ based on ForeignKey relationships between the provided models. Only
39
+ relationships between models in the input list are considered.
40
+
41
+ Args:
42
+ models (list[type[Model]]): A list of Django model classes to sort
43
+ based on their foreign key dependencies.
44
+
45
+ Returns:
46
+ list[type[Model]]: The input models sorted in dependency order, where
47
+ models that are referenced by foreign keys appear before models
48
+ that reference them. Self-referential relationships are ignored.
49
+
50
+ Raises:
51
+ graphlib.CycleError: If there are circular dependencies between models
52
+ that cannot be resolved.
53
+
54
+ Example:
55
+ >>> # Assuming Author model has no dependencies
56
+ >>> # and Book model has ForeignKey to Author
57
+ >>> models = [Book, Author]
58
+ >>> sorted_models = topological_sort_models(models)
59
+ >>> sorted_models
60
+ [<class 'Author'>, <class 'Book'>]
61
+
62
+ Note:
63
+ - Only considers ForeignKey relationships, not other field types
64
+ - Self-referential foreign keys are ignored to avoid self-loops
65
+ - Only relationships between models in the input list are considered
66
+ """
67
+ ts: TopologicalSorter[type[TModel]] = TopologicalSorter()
68
+
69
+ for model in models:
70
+ deps = {
71
+ cast("type[TModel]", field.related_model)
72
+ for field in get_fields(model)
73
+ if isinstance(field, ForeignKey)
74
+ and isinstance(field.related_model, type)
75
+ and field.related_model in models
76
+ and field.related_model is not model
77
+ }
78
+ ts.add(model, *deps)
79
+
80
+ return list(ts.static_order())
81
+
82
+
83
+ def hash_model_instance(
84
+ instance: Model,
85
+ fields: "list[Field[Any, Any] | ForeignObjectRel | GenericForeignKey]",
86
+ ) -> int:
87
+ """Hash a model instance based on its field values.
88
+
89
+ Generates a hash for a Django model instance by considering the values
90
+ of its fields. This can be useful for comparing instances, especially
91
+ when dealing with related objects or complex data structures. The hash
92
+ is generated by recursively hashing related objects up to a specified
93
+ depth.
94
+ This is not very reliable, use with caution.
95
+ Only use if working with unsafed objects or bulks, as with safed
96
+
97
+ Args:
98
+ instance (Model): The Django model instance to hash
99
+ fields (list[str]): The fields to hash
100
+
101
+ Returns:
102
+ int: The hash value representing the instance's data
103
+
104
+ """
105
+ if instance.pk:
106
+ return hash(instance.pk)
107
+
108
+ field_names = get_field_names(fields)
109
+ model_dict = model_to_dict(instance, fields=field_names)
110
+ sorted_dict = dict(sorted(model_dict.items()))
111
+ values = (type(instance), tuple(sorted_dict.items()))
112
+ return hash(values)
113
+
114
+
115
+ class BaseModel(Model):
116
+ """Base model for all models in the project.
117
+
118
+ Provides common fields and methods for all models.
119
+ """
120
+
121
+ created_at: DateTimeField[datetime, datetime] = DateTimeField(auto_now_add=True)
122
+ updated_at: DateTimeField[datetime, datetime] = DateTimeField(auto_now=True)
123
+
124
+ class Meta:
125
+ """Mark the model as abstract."""
126
+
127
+ # abstract does not inherit in children
128
+ abstract = True
129
+
130
+ def __str__(self) -> str:
131
+ """Base string representation of a model.
132
+
133
+ Returns:
134
+ str: The string representation of the model as all fields and their values.
135
+ """
136
+ return f"{self.__class__.__name__}({self.pk})"
137
+
138
+ def __repr__(self) -> str:
139
+ """Base representation of a model."""
140
+ return str(self)
141
+
142
+ @property
143
+ def meta(self) -> "Options[Self]":
144
+ """Get the meta options for the model."""
145
+ return self._meta
@@ -0,0 +1,63 @@
1
+ """Module for database operations with sql."""
2
+
3
+ from typing import Any
4
+
5
+ from django.db import connection
6
+
7
+
8
+ def execute_sql(
9
+ sql: str, params: dict[str, Any] | None = None
10
+ ) -> tuple[list[str], list[Any]]:
11
+ """Execute raw SQL query and return column names with results.
12
+
13
+ Executes a raw SQL query using Django's database connection and returns
14
+ both the column names and the result rows. This provides a convenient
15
+ way to run custom SQL queries while maintaining Django's database
16
+ connection management and parameter binding for security.
17
+
18
+ The function automatically handles cursor management and ensures proper
19
+ cleanup of database resources. Parameters are safely bound to prevent
20
+ SQL injection attacks.
21
+
22
+ Args:
23
+ sql (str): The SQL query string to execute. Can contain parameter
24
+ placeholders that will be safely bound using the params argument.
25
+ params (dict[str, Any] | None, optional): Dictionary of parameters
26
+ to bind to the SQL query for safe parameter substitution.
27
+ Defaults to None if no parameters are needed.
28
+
29
+ Returns:
30
+ tuple[list[str], list[Any]]: A tuple containing:
31
+ - list[str]: Column names from the query result
32
+ - list[Any]: List of result rows, where each row is a tuple
33
+ of values corresponding to the column names
34
+ Empty list if no results are returned
35
+
36
+ Raises:
37
+ django.db.Error: If there's a database error during query execution
38
+ django.db.ProgrammingError: If the SQL syntax is invalid
39
+ django.db.IntegrityError: If the query violates database constraints
40
+
41
+ Example:
42
+ >>> sql = "SELECT id, username FROM auth_user WHERE is_active = %(active)s"
43
+ >>> params = {"active": True}
44
+ >>> columns, rows = execute_sql(sql, params)
45
+ >>> columns
46
+ ['id', 'username']
47
+ >>> rows[0]
48
+ (1, 'admin')
49
+
50
+ Note:
51
+ - Uses Django's default database connection
52
+ - Automatically manages cursor lifecycle
53
+ - Parameters are safely bound to prevent SQL injection
54
+ - Returns all results in memory - use with caution for large datasets
55
+ """
56
+ with connection.cursor() as cursor:
57
+ cursor.execute(sql=sql, params=params)
58
+ rows = cursor.fetchall()
59
+ column_names = (
60
+ [col[0] for col in cursor.description] if cursor.description else []
61
+ )
62
+
63
+ return column_names, rows
@@ -0,0 +1,309 @@
1
+ Metadata-Version: 2.4
2
+ Name: winidjango
3
+ Version: 1.0.4
4
+ Summary: A utils package for django
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Author: Winipedia
8
+ Author-email: win.steveker@gmx.de
9
+ Requires-Python: >=3.12
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Requires-Dist: django
15
+ Requires-Dist: pyrig
16
+ Requires-Dist: winiutils
17
+ Description-Content-Type: text/markdown
18
+
19
+ # winidjango
20
+
21
+ (This project uses [pyrig](https://github.com/Winipedia/pyrig))
22
+
23
+ ## Overview
24
+
25
+ **winidjango** is a production-ready Django utilities library that simplifies complex database operations and provides structured patterns for data management tasks. Built with type safety and performance in mind, it leverages modern Python features and integrates seamlessly with Django's ORM.
26
+
27
+ ## Features
28
+
29
+ ### Database Utilities (`winidjango.src.db`)
30
+
31
+ High-performance database operations with automatic optimization, dependency management, and type safety.
32
+
33
+ #### Bulk Operations (`bulk.py`)
34
+
35
+ Efficiently process large datasets with automatic chunking, multithreading, and transaction management.
36
+
37
+ **Core Functions:**
38
+
39
+ **`bulk_create_in_steps(model, bulk, step=1000)`**
40
+ - Creates thousands of model instances in configurable batches (default: 1000)
41
+ - Uses multithreading for parallel processing across chunks
42
+ - Returns list of created instances with populated PKs
43
+ - Wrapped in atomic transactions for data integrity
44
+
45
+ **`bulk_update_in_steps(model, bulk, update_fields, step=1000)`**
46
+ - Updates large datasets efficiently in batches
47
+ - Requires explicit `update_fields` list for safety
48
+ - Returns total count of updated objects
49
+ - Multithreaded processing for maximum performance
50
+
51
+ **`bulk_delete_in_steps(model, bulk, step=1000)`**
52
+ - Deletes in batches with cascade tracking
53
+ - Returns tuple: `(total_count, {model_name: count})`
54
+ - Tracks all cascade deletions across related models
55
+ - Safe handling of foreign key constraints
56
+
57
+ **`bulk_create_bulks_in_steps(bulk_by_class, step=1000)`**
58
+ - **Automatic Dependency Resolution**: Creates multiple model types in correct order
59
+ - Uses topological sorting to handle foreign key relationships
60
+ - Accepts dict mapping model classes to instance lists
61
+ - Returns dict with created instances (PKs populated)
62
+
63
+ **Advanced Comparison & Simulation:**
64
+
65
+ **`get_differences_between_bulks(bulk1, bulk2, fields)`**
66
+ - Compares two bulks by hashing field values
67
+ - Returns 4-tuple: `(in_1_not_2, in_2_not_1, in_both_from_1, in_both_from_2)`
68
+ - Useful for sync operations and change detection
69
+ - Preserves original object references
70
+
71
+ **`simulate_bulk_deletion(model_class, entries)`**
72
+ - **Preview deletions without executing** using Django's Collector
73
+ - Returns dict mapping models to objects that would be deleted
74
+ - Includes all cascade deletions
75
+ - Perfect for "what-if" analysis before destructive operations
76
+
77
+ **`multi_simulate_bulk_deletion(entries)`**
78
+ - Simulates deletions across multiple model types
79
+ - Aggregates cascade effects into single summary
80
+ - Accepts dict of `{model_class: [instances]}`
81
+
82
+ **Usage Examples:**
83
+
84
+ ```python
85
+ from winidjango.src.db.bulk import (
86
+ bulk_create_in_steps,
87
+ bulk_create_bulks_in_steps,
88
+ get_differences_between_bulks,
89
+ simulate_bulk_deletion,
90
+ )
91
+
92
+ # Create 10,000 objects in batches of 1000
93
+ authors = [Author(name=f"Author {i}") for i in range(10000)]
94
+ created = bulk_create_in_steps(Author, authors, step=1000)
95
+ # Uses multithreading: ~10x faster than individual saves
96
+
97
+ # Create related models in dependency order
98
+ books = [Book(title=f"Book {i}", author=author) for i, author in enumerate(created)]
99
+ reviews = [Review(book=book, rating=5) for book in books]
100
+
101
+ results = bulk_create_bulks_in_steps({
102
+ Author: authors,
103
+ Book: books, # Created after Author (foreign key dependency)
104
+ Review: reviews, # Created after Book (foreign key dependency)
105
+ })
106
+ # Automatically sorted: Author → Book → Review
107
+
108
+ # Compare two datasets
109
+ from winidjango.src.db.fields import get_fields
110
+ fields = get_fields(Author)
111
+ old_authors = Author.objects.all()
112
+ new_authors = [Author(name=f"Updated {i}") for i in range(100)]
113
+
114
+ to_delete, to_create, unchanged_old, unchanged_new = get_differences_between_bulks(
115
+ list(old_authors), new_authors, fields
116
+ )
117
+
118
+ # Preview deletion impact
119
+ deletion_preview = simulate_bulk_deletion(Author, to_delete)
120
+ # Returns: {Author: {<Author: 1>, <Author: 2>}, Book: {<Book: 1>, <Book: 2>}, ...}
121
+ print(f"Would delete {len(deletion_preview[Author])} authors")
122
+ print(f"Would cascade delete {len(deletion_preview[Book])} books")
123
+ ```
124
+
125
+ **Key Features:**
126
+ - **Multithreading**: Parallel processing of chunks for maximum speed
127
+ - **Transaction Safety**: Atomic operations with nested transaction warnings
128
+ - **Configurable Batch Size**: Default 1000, adjustable per operation
129
+ - **Type-Safe**: Full generic type hints with overloads
130
+ - **Memory Efficient**: Processes data in chunks, not all at once
131
+
132
+ #### Model Utilities (`models.py`)
133
+
134
+ **`topological_sort_models(models)`**
135
+ - Sorts models by foreign key dependencies using Python's `graphlib.TopologicalSorter`
136
+ - Ensures correct creation/deletion order
137
+ - Ignores self-referential relationships
138
+ - Raises `CycleError` for circular dependencies
139
+
140
+ **`hash_model_instance(instance, fields)`**
141
+ - Hashes model instances for comparison
142
+ - PK-based for saved instances (fast)
143
+ - Field-based for unsaved instances (content comparison)
144
+ - Used internally by `get_differences_between_bulks()`
145
+
146
+ **`BaseModel`** - Abstract base model with common fields:
147
+ - `created_at` - Auto-populated on creation
148
+ - `updated_at` - Auto-updated on save
149
+ - `meta` property - Type-safe access to `_meta`
150
+ - Custom `__str__()` and `__repr__()`
151
+
152
+ ```python
153
+ from winidjango.src.db.models import BaseModel
154
+
155
+ class MyModel(BaseModel):
156
+ name = models.CharField(max_length=100)
157
+
158
+ class Meta(BaseModel.Meta):
159
+ db_table = "my_model"
160
+
161
+ # Automatically includes created_at and updated_at
162
+ obj = MyModel.objects.create(name="test")
163
+ print(obj.created_at) # datetime
164
+ print(obj) # "MyModel(1)"
165
+ ```
166
+
167
+ #### Field Utilities (`fields.py`)
168
+
169
+ **`get_fields(model)`** - Get all fields including relationships
170
+ **`get_field_names(fields)`** - Extract field names from field objects
171
+ **`get_model_meta(model)`** - Type-safe access to model `_meta`
172
+
173
+ ```python
174
+ from winidjango.src.db.fields import get_fields, get_field_names
175
+
176
+ fields = get_fields(User)
177
+ field_names = get_field_names(fields)
178
+ # ['id', 'username', 'email', 'groups', 'user_permissions', ...]
179
+ ```
180
+
181
+ #### SQL Utilities (`sql.py`)
182
+
183
+ **`execute_sql(sql, params=None)`**
184
+ - Execute raw SQL with safe parameter binding
185
+ - Returns tuple: `(column_names, rows)`
186
+ - Automatic cursor management
187
+ - Protection against SQL injection
188
+
189
+ ```python
190
+ from winidjango.src.db.sql import execute_sql
191
+
192
+ columns, rows = execute_sql(
193
+ "SELECT id, username FROM auth_user WHERE is_active = %(active)s",
194
+ params={"active": True}
195
+ )
196
+ # columns: ['id', 'username']
197
+ # rows: [(1, 'admin'), (2, 'user'), ...]
198
+ ```
199
+
200
+ ### Management Commands (`winidjango.src.commands`)
201
+
202
+ A powerful framework for building Django management commands with built-in best practices, automatic logging, and standardized argument handling.
203
+
204
+ #### `ABCBaseCommand` - Base Command Framework
205
+
206
+ Abstract base class that provides a robust foundation for all Django management commands:
207
+
208
+ **Key Features:**
209
+ - **Template Method Pattern**: Enforces consistent command structure while allowing customization
210
+ - **Automatic Logging**: All method calls are logged with performance tracking via `ABCLoggingMixin`
211
+ - **Built-in Common Arguments**: Pre-configured standard options available to all commands:
212
+ - `--dry_run` - Preview changes without executing
213
+ - `--force` - Force execution of actions
214
+ - `--delete` - Enable deletion operations
215
+ - `--yes` - Auto-confirm all prompts
216
+ - `--timeout` - Set command timeout
217
+ - `--batch_size` - Configure batch processing size
218
+ - `--threads` - Control thread count for parallel processing
219
+ - `--processes` - Control process count for multiprocessing
220
+ - **Type-Safe**: Full type hints with abstract method enforcement at compile-time
221
+ - **Structured Execution Flow**: Separates common setup (`base_handle`) from command-specific logic (`handle_command`)
222
+
223
+ **Usage Pattern:**
224
+ ```python
225
+ from winidjango.src.commands.base.base import ABCBaseCommand
226
+ from argparse import ArgumentParser
227
+
228
+ class MyCommand(ABCBaseCommand):
229
+ def add_command_arguments(self, parser: ArgumentParser) -> None:
230
+ """Add command-specific arguments."""
231
+ parser.add_argument('--input-file', type=str, required=True)
232
+ parser.add_argument('--output-format', choices=['json', 'csv'])
233
+
234
+ def handle_command(self) -> None:
235
+ """Execute command logic."""
236
+ input_file = self.get_option('input_file')
237
+ dry_run = self.get_option('dry_run') # Built-in argument
238
+ batch_size = self.get_option('batch_size') # Built-in argument
239
+
240
+ if dry_run:
241
+ self.stdout.write('Dry run mode - no changes will be made')
242
+
243
+ # Your command logic here
244
+ self.process_data(input_file, batch_size)
245
+ ```
246
+
247
+ #### `ImportDataBaseCommand` - Data Import Framework
248
+
249
+ Specialized command for structured data import workflows with automatic cleaning and bulk creation:
250
+
251
+ **Workflow Steps:**
252
+ 1. **Import** (`handle_import()`) - Fetch raw data from any source, returns Polars DataFrame
253
+ 2. **Clean** (`get_cleaning_df_cls()`) - Define data cleaning logic using `winiutils.CleaningDF`
254
+ 3. **Transform** (`get_bulks_by_model()`) - Convert cleaned DataFrame to Django model instances
255
+ 4. **Load** (`import_to_db()`) - Bulk create with automatic dependency resolution via topological sorting
256
+
257
+ **Key Features:**
258
+ - **Polars Integration**: High-performance data processing with Polars DataFrames
259
+ - **Automatic Cleaning**: Leverages `winiutils.CleaningDF` for standardized data cleaning pipeline
260
+ - **Dependency-Aware**: Uses `bulk_create_bulks_in_steps()` to handle foreign key relationships automatically
261
+ - **Inherits All Base Features**: Gets all `ABCBaseCommand` functionality (logging, common args, etc.)
262
+
263
+ **Usage Pattern:**
264
+ ```python
265
+ from winidjango.src.commands.import_data import ImportDataBaseCommand
266
+ from winiutils.src.data.dataframe.cleaning import CleaningDF
267
+ import polars as pl
268
+
269
+ class MyCleaningDF(CleaningDF):
270
+ """Define your data cleaning rules."""
271
+ NAME_COL = "name"
272
+ EMAIL_COL = "email"
273
+
274
+ @classmethod
275
+ def get_rename_map(cls) -> dict[str, str]:
276
+ return {"name": "user_name", "email": "user_email"}
277
+
278
+ @classmethod
279
+ def get_col_dtype_map(cls) -> dict[str, type[pl.DataType]]:
280
+ return {cls.NAME_COL: pl.Utf8, cls.EMAIL_COL: pl.Utf8}
281
+
282
+ # ... other cleaning methods
283
+
284
+ class ImportUsersCommand(ImportDataBaseCommand):
285
+ def handle_import(self) -> pl.DataFrame:
286
+ """Fetch data from source."""
287
+ return pl.read_csv("users.csv")
288
+
289
+ def get_cleaning_df_cls(self) -> type[CleaningDF]:
290
+ """Return your cleaning class."""
291
+ return MyCleaningDF
292
+
293
+ def get_bulks_by_model(self, df: pl.DataFrame) -> dict[type[Model], Iterable[Model]]:
294
+ """Convert cleaned data to model instances."""
295
+ users = [User(name=row["name"], email=row["email"])
296
+ for row in df.iter_rows(named=True)]
297
+ profiles = [Profile(user=user) for user in users]
298
+
299
+ # Automatically created in correct order (User before Profile)
300
+ return {User: users, Profile: profiles}
301
+ ```
302
+
303
+ **Benefits:**
304
+ - **Standardized Import Process**: Consistent pattern across all data import commands
305
+ - **Separation of Concerns**: Import, cleaning, and transformation logic clearly separated
306
+ - **Automatic Optimization**: Bulk operations with multithreading and dependency resolution
307
+ - **Data Quality**: Built-in cleaning pipeline ensures data consistency
308
+ - **Testable**: Each step can be tested independently
309
+
@@ -0,0 +1,26 @@
1
+ winidjango/__init__.py,sha256=1s_h2cuQCaChY4Jk0ZDzzvhCKgvuD59XfzmGH6tm6x4,552
2
+ winidjango/dev/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
3
+ winidjango/dev/artifacts/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
4
+ winidjango/dev/artifacts/builder/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
5
+ winidjango/dev/artifacts/builder/builder.py,sha256=DdbLRMGQQdPgtYu8DSOZrw8Ukz5EmStSq4etgShh6_I,96
6
+ winidjango/dev/cli/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
7
+ winidjango/dev/cli/subcommands.py,sha256=iurWZwJwEKAfGpfjkn1YOhnRbIruCB4ouE-8R_Lh3JY,228
8
+ winidjango/dev/configs/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
9
+ winidjango/dev/configs/configs.py,sha256=fbUjVQCECHsylMBkazUzs2YXpTGlDr4Nq6tPlgf035o,105
10
+ winidjango/main.py,sha256=uZR5lqPpMeSla1EOltLoCnh5V6GYyRL8H_JTYJEqYuo,420
11
+ winidjango/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ winidjango/src/__init__.py,sha256=4h5GWbLSKlFjaNIUbEAiX1wambPOxlE50TWTJnHGs3s,43
13
+ winidjango/src/commands/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
14
+ winidjango/src/commands/base/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
15
+ winidjango/src/commands/base/base.py,sha256=I4fevCwaMgHeDVt9YSSstvvxs44kGD4V09OtAOPNRJE,12716
16
+ winidjango/src/commands/import_data.py,sha256=-heq2183v-vgMoOTOwLmYZ3-tRg68H9Haia3sYa5270,2484
17
+ winidjango/src/db/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
18
+ winidjango/src/db/bulk.py,sha256=Rpw8pUK-swUMm2OrzR5XUzs4Gfx_OB0LvKQus13i9js,22468
19
+ winidjango/src/db/fields.py,sha256=o_gJlb4D7FmNh9smL8qv0SbEeISjd4WXTu4fDQHNJC8,3616
20
+ winidjango/src/db/models.py,sha256=dV6ZdayNFfUCHB6Gpsp_V4a1ZiciKzsQXZ83oW_gj-Q,5052
21
+ winidjango/src/db/sql.py,sha256=MG9iTUTJCetCKGf75k-EyacUzbb-G4u_-NL9NTngFrk,2446
22
+ winidjango-1.0.4.dist-info/METADATA,sha256=kysKXWS3NAh3hCp_YM-n_Pqs2PvqJ7zbSl36JXYfJmQ,11862
23
+ winidjango-1.0.4.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
24
+ winidjango-1.0.4.dist-info/entry_points.txt,sha256=NZcqI0Rnuc57cW9gJtrGTGwbyeOjCkEnfXQcmGjOuIQ,53
25
+ winidjango-1.0.4.dist-info/licenses/LICENSE,sha256=o316mE2gGzd__JT69p7S_zlOmKiHh8YjpImCCcWyTvM,1066
26
+ winidjango-1.0.4.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.2.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ winidjango=pyrig.dev.cli.cli:main
3
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Winipedia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.