winipedia-utils 0.2.0__py3-none-any.whl → 0.2.1__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 (90) hide show
  1. winipedia_utils/concurrent/concurrent.py +245 -245
  2. winipedia_utils/concurrent/multiprocessing.py +130 -130
  3. winipedia_utils/concurrent/multithreading.py +93 -93
  4. winipedia_utils/consts.py +21 -23
  5. winipedia_utils/data/__init__.py +1 -1
  6. winipedia_utils/data/dataframe/__init__.py +1 -1
  7. winipedia_utils/data/dataframe/cleaning.py +378 -378
  8. winipedia_utils/data/structures/__init__.py +1 -1
  9. winipedia_utils/data/structures/dicts.py +16 -16
  10. winipedia_utils/git/__init__.py +1 -1
  11. winipedia_utils/git/gitignore/__init__.py +1 -1
  12. winipedia_utils/git/gitignore/gitignore.py +136 -136
  13. winipedia_utils/git/pre_commit/__init__.py +1 -1
  14. winipedia_utils/git/pre_commit/config.py +70 -70
  15. winipedia_utils/git/pre_commit/hooks.py +109 -109
  16. winipedia_utils/git/pre_commit/run_hooks.py +49 -49
  17. winipedia_utils/iterating/__init__.py +1 -1
  18. winipedia_utils/iterating/iterate.py +29 -29
  19. winipedia_utils/logging/ansi.py +6 -6
  20. winipedia_utils/logging/config.py +64 -64
  21. winipedia_utils/logging/logger.py +26 -26
  22. winipedia_utils/modules/class_.py +119 -119
  23. winipedia_utils/modules/function.py +101 -101
  24. winipedia_utils/modules/module.py +379 -379
  25. winipedia_utils/modules/package.py +390 -390
  26. winipedia_utils/oop/mixins/meta.py +333 -333
  27. winipedia_utils/oop/mixins/mixin.py +37 -37
  28. winipedia_utils/os/__init__.py +1 -1
  29. winipedia_utils/os/os.py +63 -63
  30. winipedia_utils/projects/__init__.py +1 -1
  31. winipedia_utils/projects/poetry/__init__.py +1 -1
  32. winipedia_utils/projects/poetry/config.py +91 -91
  33. winipedia_utils/projects/poetry/poetry.py +31 -31
  34. winipedia_utils/projects/project.py +48 -48
  35. winipedia_utils/resources/__init__.py +1 -1
  36. winipedia_utils/resources/svgs/__init__.py +1 -1
  37. winipedia_utils/resources/svgs/download_arrow.svg +2 -2
  38. winipedia_utils/resources/svgs/exit_fullscreen_icon.svg +5 -5
  39. winipedia_utils/resources/svgs/fullscreen_icon.svg +2 -2
  40. winipedia_utils/resources/svgs/menu_icon.svg +3 -3
  41. winipedia_utils/resources/svgs/pause_icon.svg +3 -3
  42. winipedia_utils/resources/svgs/play_icon.svg +16 -16
  43. winipedia_utils/resources/svgs/plus_icon.svg +23 -23
  44. winipedia_utils/resources/svgs/svg.py +15 -15
  45. winipedia_utils/security/__init__.py +1 -1
  46. winipedia_utils/security/cryptography.py +29 -29
  47. winipedia_utils/security/keyring.py +70 -70
  48. winipedia_utils/setup.py +47 -47
  49. winipedia_utils/testing/assertions.py +23 -23
  50. winipedia_utils/testing/convention.py +177 -177
  51. winipedia_utils/testing/create_tests.py +291 -291
  52. winipedia_utils/testing/fixtures.py +28 -28
  53. winipedia_utils/testing/tests/base/fixtures/__init__.py +1 -1
  54. winipedia_utils/testing/tests/base/fixtures/fixture.py +6 -6
  55. winipedia_utils/testing/tests/base/fixtures/scopes/class_.py +33 -33
  56. winipedia_utils/testing/tests/base/fixtures/scopes/function.py +7 -7
  57. winipedia_utils/testing/tests/base/fixtures/scopes/module.py +31 -31
  58. winipedia_utils/testing/tests/base/fixtures/scopes/package.py +7 -7
  59. winipedia_utils/testing/tests/base/fixtures/scopes/session.py +312 -312
  60. winipedia_utils/testing/tests/base/utils/utils.py +82 -82
  61. winipedia_utils/testing/tests/conftest.py +32 -32
  62. winipedia_utils/text/string.py +126 -126
  63. {winipedia_utils-0.2.0.dist-info → winipedia_utils-0.2.1.dist-info}/METADATA +1 -4
  64. winipedia_utils-0.2.1.dist-info/RECORD +80 -0
  65. {winipedia_utils-0.2.0.dist-info → winipedia_utils-0.2.1.dist-info}/licenses/LICENSE +21 -21
  66. winipedia_utils/django/__init__.py +0 -24
  67. winipedia_utils/django/bulk.py +0 -538
  68. winipedia_utils/django/command.py +0 -334
  69. winipedia_utils/django/database.py +0 -289
  70. winipedia_utils/pyside/__init__.py +0 -1
  71. winipedia_utils/pyside/core/__init__.py +0 -1
  72. winipedia_utils/pyside/core/py_qiodevice.py +0 -476
  73. winipedia_utils/pyside/ui/__init__.py +0 -1
  74. winipedia_utils/pyside/ui/base/__init__.py +0 -1
  75. winipedia_utils/pyside/ui/base/base.py +0 -180
  76. winipedia_utils/pyside/ui/pages/__init__.py +0 -1
  77. winipedia_utils/pyside/ui/pages/base/__init__.py +0 -1
  78. winipedia_utils/pyside/ui/pages/base/base.py +0 -92
  79. winipedia_utils/pyside/ui/pages/browser.py +0 -26
  80. winipedia_utils/pyside/ui/pages/player.py +0 -85
  81. winipedia_utils/pyside/ui/widgets/__init__.py +0 -1
  82. winipedia_utils/pyside/ui/widgets/browser.py +0 -243
  83. winipedia_utils/pyside/ui/widgets/clickable_widget.py +0 -57
  84. winipedia_utils/pyside/ui/widgets/media_player.py +0 -430
  85. winipedia_utils/pyside/ui/widgets/notification.py +0 -78
  86. winipedia_utils/pyside/ui/windows/__init__.py +0 -1
  87. winipedia_utils/pyside/ui/windows/base/__init__.py +0 -1
  88. winipedia_utils/pyside/ui/windows/base/base.py +0 -49
  89. winipedia_utils-0.2.0.dist-info/RECORD +0 -103
  90. {winipedia_utils-0.2.0.dist-info → winipedia_utils-0.2.1.dist-info}/WHEEL +0 -0
@@ -1,334 +0,0 @@
1
- """Command utilities for Django.
2
-
3
- This module provides utility functions for working with Django commands,
4
- including command execution and output handling. These utilities help with
5
- managing and automating Django command-line tasks.
6
- """
7
-
8
- import logging
9
- from abc import abstractmethod
10
- from argparse import ArgumentParser
11
- from typing import Any, final
12
-
13
- from django.core.management import BaseCommand
14
-
15
- from winipedia_utils.oop.mixins.mixin import ABCLoggingMixin
16
-
17
- logger = logging.getLogger(__name__)
18
-
19
-
20
- class ABCBaseCommand(ABCLoggingMixin, BaseCommand):
21
- """Abstract base class for Django management commands with logging and validation.
22
-
23
- This class serves as a foundation for creating Django management commands that
24
- require abstract method implementation enforcement and automatic logging.
25
- It combines Django's BaseCommand with ABCImplementationLoggingMixin to provide
26
- both command functionality and development-time validation.
27
-
28
- The class implements a template method pattern where common argument handling
29
- and execution flow are managed by final methods, while specific implementations
30
- are defined through abstract methods that subclasses must implement.
31
-
32
- Key Features:
33
- - Automatic logging of method calls with performance tracking
34
- - Compile-time validation that all abstract methods are implemented
35
- - Structured argument handling with base and custom arguments
36
- - Template method pattern for consistent command execution flow
37
-
38
- Inheritance Order:
39
- The order of inheritance is critical: ABCImplementationLoggingMixin must
40
- come before BaseCommand because Django's BaseCommand doesn't call
41
- super().__init__(), so the mixin's metaclass initialization must happen
42
- first to ensure proper class construction.
43
-
44
- Example:
45
- >>> class MyCommand(ABCBaseCommand):
46
- ... def add_command_arguments(self, parser):
47
- ... parser.add_argument('--my-option', help='Custom option')
48
- ...
49
- ... def handle_command(self, *args, **options):
50
- ... self.stdout.write('Executing my command')
51
-
52
- Note:
53
- - All methods are automatically logged with performance tracking
54
- - Subclasses must implement add_command_arguments and handle_command
55
- - The @final decorator prevents overriding of template methods
56
- """
57
-
58
- @final
59
- def add_arguments(self, parser: ArgumentParser) -> None:
60
- """Configure command-line arguments for the Django management command.
61
-
62
- This method implements the template method pattern by first adding common
63
- base arguments that are used across multiple commands, then delegating
64
- to the abstract add_command_arguments method for command-specific arguments.
65
-
66
- The @final decorator prevents subclasses from overriding this method,
67
- ensuring consistent argument handling across all commands while still
68
- allowing customization through the abstract method.
69
-
70
- Args:
71
- parser (ArgumentParser): Django's argument parser instance used to
72
- define command-line options and arguments for the command.
73
-
74
- Note:
75
- - This method is final and cannot be overridden by subclasses
76
- - Common arguments are added first via _add_arguments()
77
- - Custom arguments are added via the abstract add_command_arguments()
78
- - Subclasses must implement add_command_arguments() for specific needs
79
- """
80
- # add base args that are used in most commands
81
- self._add_arguments(parser)
82
-
83
- # add additional args that are specific to the command
84
- self.add_command_arguments(parser)
85
-
86
- @final
87
- def _add_arguments(self, parser: ArgumentParser) -> None:
88
- """Add common command-line arguments used across multiple commands.
89
-
90
- This method defines base arguments that are commonly used across different
91
- Django management commands. These arguments provide standard functionality
92
- like dry-run mode, verbosity control, and batch processing options.
93
-
94
- The method is final to ensure consistent base argument handling, while
95
- command-specific arguments are handled through the abstract
96
- add_command_arguments method.
97
-
98
- Args:
99
- parser (ArgumentParser): Django's argument parser instance to which
100
- common arguments should be added.
101
-
102
- Note:
103
- - Provides standard arguments for dry-run, verbosity, and batch processing
104
- - The @final decorator prevents subclasses from overriding this method
105
- - Command-specific arguments should be added via add_command_arguments()
106
- """
107
- parser.add_argument(
108
- "--dry-run",
109
- action="store_true",
110
- help="Show what would be done without actually executing the changes",
111
- )
112
-
113
- parser.add_argument(
114
- "--size",
115
- type=int,
116
- default=None,
117
- help="Size of smth in a command",
118
- )
119
-
120
- parser.add_argument(
121
- "--force",
122
- action="store_true",
123
- help="Force an action in a command",
124
- )
125
-
126
- parser.add_argument(
127
- "--delete",
128
- action="store_true",
129
- help="Deleting smth in a command",
130
- )
131
-
132
- parser.add_argument(
133
- "--quiet",
134
- action="store_true",
135
- help="Suppress non-error output for cleaner automation",
136
- )
137
-
138
- parser.add_argument(
139
- "--debug",
140
- action="store_true",
141
- help="Print debug output for detailed tracing",
142
- )
143
-
144
- parser.add_argument(
145
- "--yes",
146
- action="store_true",
147
- help="Answer yes to all prompts",
148
- default=False,
149
- )
150
-
151
- parser.add_argument(
152
- "--config",
153
- type=str,
154
- help="A configuration setup like filepath or json string for a command",
155
- default=None,
156
- )
157
-
158
- parser.add_argument(
159
- "--timeout",
160
- type=int,
161
- help="Timeout for a command",
162
- default=None,
163
- )
164
-
165
- parser.add_argument(
166
- "--batch-size",
167
- type=int,
168
- default=None,
169
- help="Number of items to process in each batch",
170
- )
171
-
172
- parser.add_argument(
173
- "--no-input",
174
- action="store_true",
175
- help="Do not prompt for user input",
176
- )
177
-
178
- parser.add_argument(
179
- "--threads",
180
- type=int,
181
- default=None,
182
- help="Number of threads to use for processing",
183
- )
184
-
185
- parser.add_argument(
186
- "--processes",
187
- type=int,
188
- default=None,
189
- help="Number of processes to use for processing",
190
- )
191
-
192
- @abstractmethod
193
- def add_command_arguments(self, parser: ArgumentParser) -> None:
194
- """Add command-specific arguments to the argument parser.
195
-
196
- This abstract method must be implemented by subclasses to define
197
- command-specific command-line arguments. It is called after common
198
- base arguments are added, allowing each command to customize its
199
- argument interface while maintaining consistent base functionality.
200
-
201
- Subclasses should use this method to add arguments specific to their
202
- command's functionality, such as file paths, configuration options,
203
- or operational flags.
204
-
205
- Args:
206
- parser (ArgumentParser): Django's argument parser instance to which
207
- command-specific arguments should be added.
208
-
209
- Example:
210
- >>> def add_command_arguments(self, parser):
211
- ... parser.add_argument(
212
- ... '--input-file',
213
- ... type=str,
214
- ... required=True,
215
- ... help='Path to input file'
216
- ... )
217
- ... parser.add_argument(
218
- ... '--output-format',
219
- ... choices=['json', 'csv', 'xml'],
220
- ... default='json',
221
- ... help='Output format for results'
222
- ... )
223
-
224
- Note:
225
- - This method is abstract and must be implemented by subclasses
226
- - Called after _add_arguments() adds common base arguments
227
- - Should focus on command-specific functionality only
228
- """
229
-
230
- @final
231
- def handle(self, *args: Any, **options: Any) -> None:
232
- """Execute the Django management command using template method pattern.
233
-
234
- This method implements the main execution flow for the command by first
235
- calling common handling logic through _handle(), then delegating to
236
- the command-specific implementation via handle_command().
237
-
238
- The @final decorator ensures this execution pattern cannot be overridden,
239
- maintaining consistent command execution flow while allowing customization
240
- through the abstract handle_command method.
241
-
242
- Args:
243
- *args: Positional arguments passed from Django's command execution.
244
- **options: Keyword arguments containing parsed command-line options
245
- and their values as defined by add_arguments().
246
-
247
- Note:
248
- - This method is final and cannot be overridden by subclasses
249
- - Common handling logic is executed first via _handle()
250
- - Command-specific logic is executed via abstract handle_command()
251
- - All method calls are automatically logged with performance tracking
252
- """
253
- self._handle(*args, **options)
254
- self.handle_command(*args, **options)
255
-
256
- @final
257
- def _handle(self, *_args: Any, **options: Any) -> None:
258
- """Execute common handling logic shared across all commands.
259
-
260
- This method is intended to contain common processing logic that should
261
- be executed before command-specific handling. Currently, it serves as
262
- a placeholder for future common functionality such as logging setup,
263
- validation, or shared initialization.
264
-
265
- The method is final to ensure consistent common handling across all
266
- commands, while command-specific logic is handled through the abstract
267
- handle_command method.
268
-
269
- Args:
270
- *args: Positional arguments passed from Django's command execution.
271
- Currently unused but reserved for future common processing.
272
- **options: Keyword arguments containing parsed command-line options.
273
- Currently unused but reserved for future common processing.
274
-
275
- Note:
276
- - Examples might include logging setup, database connection validation, etc.
277
- - The @final decorator prevents subclasses from overriding this method
278
- - Called before handle_command() in the template method pattern
279
- """
280
- # log each option for the command
281
- for key, value in options.items():
282
- logger.info(
283
- "Command '%s' - runs with option: '%s' with value: '%s'",
284
- self.__class__.__name__,
285
- key,
286
- value,
287
- )
288
-
289
- @abstractmethod
290
- def handle_command(self, *args: Any, **options: Any) -> None:
291
- """Execute command-specific logic and functionality.
292
-
293
- This abstract method must be implemented by subclasses to define the
294
- core functionality of the Django management command. It is called after
295
- common handling logic is executed, allowing each command to implement
296
- its specific business logic while benefiting from shared infrastructure.
297
-
298
- This method should contain the main logic that the command is designed
299
- to perform, such as data processing, database operations, file manipulation,
300
- or any other command-specific tasks.
301
-
302
- Args:
303
- *args: Positional arguments passed from Django's command execution.
304
- These are typically not used in Django management commands.
305
- **options: Keyword arguments containing parsed command-line options
306
- and their values as defined by add_command_arguments().
307
-
308
- Example:
309
- >>> def handle_command(self, *args, **options):
310
- ... input_file = options['input_file']
311
- ... dry_run = options['dry_run'] # Base argument
312
- ... batch_size = options['batch_size'] # Base argument
313
- ... quiet = options['quiet'] # Base argument
314
- ...
315
- ... if dry_run:
316
- ... self.stdout.write('Dry run mode - no changes will be made')
317
- ...
318
- ... if not quiet:
319
- ... msg = f'Processing {input_file} in batches of {batch_size}'
320
- ... self.stdout.write(msg)
321
- ...
322
- ... # Perform command-specific operations
323
- ... self.process_file(input_file, batch_size, dry_run)
324
- ...
325
- ... if not quiet:
326
- ... self.stdout.write('Command completed successfully')
327
-
328
- Note:
329
- - This method is abstract and must be implemented by subclasses
330
- - Called after _handle() executes common logic
331
- - Should contain the main functionality of the command
332
- - All method calls are automatically logged with performance tracking
333
- - Use self.stdout.write() for output instead of print()
334
- """
@@ -1,289 +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
-
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 +0,0 @@
1
- """__init__ module for winipedia_utils.pyside6."""
@@ -1 +0,0 @@
1
- """__init__ module for winipedia_utils.pyside6.core."""