winipedia-utils 0.1.63__py3-none-any.whl → 0.2.0__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.

Potentially problematic release.


This version of winipedia-utils might be problematic. Click here for more details.

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