winidjango 1.0.4__py3-none-any.whl → 2.0.31__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.
@@ -1,8 +1,9 @@
1
- """Database utilities for Django.
1
+ """Database utilities and lightweight model helpers.
2
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.
3
+ This module provides helpers used across the project when manipulating
4
+ Django models: ordering models by foreign-key dependencies, creating a
5
+ deterministic hash for unsaved instances, and a project-wide
6
+ ``BaseModel`` that exposes common timestamp fields.
6
7
  """
7
8
 
8
9
  from datetime import datetime
@@ -84,23 +85,26 @@ def hash_model_instance(
84
85
  instance: Model,
85
86
  fields: "list[Field[Any, Any] | ForeignObjectRel | GenericForeignKey]",
86
87
  ) -> int:
87
- """Hash a model instance based on its field values.
88
+ """Compute a deterministic hash for a model instance.
88
89
 
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
90
+ The function returns a hash suitable for comparing unsaved model
91
+ instances by their field values. If the instance has a primary key
92
+ (``instance.pk``) that key is hashed and returned immediately; this
93
+ keeps comparisons cheap for persisted objects.
96
94
 
97
95
  Args:
98
- instance (Model): The Django model instance to hash
99
- fields (list[str]): The fields to hash
96
+ instance (Model): The Django model instance to hash.
97
+ fields (list[Field | ForeignObjectRel | GenericForeignKey]):
98
+ Field objects that should be included when computing the hash.
100
99
 
101
100
  Returns:
102
- int: The hash value representing the instance's data
101
+ int: Deterministic integer hash of the instance. For persisted
102
+ instances this is ``hash(instance.pk)``.
103
103
 
104
+ Notes:
105
+ - The returned hash is intended for heuristic comparisons (e.g.
106
+ deduplication in import pipelines) and is not cryptographically
107
+ secure. Use with care when relying on absolute uniqueness.
104
108
  """
105
109
  if instance.pk:
106
110
  return hash(instance.pk)
@@ -113,9 +117,11 @@ def hash_model_instance(
113
117
 
114
118
 
115
119
  class BaseModel(Model):
116
- """Base model for all models in the project.
120
+ """Abstract base model containing common fields and helpers.
117
121
 
118
- Provides common fields and methods for all models.
122
+ Concrete models can inherit from this class to get consistent
123
+ ``created_at`` and ``updated_at`` timestamp fields and convenient
124
+ string representations.
119
125
  """
120
126
 
121
127
  created_at: DateTimeField[datetime, datetime] = DateTimeField(auto_now_add=True)
@@ -128,10 +134,13 @@ class BaseModel(Model):
128
134
  abstract = True
129
135
 
130
136
  def __str__(self) -> str:
131
- """Base string representation of a model.
137
+ """Return a concise human-readable representation.
138
+
139
+ The default shows the model class name and primary key which is
140
+ useful for logging and interactive debugging.
132
141
 
133
142
  Returns:
134
- str: The string representation of the model as all fields and their values.
143
+ str: Short representation, e.g. ``MyModel(123)``.
135
144
  """
136
145
  return f"{self.__class__.__name__}({self.pk})"
137
146
 
@@ -141,5 +150,9 @@ class BaseModel(Model):
141
150
 
142
151
  @property
143
152
  def meta(self) -> "Options[Self]":
144
- """Get the meta options for the model."""
153
+ """Return the model's ``_meta`` options object.
154
+
155
+ This property is a small convenience wrapper used to make access
156
+ sites slightly more explicit in code and improve typing in callers.
157
+ """
145
158
  return self._meta
winidjango/src/db/sql.py CHANGED
@@ -1,4 +1,14 @@
1
- """Module for database operations with sql."""
1
+ """Low-level helper to execute raw SQL against Django's database.
2
+
3
+ This module exposes :func:`execute_sql` which runs a parameterized SQL
4
+ query using Django's database connection and returns column names and
5
+ rows. It is intended for one-off queries where ORM abstractions are
6
+ insufficient or when reading complex reports from the database.
7
+
8
+ The helper uses Django's connection cursor context manager to ensure
9
+ resources are cleaned up correctly. Results are fetched into memory so
10
+ avoid using it for very large result sets.
11
+ """
2
12
 
3
13
  from typing import Any
4
14
 
@@ -7,51 +17,23 @@ from django.db import connection
7
17
 
8
18
  def execute_sql(
9
19
  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.
20
+ ) -> tuple[list[str], list[tuple[Any, ...]]]:
21
+ """Execute a SQL statement and return column names and rows.
21
22
 
22
23
  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.
24
+ sql (str): SQL statement possibly containing named placeholders
25
+ (``%(name)s``) for database binding.
26
+ params (dict[str, Any] | None): Optional mapping of parameters to
27
+ bind to the query.
28
28
 
29
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
30
+ Tuple[List[str], List[Tuple[Any, ...]]]: A tuple where the first
31
+ element is the list of column names (empty list if the statement
32
+ returned no rows) and the second element is a list of row tuples.
35
33
 
36
34
  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
35
+ django.db.Error: Propagates underlying database errors raised by
36
+ Django's database backend.
55
37
  """
56
38
  with connection.cursor() as cursor:
57
39
  cursor.execute(sql=sql, params=params)
@@ -0,0 +1,255 @@
1
+ Metadata-Version: 2.4
2
+ Name: winidjango
3
+ Version: 2.0.31
4
+ Summary: A utils package for django
5
+ Author: Winipedia
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Classifier: Programming Language :: Python :: 3.12
9
+ Classifier: Programming Language :: Python :: 3.13
10
+ Classifier: Programming Language :: Python :: 3.14
11
+ Requires-Dist: django
12
+ Requires-Dist: django-stubs-ext
13
+ Requires-Dist: winiutils
14
+ Requires-Python: >=3.12
15
+ Description-Content-Type: text/markdown
16
+
17
+ # winidjango
18
+
19
+ <!-- tooling -->
20
+ [![pyrig](https://img.shields.io/badge/built%20with-pyrig-3776AB?logo=buildkite&logoColor=black)](https://github.com/Winipedia/pyrig)
21
+ [![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
22
+ [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://pre-commit.com/)
23
+ <!-- code-quality -->
24
+ [![ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
25
+ [![ty](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ty/main/assets/badge/v0.json)](https://github.com/astral-sh/ty)[![mypy](https://img.shields.io/badge/type%20checked-mypy-039dfc.svg)](https://mypy-lang.org/)
26
+ [![security: bandit](https://img.shields.io/badge/security-bandit-yellow.svg)](https://github.com/PyCQA/bandit)
27
+ [![pytest](https://img.shields.io/badge/tested%20with-pytest-46a2f1.svg?logo=pytest)](https://pytest.org/)
28
+ [![codecov](https://codecov.io/gh/Winipedia/winidjango/branch/main/graph/badge.svg)](https://codecov.io/gh/Winipedia/winidjango)
29
+ <!-- package-info -->
30
+ [![PyPI](https://img.shields.io/pypi/v/winidjango?logo=pypi&logoColor=white)](https://pypi.org/project/winidjango/)
31
+ [![Python](https://img.shields.io/badge/python-3.12|3.13|3.14-blue.svg?logo=python&logoColor=white)](https://www.python.org/)
32
+ [![License](https://img.shields.io/github/license/Winipedia/winidjango)](https://github.com/Winipedia/winidjango/blob/main/LICENSE)
33
+ <!-- ci/cd -->
34
+ [![CI](https://img.shields.io/github/actions/workflow/status/Winipedia/winidjango/health_check.yaml?label=CI&logo=github)](https://github.com/Winipedia/winidjango/actions/workflows/health_check.yaml)
35
+ [![CD](https://img.shields.io/github/actions/workflow/status/Winipedia/winidjango/release.yaml?label=CD&logo=github)](https://github.com/Winipedia/winidjango/actions/workflows/release.yaml)
36
+
37
+
38
+ ---
39
+
40
+ > A utils package for django
41
+
42
+ ---
43
+
44
+
45
+ ## Table of Contents
46
+
47
+ - [Features](#features)
48
+ - [Installation](#installation)
49
+ - [Quick Start](#quick-start)
50
+ - [Documentation](#documentation)
51
+ - [Requirements](#requirements)
52
+ - [Development](#development)
53
+ - [Testing](#testing)
54
+ - [Contributing](#contributing)
55
+ - [License](#license)
56
+
57
+ ## Features
58
+
59
+ ### 🚀 High-Performance Bulk Operations
60
+ - **Multithreaded Processing**: Parallel execution of database operations for maximum speed
61
+ - **Automatic Chunking**: Configurable batch sizes (default: 1000) for memory-efficient processing
62
+ - **Transaction Safety**: Atomic operations with intelligent transaction management
63
+ - **Dependency Resolution**: Automatic topological sorting for foreign key relationships
64
+
65
+ ### 🛠️ Database Utilities
66
+ - **Bulk Create/Update/Delete**: Process thousands of records efficiently
67
+ - **Deletion Simulation**: Preview cascade effects before executing destructive operations
68
+ - **Bulk Comparison**: Detect differences between datasets with field-level hashing
69
+ - **Raw SQL Execution**: Safe parameter binding with automatic cursor management
70
+
71
+ ### 📦 Model Utilities
72
+ - **BaseModel**: Abstract base with `created_at`, `updated_at`, and type-safe `meta` property
73
+ - **Topological Sorting**: Automatic dependency ordering for model operations
74
+ - **Field Introspection**: Type-safe utilities for working with model fields
75
+
76
+ ### 🎯 Management Command Framework
77
+ - **ABCBaseCommand**: Template method pattern with automatic logging
78
+ - **ImportDataBaseCommand**: Structured data import with Polars integration
79
+ - **Built-in Arguments**: Standard options for dry-run, batch size, threading, and more
80
+ - **Type Safety**: Full type hints with abstract method enforcement
81
+
82
+ ## Installation
83
+
84
+ ```bash
85
+ pip install winidjango
86
+ ```
87
+
88
+ Or using `uv`:
89
+
90
+ ```bash
91
+ uv add winidjango
92
+ ```
93
+
94
+ ## Quick Start
95
+
96
+ ### Bulk Operations
97
+
98
+ ```python
99
+ from winidjango.src.db.bulk import bulk_create_in_steps
100
+
101
+ # Create 10,000 records in batches of 1000
102
+ authors = [Author(name=f"Author {i}") for i in range(10000)]
103
+ created = bulk_create_in_steps(Author, authors, step=1000)
104
+ ```
105
+
106
+ ### Automatic Dependency Resolution
107
+
108
+ ```python
109
+ from winidjango.src.db.bulk import bulk_create_bulks_in_steps
110
+
111
+ # Create related models in correct order automatically
112
+ results = bulk_create_bulks_in_steps({
113
+ Author: authors,
114
+ Book: books, # Created after Author
115
+ Review: reviews, # Created after Book
116
+ })
117
+ ```
118
+
119
+ ### Deletion Simulation
120
+
121
+ ```python
122
+ from winidjango.src.db.bulk import simulate_bulk_deletion
123
+
124
+ # Preview what would be deleted
125
+ deletion_preview = simulate_bulk_deletion(Author, authors_to_delete)
126
+ print(f"Would delete {len(deletion_preview[Author])} authors")
127
+ print(f"Would cascade delete {len(deletion_preview[Book])} books")
128
+ ```
129
+
130
+ ### Custom Management Command
131
+
132
+ ```python
133
+ from winidjango.src.commands.base.base import ABCBaseCommand
134
+ from argparse import ArgumentParser
135
+
136
+ class MyCommand(ABCBaseCommand):
137
+ def add_command_arguments(self, parser: ArgumentParser) -> None:
138
+ parser.add_argument('--input-file', type=str, required=True)
139
+
140
+ def handle_command(self) -> None:
141
+ input_file = self.get_option('input_file')
142
+ dry_run = self.get_option('dry_run') # Built-in
143
+
144
+ if dry_run:
145
+ self.stdout.write('Dry run mode')
146
+
147
+ # Your logic here
148
+ ```
149
+
150
+ ### Data Import Command
151
+
152
+ ```python
153
+ from winidjango.src.commands.import_data import ImportDataBaseCommand
154
+ import polars as pl
155
+
156
+ class ImportUsersCommand(ImportDataBaseCommand):
157
+ def handle_import(self) -> pl.DataFrame:
158
+ return pl.read_csv("users.csv")
159
+
160
+ def get_cleaning_df_cls(self) -> type[CleaningDF]:
161
+ return MyCleaningDF
162
+
163
+ def get_bulks_by_model(self, df: pl.DataFrame) -> dict[type[Model], Iterable[Model]]:
164
+ users = [User(name=row["name"]) for row in df.iter_rows(named=True)]
165
+ return {User: users}
166
+ ```
167
+
168
+ ## Documentation
169
+
170
+ Comprehensive documentation is available in the [`docs/`](docs/) directory:
171
+
172
+ - **[Database Utilities](docs/db.md)** - Bulk operations, model utilities, and SQL helpers
173
+ - **[Management Commands](docs/commands.md)** - Command framework and data import patterns
174
+ - **[API Reference](docs/index.md)** - Complete API documentation
175
+
176
+ ## Requirements
177
+
178
+ - **Python**: 3.12+
179
+ - **Django**: Compatible with modern Django versions
180
+ - **Dependencies**:
181
+ - `django`
182
+ - `django-stubs-ext`
183
+ - `winiutils`
184
+
185
+ ## Development
186
+
187
+ ### Setup
188
+
189
+ ```bash
190
+ # Clone the repository
191
+ git clone https://github.com/Winipedia/winidjango.git
192
+ cd winidjango
193
+
194
+ # Install dependencies
195
+ uv sync
196
+
197
+ # Install pre-commit hooks
198
+ pre-commit install
199
+ ```
200
+
201
+ ### Code Quality
202
+
203
+ This project uses:
204
+ - **mypy**: Strict type checking
205
+ - **ruff**: Linting and formatting
206
+ - **bandit**: Security analysis
207
+ - **pytest**: Testing framework
208
+
209
+ ```bash
210
+ # Run type checking
211
+ mypy .
212
+
213
+ # Run linting
214
+ ruff check .
215
+
216
+ # Run security checks
217
+ bandit -r winidjango
218
+
219
+ # Format code
220
+ ruff format .
221
+ ```
222
+
223
+ ## Testing
224
+
225
+ ```bash
226
+ # Run all tests
227
+ pytest
228
+
229
+ # Run with coverage
230
+ pytest --cov=winidjango
231
+
232
+ # Run specific test file
233
+ pytest tests/test_winidjango/test_src/test_db/test_bulk.py
234
+ ```
235
+
236
+ ## Contributing
237
+
238
+ Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
239
+
240
+ 1. Fork the repository
241
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
242
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
243
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
244
+ 5. Open a Pull Request
245
+
246
+ ## License
247
+
248
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
249
+
250
+ ## Acknowledgments
251
+
252
+ - Built with [pyrig](https://github.com/Winipedia/pyrig) - Python project scaffolding tool
253
+ - Integrates with [winiutils](https://github.com/Winipedia/winiutils) - General Python utilities
254
+
255
+ ---
@@ -0,0 +1,27 @@
1
+ winidjango/__init__.py,sha256=pxCy4ywQWxN3MKjm9U2vcIDz-nsvDLgNzsE9IOq15DQ,823
2
+ winidjango/dev/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
3
+ winidjango/dev/builders/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
4
+ winidjango/dev/cli/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
5
+ winidjango/dev/cli/subcommands.py,sha256=iurWZwJwEKAfGpfjkn1YOhnRbIruCB4ouE-8R_Lh3JY,228
6
+ winidjango/dev/configs/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
7
+ winidjango/dev/configs/configs.py,sha256=N85kVaRHCHXc-ny0jbuYQwTcrxQJx_X_BcG30IcyrGw,586
8
+ winidjango/dev/tests/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
9
+ winidjango/dev/tests/fixtures/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
10
+ winidjango/main.py,sha256=TdxQpUtNZIiYCsEdysMlQW-1UNy0__WTax-Ot5E2eYQ,144
11
+ winidjango/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ winidjango/resources/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
13
+ winidjango/src/__init__.py,sha256=LOIpCo9zHZCs_ifeiw3PLILm6ik1hyIHJNC9gYir1Eg,569
14
+ winidjango/src/commands/__init__.py,sha256=5rI9IBYLsGpwbdxPzEP21EWlRFC0-6EzTm3NRc758Fk,376
15
+ winidjango/src/commands/base/__init__.py,sha256=ZpOXolcoL9RnhppgzKqF0581gcLcsjin8hydYUd_QZc,320
16
+ winidjango/src/commands/base/base.py,sha256=SeJxHiH_YA2J0bEhGGScniXcwekFLUB8MmdCTJ5hGik,6162
17
+ winidjango/src/commands/import_data.py,sha256=kQatdfQ7HRH2VFSwmVGx6s4J7AAh2FKatTEqWLQDScI,4354
18
+ winidjango/src/db/__init__.py,sha256=x1zuU0bJMuPxqhBAW6g7Ob8BrcC0wyW_NlLn9S4ZaqI,323
19
+ winidjango/src/db/bulk.py,sha256=5mJyGyt3vtu8cf-xji3dd2zxAU5eH0tuV85RjXIMriw,20110
20
+ winidjango/src/db/fields.py,sha256=sa_qe36rQoh7zzsgbadsREKYOqzgx0WDar5tPdZ4rpE,2098
21
+ winidjango/src/db/models.py,sha256=LBdN1pbG4jp_yy_pEKdXxNd05ZXAVCkj7srvO-UE1vw,5750
22
+ winidjango/src/db/sql.py,sha256=MMiJyc8uhJ_4VcN2s6i8koNfvDKxsSv3Gpb_kD-01IE,1638
23
+ winidjango-2.0.31.dist-info/licenses/LICENSE,sha256=o316mE2gGzd__JT69p7S_zlOmKiHh8YjpImCCcWyTvM,1066
24
+ winidjango-2.0.31.dist-info/WHEEL,sha256=xDCZ-UyfvkGuEHPeI7BcJzYKIZzdqN8A8o1M5Om8IyA,79
25
+ winidjango-2.0.31.dist-info/entry_points.txt,sha256=qM1ENsRWSLulhF8Cy92TeHW-4v8SAgzkRW7hNHXHG-4,55
26
+ winidjango-2.0.31.dist-info/METADATA,sha256=s5VV6RvdhZvKXCiOTvHlsuoNPVzGKBWo-LmqGMoUNfU,8263
27
+ winidjango-2.0.31.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.9.17
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
+
@@ -1,4 +0,0 @@
1
- """Build script.
2
-
3
- All subclasses of Builder in the builds package are automatically called.
4
- """