winipedia-django 0.2.27__py3-none-any.whl → 0.2.28__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.
- winipedia_django/dev/__init__.py +1 -0
- winipedia_django/dev/artifacts/__init__.py +1 -0
- winipedia_django/dev/artifacts/builder/__init__.py +1 -0
- winipedia_django/dev/artifacts/builder/builder.py +4 -0
- winipedia_django/dev/artifacts/builds/__init__.py +1 -0
- winipedia_django/dev/artifacts/builds/builder.py +4 -0
- winipedia_django/dev/configs/__init__.py +1 -0
- winipedia_django/dev/configs/configs.py +4 -0
- winipedia_django/utils/__init__.py +1 -0
- winipedia_django/utils/commands/__init__.py +1 -0
- winipedia_django/utils/commands/base/__init__.py +1 -0
- winipedia_django/utils/db/__init__.py +1 -0
- winipedia_django/{bulk.py → utils/db/bulk.py} +1 -1
- winipedia_django/utils/db/fields.py +101 -0
- winipedia_django/utils/db/models.py +146 -0
- winipedia_django/utils/db/sql.py +60 -0
- {winipedia_django-0.2.27.dist-info → winipedia_django-0.2.28.dist-info}/METADATA +1 -1
- winipedia_django-0.2.28.dist-info/RECORD +23 -0
- winipedia_django/database.py +0 -288
- winipedia_django-0.2.27.dist-info/RECORD +0 -9
- /winipedia_django/{command.py → utils/commands/base/command.py} +0 -0
- {winipedia_django-0.2.27.dist-info → winipedia_django-0.2.28.dist-info}/WHEEL +0 -0
- {winipedia_django-0.2.27.dist-info → winipedia_django-0.2.28.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""__init__ module."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""__init__ module."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""__init__ module."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""__init__ module."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""__init__ module."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""__init__ module."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""__init__ module."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""__init__ module."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""__init__ module."""
|
|
@@ -21,7 +21,7 @@ from django.db.models.deletion import Collector
|
|
|
21
21
|
from winipedia_utils.utils.iterating.concurrent.multithreading import multithread_loop
|
|
22
22
|
from winipedia_utils.utils.logging.logger import get_logger
|
|
23
23
|
|
|
24
|
-
from winipedia_django.
|
|
24
|
+
from winipedia_django.utils.db.models import (
|
|
25
25
|
hash_model_instance,
|
|
26
26
|
topological_sort_models,
|
|
27
27
|
)
|
|
@@ -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(
|
|
72
|
+
model: type[Model],
|
|
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,146 @@
|
|
|
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
|
|
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
|
+
from winipedia_utils.utils.logging.logger import get_logger
|
|
16
|
+
|
|
17
|
+
from winipedia_django.utils.db.fields import get_field_names, get_fields
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from django.contrib.contenttypes.fields import GenericForeignKey
|
|
21
|
+
from django.db.models.options import Options
|
|
22
|
+
|
|
23
|
+
logger = get_logger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def topological_sort_models(models: list[type[Model]]) -> list[type[Model]]:
|
|
27
|
+
"""Sort Django models in dependency order using topological sorting.
|
|
28
|
+
|
|
29
|
+
Analyzes foreign key relationships between Django models and returns them
|
|
30
|
+
in an order where dependencies come before dependents. This ensures that
|
|
31
|
+
when performing operations like bulk creation or deletion, models are
|
|
32
|
+
processed in the correct order to avoid foreign key constraint violations.
|
|
33
|
+
|
|
34
|
+
The function uses Python's graphlib.TopologicalSorter to perform the sorting
|
|
35
|
+
based on ForeignKey relationships between the provided models. Only
|
|
36
|
+
relationships between models in the input list are considered.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
models (list[type[Model]]): A list of Django model classes to sort
|
|
40
|
+
based on their foreign key dependencies.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
list[type[Model]]: The input models sorted in dependency order, where
|
|
44
|
+
models that are referenced by foreign keys appear before models
|
|
45
|
+
that reference them. Self-referential relationships are ignored.
|
|
46
|
+
|
|
47
|
+
Raises:
|
|
48
|
+
graphlib.CycleError: If there are circular dependencies between models
|
|
49
|
+
that cannot be resolved.
|
|
50
|
+
|
|
51
|
+
Example:
|
|
52
|
+
>>> # Assuming Author model has no dependencies
|
|
53
|
+
>>> # and Book model has ForeignKey to Author
|
|
54
|
+
>>> models = [Book, Author]
|
|
55
|
+
>>> sorted_models = topological_sort_models(models)
|
|
56
|
+
>>> sorted_models
|
|
57
|
+
[<class 'Author'>, <class 'Book'>]
|
|
58
|
+
|
|
59
|
+
Note:
|
|
60
|
+
- Only considers ForeignKey relationships, not other field types
|
|
61
|
+
- Self-referential foreign keys are ignored to avoid self-loops
|
|
62
|
+
- Only relationships between models in the input list are considered
|
|
63
|
+
"""
|
|
64
|
+
ts: TopologicalSorter[type[Model]] = TopologicalSorter()
|
|
65
|
+
|
|
66
|
+
for model in models:
|
|
67
|
+
deps = {
|
|
68
|
+
field.related_model
|
|
69
|
+
for field in get_fields(model)
|
|
70
|
+
if isinstance(field, ForeignKey)
|
|
71
|
+
and isinstance(field.related_model, type)
|
|
72
|
+
and field.related_model in models
|
|
73
|
+
and field.related_model is not model
|
|
74
|
+
}
|
|
75
|
+
ts.add(model, *deps)
|
|
76
|
+
|
|
77
|
+
return list(ts.static_order())
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def hash_model_instance(
|
|
81
|
+
instance: Model,
|
|
82
|
+
fields: "list[Field[Any, Any] | ForeignObjectRel | GenericForeignKey]",
|
|
83
|
+
) -> int:
|
|
84
|
+
"""Hash a model instance based on its field values.
|
|
85
|
+
|
|
86
|
+
Generates a hash for a Django model instance by considering the values
|
|
87
|
+
of its fields. This can be useful for comparing instances, especially
|
|
88
|
+
when dealing with related objects or complex data structures. The hash
|
|
89
|
+
is generated by recursively hashing related objects up to a specified
|
|
90
|
+
depth.
|
|
91
|
+
This is not very reliable, use with caution.
|
|
92
|
+
Only use if working with unsafed objects or bulks, as with safed
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
instance (Model): The Django model instance to hash
|
|
96
|
+
fields (list[str]): The fields to hash
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
int: The hash value representing the instance's data
|
|
100
|
+
|
|
101
|
+
"""
|
|
102
|
+
if instance.pk:
|
|
103
|
+
return hash(instance.pk)
|
|
104
|
+
|
|
105
|
+
field_names = get_field_names(fields)
|
|
106
|
+
model_dict = model_to_dict(instance, fields=field_names)
|
|
107
|
+
sorted_dict = dict(sorted(model_dict.items()))
|
|
108
|
+
values = (type(instance), tuple(sorted_dict.items()))
|
|
109
|
+
return hash(values)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class BaseModel(Model):
|
|
113
|
+
"""Base model for all models in the project.
|
|
114
|
+
|
|
115
|
+
Provides common fields and methods for all models.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
created_at: DateTimeField[datetime, datetime] = DateTimeField(auto_now_add=True)
|
|
119
|
+
updated_at: DateTimeField[datetime, datetime] = DateTimeField(auto_now=True)
|
|
120
|
+
|
|
121
|
+
class Meta:
|
|
122
|
+
"""Mark the model as abstract."""
|
|
123
|
+
|
|
124
|
+
# abstract does not inherit in children
|
|
125
|
+
abstract = True
|
|
126
|
+
|
|
127
|
+
def __str__(self) -> str:
|
|
128
|
+
"""Base string representation of a model.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
str: The string representation of the model as all fields and their values.
|
|
132
|
+
"""
|
|
133
|
+
fields_values = ", ".join(
|
|
134
|
+
f"{field.name}={getattr(self, field.name)}"
|
|
135
|
+
for field in get_fields(self.__class__)
|
|
136
|
+
)
|
|
137
|
+
return f"{self.__class__.__name__}({fields_values})"
|
|
138
|
+
|
|
139
|
+
def __repr__(self) -> str:
|
|
140
|
+
"""Base representation of a model."""
|
|
141
|
+
return str(self)
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def meta(self) -> "Options[Self]":
|
|
145
|
+
"""Get the meta options for the model."""
|
|
146
|
+
return self._meta
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
django.db.Error: If there's a database error during query execution
|
|
37
|
+
django.db.ProgrammingError: If the SQL syntax is invalid
|
|
38
|
+
django.db.IntegrityError: If the query violates database constraints
|
|
39
|
+
|
|
40
|
+
Example:
|
|
41
|
+
>>> sql = "SELECT id, username FROM auth_user WHERE is_active = %(active)s"
|
|
42
|
+
>>> params = {"active": True}
|
|
43
|
+
>>> columns, rows = execute_sql(sql, params)
|
|
44
|
+
>>> columns
|
|
45
|
+
['id', 'username']
|
|
46
|
+
>>> rows[0]
|
|
47
|
+
(1, 'admin')
|
|
48
|
+
|
|
49
|
+
Note:
|
|
50
|
+
- Uses Django's default database connection
|
|
51
|
+
- Automatically manages cursor lifecycle
|
|
52
|
+
- Parameters are safely bound to prevent SQL injection
|
|
53
|
+
- Returns all results in memory - use with caution for large datasets
|
|
54
|
+
"""
|
|
55
|
+
with connection.cursor() as cursor:
|
|
56
|
+
cursor.execute(sql=sql, params=params)
|
|
57
|
+
rows = cursor.fetchall()
|
|
58
|
+
column_names = [col[0] for col in cursor.description]
|
|
59
|
+
|
|
60
|
+
return column_names, rows
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
winipedia_django/__init__.py,sha256=sY_FUPBGtVwOh4PcoXpa-7h3_2XTgcnKBaPxPYI8ycM,555
|
|
2
|
+
winipedia_django/dev/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
|
|
3
|
+
winipedia_django/dev/artifacts/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
|
|
4
|
+
winipedia_django/dev/artifacts/builder/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
|
|
5
|
+
winipedia_django/dev/artifacts/builder/builder.py,sha256=DdbLRMGQQdPgtYu8DSOZrw8Ukz5EmStSq4etgShh6_I,96
|
|
6
|
+
winipedia_django/dev/artifacts/builds/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
|
|
7
|
+
winipedia_django/dev/artifacts/builds/builder.py,sha256=DdbLRMGQQdPgtYu8DSOZrw8Ukz5EmStSq4etgShh6_I,96
|
|
8
|
+
winipedia_django/dev/configs/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
|
|
9
|
+
winipedia_django/dev/configs/configs.py,sha256=FF25IOrj2wBPdybdr8-txGYKRBbS7hMWUVX68PyIKGo,115
|
|
10
|
+
winipedia_django/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
winipedia_django/utils/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
|
|
12
|
+
winipedia_django/utils/commands/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
|
|
13
|
+
winipedia_django/utils/commands/base/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
|
|
14
|
+
winipedia_django/utils/commands/base/command.py,sha256=JKRiNSLG2OaNsYNIQptXLvnLryo96kNEr4DfoVo2I6g,13467
|
|
15
|
+
winipedia_django/utils/db/__init__.py,sha256=XHsbmjiaGom-KX-S3leCY9cJD3aP9p_0X6xYMcdkHBU,23
|
|
16
|
+
winipedia_django/utils/db/bulk.py,sha256=Ak08n5bKoa_OKYblBCjSq69MRM6sMIqxbUCOkHZpQJY,19906
|
|
17
|
+
winipedia_django/utils/db/fields.py,sha256=_jQotlkUdv7WNh-o0Dzmch2q28jbJUOhNDSIBlIvZTk,3600
|
|
18
|
+
winipedia_django/utils/db/models.py,sha256=c-kprUqXkqA0AcoARffGXwBAbMXo7mi9G-YTSC3-r94,5203
|
|
19
|
+
winipedia_django/utils/db/sql.py,sha256=0vyGfyek-9cYW-MidD_LSo1c7hYdhier4IonjMv9HNo,2346
|
|
20
|
+
winipedia_django-0.2.28.dist-info/METADATA,sha256=x9FEH_5dFsMfZcaWhrRAA4OadfK9Isx7im_D2SUQHQo,19942
|
|
21
|
+
winipedia_django-0.2.28.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
22
|
+
winipedia_django-0.2.28.dist-info/licenses/LICENSE,sha256=o316mE2gGzd__JT69p7S_zlOmKiHh8YjpImCCcWyTvM,1066
|
|
23
|
+
winipedia_django-0.2.28.dist-info/RECORD,,
|
winipedia_django/database.py
DELETED
|
@@ -1,288 +0,0 @@
|
|
|
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
|
|
11
|
-
|
|
12
|
-
from django.db import connection
|
|
13
|
-
from django.db.models import DateTimeField, Field, Model
|
|
14
|
-
from django.db.models.fields.related import ForeignKey, ForeignObjectRel
|
|
15
|
-
from django.forms.models import model_to_dict
|
|
16
|
-
from winipedia_utils.utils.logging.logger import get_logger
|
|
17
|
-
|
|
18
|
-
if TYPE_CHECKING:
|
|
19
|
-
from django.contrib.contenttypes.fields import GenericForeignKey
|
|
20
|
-
from django.db.models.options import Options
|
|
21
|
-
|
|
22
|
-
logger = get_logger(__name__)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def get_model_meta(model: type[Model]) -> "Options[Model]":
|
|
26
|
-
"""Get the Django model metadata options object.
|
|
27
|
-
|
|
28
|
-
Retrieves the _meta attribute from a Django model class, which contains
|
|
29
|
-
metadata about the model including field definitions, table name, and
|
|
30
|
-
other model configuration options. This is a convenience wrapper around
|
|
31
|
-
accessing the private _meta attribute directly.
|
|
32
|
-
|
|
33
|
-
Args:
|
|
34
|
-
model (type[Model]): The Django model class to get metadata from.
|
|
35
|
-
|
|
36
|
-
Returns:
|
|
37
|
-
Options[Model]: The model's metadata options object containing
|
|
38
|
-
field definitions, table information, and other model configuration.
|
|
39
|
-
|
|
40
|
-
Example:
|
|
41
|
-
>>> from django.contrib.auth.models import User
|
|
42
|
-
>>> meta = get_model_meta(User)
|
|
43
|
-
>>> meta.db_table
|
|
44
|
-
'auth_user'
|
|
45
|
-
>>> len(meta.get_fields())
|
|
46
|
-
11
|
|
47
|
-
"""
|
|
48
|
-
return model._meta # noqa: SLF001
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def get_fields(
|
|
52
|
-
model: type[Model],
|
|
53
|
-
) -> "list[Field[Any, Any] | ForeignObjectRel | GenericForeignKey]":
|
|
54
|
-
"""Get all fields from a Django model including relationships.
|
|
55
|
-
|
|
56
|
-
Retrieves all field objects from a Django model, including regular fields,
|
|
57
|
-
foreign key relationships, reverse foreign key relationships, and generic
|
|
58
|
-
foreign keys. This provides a comprehensive view of all model attributes
|
|
59
|
-
that can be used for introspection, validation, or bulk operations.
|
|
60
|
-
|
|
61
|
-
Args:
|
|
62
|
-
model (type[Model]): The Django model class to get fields from.
|
|
63
|
-
|
|
64
|
-
Returns:
|
|
65
|
-
list[Field | ForeignObjectRel | GenericForeignKey]: A list
|
|
66
|
-
containing all field objects associated with the model, including:
|
|
67
|
-
- Regular model fields (CharField, IntegerField, etc.)
|
|
68
|
-
- Foreign key fields (ForeignKey, OneToOneField, etc.)
|
|
69
|
-
- Reverse relationship fields (ForeignObjectRel)
|
|
70
|
-
- Generic foreign key fields (GenericForeignKey)
|
|
71
|
-
|
|
72
|
-
Example:
|
|
73
|
-
>>> from django.contrib.auth.models import User
|
|
74
|
-
>>> fields = get_fields(User)
|
|
75
|
-
>>> field_names = [f.name for f in fields if hasattr(f, 'name')]
|
|
76
|
-
>>> 'username' in field_names
|
|
77
|
-
True
|
|
78
|
-
>>> 'email' in field_names
|
|
79
|
-
True
|
|
80
|
-
"""
|
|
81
|
-
return get_model_meta(model).get_fields()
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def get_field_names(
|
|
85
|
-
fields: "list[Field[Any, Any] | ForeignObjectRel | GenericForeignKey]",
|
|
86
|
-
) -> list[str]:
|
|
87
|
-
"""Get the names of all fields from a Django model including relationships.
|
|
88
|
-
|
|
89
|
-
Retrieves the names of all field objects from a Django model, including
|
|
90
|
-
regular fields, foreign key relationships, reverse foreign key relationships,
|
|
91
|
-
and generic foreign keys. This provides a comprehensive view of all model
|
|
92
|
-
attributes that can be used for introspection, validation, or bulk operations.
|
|
93
|
-
|
|
94
|
-
Args:
|
|
95
|
-
fields (list[Field | ForeignObjectRel | GenericForeignKey]):
|
|
96
|
-
The list of field objects to get names from.
|
|
97
|
-
|
|
98
|
-
Returns:
|
|
99
|
-
list[str]: A list containing the names of all fields.
|
|
100
|
-
|
|
101
|
-
Example:
|
|
102
|
-
>>> from django.contrib.auth.models import User
|
|
103
|
-
>>> fields = get_fields(User)
|
|
104
|
-
>>> field_names = get_field_names(fields)
|
|
105
|
-
>>> 'username' in field_names
|
|
106
|
-
True
|
|
107
|
-
>>> 'email' in field_names
|
|
108
|
-
True
|
|
109
|
-
"""
|
|
110
|
-
return [field.name for field in fields]
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def topological_sort_models(models: list[type[Model]]) -> list[type[Model]]:
|
|
114
|
-
"""Sort Django models in dependency order using topological sorting.
|
|
115
|
-
|
|
116
|
-
Analyzes foreign key relationships between Django models and returns them
|
|
117
|
-
in an order where dependencies come before dependents. This ensures that
|
|
118
|
-
when performing operations like bulk creation or deletion, models are
|
|
119
|
-
processed in the correct order to avoid foreign key constraint violations.
|
|
120
|
-
|
|
121
|
-
The function uses Python's graphlib.TopologicalSorter to perform the sorting
|
|
122
|
-
based on ForeignKey relationships between the provided models. Only
|
|
123
|
-
relationships between models in the input list are considered.
|
|
124
|
-
|
|
125
|
-
Args:
|
|
126
|
-
models (list[type[Model]]): A list of Django model classes to sort
|
|
127
|
-
based on their foreign key dependencies.
|
|
128
|
-
|
|
129
|
-
Returns:
|
|
130
|
-
list[type[Model]]: The input models sorted in dependency order, where
|
|
131
|
-
models that are referenced by foreign keys appear before models
|
|
132
|
-
that reference them. Self-referential relationships are ignored.
|
|
133
|
-
|
|
134
|
-
Raises:
|
|
135
|
-
graphlib.CycleError: If there are circular dependencies between models
|
|
136
|
-
that cannot be resolved.
|
|
137
|
-
|
|
138
|
-
Example:
|
|
139
|
-
>>> # Assuming Author model has no dependencies
|
|
140
|
-
>>> # and Book model has ForeignKey to Author
|
|
141
|
-
>>> models = [Book, Author]
|
|
142
|
-
>>> sorted_models = topological_sort_models(models)
|
|
143
|
-
>>> sorted_models
|
|
144
|
-
[<class 'Author'>, <class 'Book'>]
|
|
145
|
-
|
|
146
|
-
Note:
|
|
147
|
-
- Only considers ForeignKey relationships, not other field types
|
|
148
|
-
- Self-referential foreign keys are ignored to avoid self-loops
|
|
149
|
-
- Only relationships between models in the input list are considered
|
|
150
|
-
"""
|
|
151
|
-
ts: TopologicalSorter[type[Model]] = TopologicalSorter()
|
|
152
|
-
|
|
153
|
-
for model in models:
|
|
154
|
-
deps = {
|
|
155
|
-
field.related_model
|
|
156
|
-
for field in get_fields(model)
|
|
157
|
-
if isinstance(field, ForeignKey)
|
|
158
|
-
and isinstance(field.related_model, type)
|
|
159
|
-
and field.related_model in models
|
|
160
|
-
and field.related_model is not model
|
|
161
|
-
}
|
|
162
|
-
ts.add(model, *deps)
|
|
163
|
-
|
|
164
|
-
return list(ts.static_order())
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
def execute_sql(
|
|
168
|
-
sql: str, params: dict[str, Any] | None = None
|
|
169
|
-
) -> tuple[list[str], list[Any]]:
|
|
170
|
-
"""Execute raw SQL query and return column names with results.
|
|
171
|
-
|
|
172
|
-
Executes a raw SQL query using Django's database connection and returns
|
|
173
|
-
both the column names and the result rows. This provides a convenient
|
|
174
|
-
way to run custom SQL queries while maintaining Django's database
|
|
175
|
-
connection management and parameter binding for security.
|
|
176
|
-
|
|
177
|
-
The function automatically handles cursor management and ensures proper
|
|
178
|
-
cleanup of database resources. Parameters are safely bound to prevent
|
|
179
|
-
SQL injection attacks.
|
|
180
|
-
|
|
181
|
-
Args:
|
|
182
|
-
sql (str): The SQL query string to execute. Can contain parameter
|
|
183
|
-
placeholders that will be safely bound using the params argument.
|
|
184
|
-
params (dict[str, Any] | None, optional): Dictionary of parameters
|
|
185
|
-
to bind to the SQL query for safe parameter substitution.
|
|
186
|
-
Defaults to None if no parameters are needed.
|
|
187
|
-
|
|
188
|
-
Returns:
|
|
189
|
-
tuple[list[str], list[Any]]: A tuple containing:
|
|
190
|
-
- list[str]: Column names from the query result
|
|
191
|
-
- list[Any]: List of result rows, where each row is a tuple
|
|
192
|
-
of values corresponding to the column names
|
|
193
|
-
|
|
194
|
-
Raises:
|
|
195
|
-
django.db.Error: If there's a database error during query execution
|
|
196
|
-
django.db.ProgrammingError: If the SQL syntax is invalid
|
|
197
|
-
django.db.IntegrityError: If the query violates database constraints
|
|
198
|
-
|
|
199
|
-
Example:
|
|
200
|
-
>>> sql = "SELECT id, username FROM auth_user WHERE is_active = %(active)s"
|
|
201
|
-
>>> params = {"active": True}
|
|
202
|
-
>>> columns, rows = execute_sql(sql, params)
|
|
203
|
-
>>> columns
|
|
204
|
-
['id', 'username']
|
|
205
|
-
>>> rows[0]
|
|
206
|
-
(1, 'admin')
|
|
207
|
-
|
|
208
|
-
Note:
|
|
209
|
-
- Uses Django's default database connection
|
|
210
|
-
- Automatically manages cursor lifecycle
|
|
211
|
-
- Parameters are safely bound to prevent SQL injection
|
|
212
|
-
- Returns all results in memory - use with caution for large datasets
|
|
213
|
-
"""
|
|
214
|
-
with connection.cursor() as cursor:
|
|
215
|
-
cursor.execute(sql=sql, params=params)
|
|
216
|
-
rows = cursor.fetchall()
|
|
217
|
-
column_names = [col[0] for col in cursor.description]
|
|
218
|
-
|
|
219
|
-
return column_names, rows
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
def hash_model_instance(
|
|
223
|
-
instance: Model,
|
|
224
|
-
fields: "list[Field[Any, Any] | ForeignObjectRel | GenericForeignKey]",
|
|
225
|
-
) -> int:
|
|
226
|
-
"""Hash a model instance based on its field values.
|
|
227
|
-
|
|
228
|
-
Generates a hash for a Django model instance by considering the values
|
|
229
|
-
of its fields. This can be useful for comparing instances, especially
|
|
230
|
-
when dealing with related objects or complex data structures. The hash
|
|
231
|
-
is generated by recursively hashing related objects up to a specified
|
|
232
|
-
depth.
|
|
233
|
-
This is not very reliable, use with caution.
|
|
234
|
-
Only use if working with unsafed objects or bulks, as with safed
|
|
235
|
-
|
|
236
|
-
Args:
|
|
237
|
-
instance (Model): The Django model instance to hash
|
|
238
|
-
fields (list[str]): The fields to hash
|
|
239
|
-
|
|
240
|
-
Returns:
|
|
241
|
-
int: The hash value representing the instance's data
|
|
242
|
-
|
|
243
|
-
"""
|
|
244
|
-
if instance.pk:
|
|
245
|
-
return hash(instance.pk)
|
|
246
|
-
|
|
247
|
-
field_names = get_field_names(fields)
|
|
248
|
-
model_dict = model_to_dict(instance, fields=field_names)
|
|
249
|
-
sorted_dict = dict(sorted(model_dict.items()))
|
|
250
|
-
values = (type(instance), tuple(sorted_dict.items()))
|
|
251
|
-
return hash(values)
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
class BaseModel(Model):
|
|
255
|
-
"""Base model for all models in the project.
|
|
256
|
-
|
|
257
|
-
Provides common fields and methods for all models.
|
|
258
|
-
"""
|
|
259
|
-
|
|
260
|
-
created_at: DateTimeField[datetime, datetime] = DateTimeField(auto_now_add=True)
|
|
261
|
-
updated_at: DateTimeField[datetime, datetime] = DateTimeField(auto_now=True)
|
|
262
|
-
|
|
263
|
-
class Meta:
|
|
264
|
-
"""Mark the model as abstract."""
|
|
265
|
-
|
|
266
|
-
# abstract does not inherit in children
|
|
267
|
-
abstract = True
|
|
268
|
-
|
|
269
|
-
def __str__(self) -> str:
|
|
270
|
-
"""Base string representation of a model.
|
|
271
|
-
|
|
272
|
-
Returns:
|
|
273
|
-
str: The string representation of the model as all fields and their values.
|
|
274
|
-
"""
|
|
275
|
-
fields_values = ", ".join(
|
|
276
|
-
f"{field.name}={getattr(self, field.name)}"
|
|
277
|
-
for field in get_fields(self.__class__)
|
|
278
|
-
)
|
|
279
|
-
return f"{self.__class__.__name__}({fields_values})"
|
|
280
|
-
|
|
281
|
-
def __repr__(self) -> str:
|
|
282
|
-
"""Base representation of a model."""
|
|
283
|
-
return str(self)
|
|
284
|
-
|
|
285
|
-
@property
|
|
286
|
-
def meta(self) -> "Options[Self]":
|
|
287
|
-
"""Get the meta options for the model."""
|
|
288
|
-
return self._meta
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
winipedia_django/__init__.py,sha256=sY_FUPBGtVwOh4PcoXpa-7h3_2XTgcnKBaPxPYI8ycM,555
|
|
2
|
-
winipedia_django/bulk.py,sha256=v81tJvvDvuNnhqPjyNgY_GNwjqXCbKTsq1n1-8v69nU,19899
|
|
3
|
-
winipedia_django/command.py,sha256=JKRiNSLG2OaNsYNIQptXLvnLryo96kNEr4DfoVo2I6g,13467
|
|
4
|
-
winipedia_django/database.py,sha256=XjUd3Oe1sctqmbwgXBSAEHL8nLsC9vpNQSfoxvW9DKU,10652
|
|
5
|
-
winipedia_django/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
winipedia_django-0.2.27.dist-info/METADATA,sha256=SMPFHGB-uphWNR04JJcYGlMDq4NGlfx4VT9MBq4iisU,19942
|
|
7
|
-
winipedia_django-0.2.27.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
8
|
-
winipedia_django-0.2.27.dist-info/licenses/LICENSE,sha256=o316mE2gGzd__JT69p7S_zlOmKiHh8YjpImCCcWyTvM,1066
|
|
9
|
-
winipedia_django-0.2.27.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|