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.
- winidjango/__init__.py +11 -2
- winidjango/dev/configs/configs.py +14 -0
- winidjango/dev/tests/fixtures/__init__.py +1 -0
- winidjango/main.py +0 -10
- winidjango/resources/__init__.py +1 -0
- winidjango/src/__init__.py +13 -2
- winidjango/src/commands/__init__.py +8 -1
- winidjango/src/commands/base/__init__.py +7 -1
- winidjango/src/commands/base/base.py +57 -178
- winidjango/src/commands/import_data.py +64 -23
- winidjango/src/db/__init__.py +7 -1
- winidjango/src/db/bulk.py +130 -154
- winidjango/src/db/fields.py +20 -56
- winidjango/src/db/models.py +33 -20
- winidjango/src/db/sql.py +22 -40
- winidjango-2.0.31.dist-info/METADATA +255 -0
- winidjango-2.0.31.dist-info/RECORD +27 -0
- winidjango-2.0.31.dist-info/WHEEL +4 -0
- winidjango-2.0.31.dist-info/entry_points.txt +3 -0
- winidjango/dev/artifacts/builder/builder.py +0 -4
- winidjango-1.0.4.dist-info/METADATA +0 -309
- winidjango-1.0.4.dist-info/RECORD +0 -26
- winidjango-1.0.4.dist-info/WHEEL +0 -4
- winidjango-1.0.4.dist-info/entry_points.txt +0 -3
- /winidjango/dev/{artifacts → builders}/__init__.py +0 -0
- /winidjango/dev/{artifacts/builder → tests}/__init__.py +0 -0
- {winidjango-1.0.4.dist-info → winidjango-2.0.31.dist-info}/licenses/LICENSE +0 -0
winidjango/__init__.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""__init__ module."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
+
from pathlib import Path
|
|
4
5
|
|
|
5
6
|
import django
|
|
6
7
|
import django_stubs_ext
|
|
@@ -11,8 +12,14 @@ logger = logging.getLogger(__name__)
|
|
|
11
12
|
django_stubs_ext.monkeypatch()
|
|
12
13
|
logger.info("Monkeypatched django-stubs")
|
|
13
14
|
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Configure Django settings for tests if not already configured
|
|
14
20
|
if not settings.configured:
|
|
15
|
-
logger.info("Configuring minimal django settings")
|
|
21
|
+
logger.info("Configuring minimal django settings for tests")
|
|
22
|
+
installed_apps = ["tests"] if Path("tests").exists() else []
|
|
16
23
|
settings.configure(
|
|
17
24
|
DATABASES={
|
|
18
25
|
"default": {
|
|
@@ -20,6 +27,8 @@ if not settings.configured:
|
|
|
20
27
|
"NAME": ":memory:",
|
|
21
28
|
}
|
|
22
29
|
},
|
|
23
|
-
INSTALLED_APPS=
|
|
30
|
+
INSTALLED_APPS=installed_apps,
|
|
31
|
+
USE_TZ=True,
|
|
24
32
|
)
|
|
25
33
|
django.setup()
|
|
34
|
+
logger.info("Django setup complete")
|
|
@@ -2,3 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
All subclasses of ConfigFile in the configs package are automatically called.
|
|
4
4
|
"""
|
|
5
|
+
|
|
6
|
+
from pyrig.dev.configs.pyproject import PyprojectConfigFile as PyrigPyprojectConfigFile
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PyprojectConfigFile(PyrigPyprojectConfigFile):
|
|
10
|
+
"""Pyproject.toml config file."""
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
def get_standard_dev_dependencies(cls) -> list[str]:
|
|
14
|
+
"""Get the standard dev dependencies."""
|
|
15
|
+
dev_dependencies = super().get_standard_dev_dependencies()
|
|
16
|
+
dev_dependencies.extend(["django-stubs", "pytest-django"])
|
|
17
|
+
|
|
18
|
+
return sorted(dev_dependencies)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""__init__ module."""
|
winidjango/main.py
CHANGED
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
"""Main entrypoint for the project."""
|
|
2
2
|
|
|
3
|
-
import pyrig
|
|
4
|
-
|
|
5
3
|
|
|
6
4
|
def main() -> None:
|
|
7
5
|
"""Main entrypoint for the project."""
|
|
8
|
-
msg = f"""Add your projects entrypoint code to this function.
|
|
9
|
-
This function is automatically added to your cli by {pyrig.__name__}.
|
|
10
|
-
You can call it with
|
|
11
|
-
`poetry run your-pkg-name main`
|
|
12
|
-
or via
|
|
13
|
-
`python -m your-pkg-name`.
|
|
14
|
-
"""
|
|
15
|
-
raise NotImplementedError(msg)
|
|
16
6
|
|
|
17
7
|
|
|
18
8
|
if __name__ == "__main__":
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""__init__ module."""
|
winidjango/src/__init__.py
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
-
"""src package.
|
|
1
|
+
"""src package.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This package exposes the project's internal modules used by the
|
|
4
|
+
command-line utilities and database helpers. It exists primarily so
|
|
5
|
+
that code under `winidjango/src` can be imported using the
|
|
6
|
+
`winidjango.src` package path in other modules and tests.
|
|
7
|
+
|
|
8
|
+
The package itself contains the following subpackages:
|
|
9
|
+
- `commands` - management command helpers and base classes
|
|
10
|
+
- `db` - database utilities and model helpers
|
|
11
|
+
|
|
12
|
+
Consumers should import the specific submodules they need rather than
|
|
13
|
+
relying on side effects from this package's import-time execution.
|
|
14
|
+
"""
|
|
@@ -1 +1,8 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Utilities and base classes for management commands.
|
|
2
|
+
|
|
3
|
+
The `commands` package contains base command classes and helpers used to
|
|
4
|
+
implement Django management commands in the project. Subpackages and
|
|
5
|
+
modules under `commands` provide reusable patterns for argument handling,
|
|
6
|
+
logging and common command behaviors so that individual commands can focus
|
|
7
|
+
on their business logic.
|
|
8
|
+
"""
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Base helpers and abstractions for management commands.
|
|
2
|
+
|
|
3
|
+
This package provides a common abstract base class used by project
|
|
4
|
+
management commands. The base class centralizes argument handling,
|
|
5
|
+
standard options (dry-run, batching, timeouts, etc.) and integrates
|
|
6
|
+
logging behavior used throughout the commands package.
|
|
7
|
+
"""
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
This module
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
"""Utilities and an abstract base class for Django management commands.
|
|
2
|
+
|
|
3
|
+
This module defines :class:`ABCBaseCommand`, a reusable abstract base
|
|
4
|
+
that combines Django's ``BaseCommand`` with the project's logging
|
|
5
|
+
mixins and standard argument handling. The base class implements a
|
|
6
|
+
template method pattern so concrete commands only need to implement the
|
|
7
|
+
abstract extension points for providing command-specific arguments and
|
|
8
|
+
business logic.
|
|
6
9
|
"""
|
|
7
10
|
|
|
8
11
|
import logging
|
|
@@ -17,41 +20,19 @@ logger = logging.getLogger(__name__)
|
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
class ABCBaseCommand(ABCLoggingMixin, BaseCommand):
|
|
20
|
-
"""Abstract base class for
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
- Compile-time validation that all abstract methods are implemented
|
|
34
|
-
- Structured argument handling with base and custom arguments
|
|
35
|
-
- Template method pattern for consistent command execution flow
|
|
36
|
-
|
|
37
|
-
Inheritance Order:
|
|
38
|
-
The order of inheritance is critical: ABCImplementationLoggingMixin must
|
|
39
|
-
come before BaseCommand because Django's BaseCommand doesn't call
|
|
40
|
-
super().__init__(), so the mixin's metaclass initialization must happen
|
|
41
|
-
first to ensure proper class construction.
|
|
42
|
-
|
|
43
|
-
Example:
|
|
44
|
-
>>> class MyCommand(ABCBaseCommand):
|
|
45
|
-
... def add_command_arguments(self, parser):
|
|
46
|
-
... parser.add_argument('--my-option', help='Custom option')
|
|
47
|
-
...
|
|
48
|
-
... def handle_command(self, *args, **options):
|
|
49
|
-
... self.stdout.write('Executing my command')
|
|
50
|
-
|
|
51
|
-
Note:
|
|
52
|
-
- All methods are automatically logged with performance tracking
|
|
53
|
-
- Subclasses must implement add_command_arguments and handle_command
|
|
54
|
-
- The @final decorator prevents overriding of template methods
|
|
23
|
+
"""Abstract base class for management commands with logging and standard options.
|
|
24
|
+
|
|
25
|
+
The class wires common behavior such as base arguments (dry-run,
|
|
26
|
+
batching, timeouts) and provides extension points that concrete
|
|
27
|
+
commands must implement: :meth:`add_command_arguments` and
|
|
28
|
+
:meth:`handle_command`.
|
|
29
|
+
|
|
30
|
+
Notes:
|
|
31
|
+
- Inheritance order matters: the logging mixin must precede
|
|
32
|
+
``BaseCommand`` so mixin initialization occurs as expected.
|
|
33
|
+
- The base class follows the template method pattern; concrete
|
|
34
|
+
commands should not override :meth:`add_arguments` or
|
|
35
|
+
:meth:`handle` but implement the abstract hooks instead.
|
|
55
36
|
"""
|
|
56
37
|
|
|
57
38
|
class Options:
|
|
@@ -67,25 +48,14 @@ class ABCBaseCommand(ABCLoggingMixin, BaseCommand):
|
|
|
67
48
|
PROCESSES = "processes"
|
|
68
49
|
|
|
69
50
|
def add_arguments(self, parser: ArgumentParser) -> None:
|
|
70
|
-
"""Configure command-line arguments for the
|
|
71
|
-
|
|
72
|
-
This method implements the template method pattern by first adding common
|
|
73
|
-
base arguments that are used across multiple commands, then delegating
|
|
74
|
-
to the abstract add_command_arguments method for command-specific arguments.
|
|
51
|
+
"""Configure command-line arguments for the command.
|
|
75
52
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
53
|
+
Adds common base arguments (dry-run, force, delete, timeout,
|
|
54
|
+
batching and concurrency options) and then delegates to
|
|
55
|
+
:meth:`add_command_arguments` for command-specific options.
|
|
79
56
|
|
|
80
57
|
Args:
|
|
81
|
-
parser (ArgumentParser):
|
|
82
|
-
define command-line options and arguments for the command.
|
|
83
|
-
|
|
84
|
-
Note:
|
|
85
|
-
- This method is final and cannot be overridden by subclasses
|
|
86
|
-
- Common arguments are added first via _add_arguments()
|
|
87
|
-
- Custom arguments are added via the abstract add_command_arguments()
|
|
88
|
-
- Subclasses must implement add_command_arguments() for specific needs
|
|
58
|
+
parser (ArgumentParser): The argument parser passed by Django.
|
|
89
59
|
"""
|
|
90
60
|
# add base args that are used in most commands
|
|
91
61
|
self.base_add_arguments(parser)
|
|
@@ -94,24 +64,10 @@ class ABCBaseCommand(ABCLoggingMixin, BaseCommand):
|
|
|
94
64
|
self.add_command_arguments(parser)
|
|
95
65
|
|
|
96
66
|
def base_add_arguments(self, parser: ArgumentParser) -> None:
|
|
97
|
-
"""Add
|
|
98
|
-
|
|
99
|
-
This method defines base arguments that are commonly used across different
|
|
100
|
-
Django management commands. These arguments provide standard functionality
|
|
101
|
-
like dry-run mode, verbosity control, and batch processing options.
|
|
102
|
-
|
|
103
|
-
The method is final to ensure consistent base argument handling, while
|
|
104
|
-
command-specific arguments are handled through the abstract
|
|
105
|
-
add_command_arguments method.
|
|
67
|
+
"""Add the project's standard command-line arguments to ``parser``.
|
|
106
68
|
|
|
107
69
|
Args:
|
|
108
|
-
parser (ArgumentParser):
|
|
109
|
-
common arguments should be added.
|
|
110
|
-
|
|
111
|
-
Note:
|
|
112
|
-
- Provides standard arguments for dry-run, verbosity, and batch processing
|
|
113
|
-
- The @final decorator prevents subclasses from overriding this method
|
|
114
|
-
- Command-specific arguments should be added via add_command_arguments()
|
|
70
|
+
parser (ArgumentParser): The argument parser passed by Django.
|
|
115
71
|
"""
|
|
116
72
|
parser.add_argument(
|
|
117
73
|
f"--{self.Options.DRY_RUN}",
|
|
@@ -168,138 +124,61 @@ class ABCBaseCommand(ABCLoggingMixin, BaseCommand):
|
|
|
168
124
|
|
|
169
125
|
@abstractmethod
|
|
170
126
|
def add_command_arguments(self, parser: ArgumentParser) -> None:
|
|
171
|
-
"""
|
|
172
|
-
|
|
173
|
-
This abstract method must be implemented by subclasses to define
|
|
174
|
-
command-specific command-line arguments. It is called after common
|
|
175
|
-
base arguments are added, allowing each command to customize its
|
|
176
|
-
argument interface while maintaining consistent base functionality.
|
|
127
|
+
"""Define command-specific arguments.
|
|
177
128
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
or operational flags.
|
|
129
|
+
Implement this hook to add options and positional arguments that are
|
|
130
|
+
specific to the concrete management command.
|
|
181
131
|
|
|
182
132
|
Args:
|
|
183
|
-
parser (ArgumentParser):
|
|
184
|
-
command-specific arguments should be added.
|
|
185
|
-
|
|
186
|
-
Example:
|
|
187
|
-
>>> def add_command_arguments(self, parser):
|
|
188
|
-
... parser.add_argument(
|
|
189
|
-
... '--input-file',
|
|
190
|
-
... type=str,
|
|
191
|
-
... required=True,
|
|
192
|
-
... help='Path to input file'
|
|
193
|
-
... )
|
|
194
|
-
... parser.add_argument(
|
|
195
|
-
... '--output-format',
|
|
196
|
-
... choices=['json', 'csv', 'xml'],
|
|
197
|
-
... default='json',
|
|
198
|
-
... help='Output format for results'
|
|
199
|
-
... )
|
|
200
|
-
|
|
201
|
-
Note:
|
|
202
|
-
- This method is abstract and must be implemented by subclasses
|
|
203
|
-
- Called after _add_arguments() adds common base arguments
|
|
204
|
-
- Should focus on command-specific functionality only
|
|
133
|
+
parser (ArgumentParser): The argument parser passed by Django.
|
|
205
134
|
"""
|
|
206
135
|
|
|
207
136
|
def handle(self, *args: Any, **options: Any) -> None:
|
|
208
|
-
"""
|
|
209
|
-
|
|
210
|
-
This method implements the main execution flow for the command by first
|
|
211
|
-
calling common handling logic through _handle(), then delegating to
|
|
212
|
-
the command-specific implementation via handle_command().
|
|
137
|
+
"""Orchestrate command execution.
|
|
213
138
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
139
|
+
Performs shared pre-processing by calling :meth:`base_handle` and
|
|
140
|
+
then delegates to :meth:`handle_command` which must be implemented
|
|
141
|
+
by subclasses.
|
|
217
142
|
|
|
218
143
|
Args:
|
|
219
|
-
*args: Positional arguments
|
|
220
|
-
**options:
|
|
221
|
-
and their values as defined by add_arguments().
|
|
222
|
-
|
|
223
|
-
Note:
|
|
224
|
-
- This method is final and cannot be overridden by subclasses
|
|
225
|
-
- Common handling logic is executed first via _handle()
|
|
226
|
-
- Command-specific logic is executed via abstract handle_command()
|
|
227
|
-
- All method calls are automatically logged with performance tracking
|
|
144
|
+
*args: Positional arguments forwarded from Django.
|
|
145
|
+
**options: Parsed command-line options.
|
|
228
146
|
"""
|
|
229
147
|
self.base_handle(*args, **options)
|
|
230
148
|
self.handle_command()
|
|
231
149
|
|
|
232
150
|
def base_handle(self, *args: Any, **options: Any) -> None:
|
|
233
|
-
"""
|
|
151
|
+
"""Perform common pre-processing for commands.
|
|
234
152
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
a placeholder for future common functionality such as logging setup,
|
|
238
|
-
validation, or shared initialization.
|
|
239
|
-
|
|
240
|
-
The method is final to ensure consistent common handling across all
|
|
241
|
-
commands, while command-specific logic is handled through the abstract
|
|
242
|
-
handle_command method.
|
|
153
|
+
Stores the incoming arguments and options on the instance for use
|
|
154
|
+
by :meth:`handle_command` and subclasses.
|
|
243
155
|
|
|
244
156
|
Args:
|
|
245
|
-
*args: Positional arguments
|
|
246
|
-
|
|
247
|
-
**options: Keyword arguments containing parsed command-line options.
|
|
248
|
-
Currently unused but reserved for future common processing.
|
|
249
|
-
|
|
250
|
-
Note:
|
|
251
|
-
- Examples might include logging setup, database connection validation, etc.
|
|
252
|
-
- The @final decorator prevents subclasses from overriding this method
|
|
253
|
-
- Called before handle_command() in the template method pattern
|
|
157
|
+
*args: Positional arguments forwarded from Django.
|
|
158
|
+
**options: Parsed command-line options.
|
|
254
159
|
"""
|
|
255
160
|
self.args = args
|
|
256
161
|
self.options = options
|
|
257
162
|
|
|
258
163
|
@abstractmethod
|
|
259
164
|
def handle_command(self) -> None:
|
|
260
|
-
"""
|
|
165
|
+
"""Run the command-specific behavior.
|
|
261
166
|
|
|
262
|
-
This abstract
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
167
|
+
This abstract hook should be implemented by concrete commands to
|
|
168
|
+
perform the command's main work. Implementations should read
|
|
169
|
+
``self.args`` and ``self.options`` which were set in
|
|
170
|
+
:meth:`base_handle`.
|
|
171
|
+
"""
|
|
266
172
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
or any other command-specific tasks.
|
|
173
|
+
def get_option(self, option: str) -> Any:
|
|
174
|
+
"""Retrieve a parsed command option by key.
|
|
270
175
|
|
|
271
176
|
Args:
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
Example:
|
|
275
|
-
>>> def handle_command(self):
|
|
276
|
-
... args, options = self.args, self.options
|
|
277
|
-
... input_file = options['input_file']
|
|
278
|
-
... dry_run = options['dry_run'] # Base argument
|
|
279
|
-
... batch_size = options['batch_size'] # Base argument
|
|
280
|
-
... quiet = options['quiet'] # Base argument
|
|
281
|
-
...
|
|
282
|
-
... if dry_run:
|
|
283
|
-
... self.stdout.write('Dry run mode - no changes will be made')
|
|
284
|
-
...
|
|
285
|
-
... if not quiet:
|
|
286
|
-
... msg = f'Processing {input_file} in batches of {batch_size}'
|
|
287
|
-
... self.stdout.write(msg)
|
|
288
|
-
...
|
|
289
|
-
... # Perform command-specific operations
|
|
290
|
-
... self.process_file(input_file, batch_size, dry_run)
|
|
291
|
-
...
|
|
292
|
-
... if not quiet:
|
|
293
|
-
... self.stdout.write('Command completed successfully')
|
|
294
|
-
|
|
295
|
-
Note:
|
|
296
|
-
- This method is abstract and must be implemented by subclasses
|
|
297
|
-
- Called after _handle() executes common logic
|
|
298
|
-
- Should contain the main functionality of the command
|
|
299
|
-
- All method calls are automatically logged with performance tracking
|
|
300
|
-
- Use self.stdout.write() for output instead of print()
|
|
301
|
-
"""
|
|
177
|
+
option (str): The option key to retrieve from ``self.options``.
|
|
302
178
|
|
|
303
|
-
|
|
304
|
-
|
|
179
|
+
Returns:
|
|
180
|
+
Any: The value for the requested option. If the option is not
|
|
181
|
+
present a ``KeyError`` will be raised (matching how Django
|
|
182
|
+
exposes options in management commands).
|
|
183
|
+
"""
|
|
305
184
|
return self.options[option]
|
|
@@ -1,6 +1,20 @@
|
|
|
1
|
-
"""Import command
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"""Import command base class and utilities.
|
|
2
|
+
|
|
3
|
+
This module defines a reusable base command for importing tabular data
|
|
4
|
+
into Django models. Implementations should provide a concrete source
|
|
5
|
+
ingestion (for example, reading from CSV or an external API), a
|
|
6
|
+
cleaning/normalization step implemented by a `CleaningDF` subclass, and
|
|
7
|
+
mapping logic that groups cleaned data into model instances that can be
|
|
8
|
+
bulk-created.
|
|
9
|
+
|
|
10
|
+
The base command centralizes the typical flow:
|
|
11
|
+
1. Read raw data (``handle_import``)
|
|
12
|
+
2. Wrap and clean the data using a `CleaningDF` subclass
|
|
13
|
+
3. Convert the cleaned frame into per-model bulks
|
|
14
|
+
4. Persist bulks using the project's bulk create helpers
|
|
15
|
+
|
|
16
|
+
Using this base class ensures a consistent import lifecycle and
|
|
17
|
+
reduces duplicated boilerplate across different import implementations.
|
|
4
18
|
"""
|
|
5
19
|
|
|
6
20
|
import logging
|
|
@@ -18,49 +32,72 @@ logger = logging.getLogger(__name__)
|
|
|
18
32
|
|
|
19
33
|
|
|
20
34
|
class ImportDataBaseCommand(ABCBaseCommand):
|
|
21
|
-
"""
|
|
35
|
+
"""Abstract base for data-import Django management commands.
|
|
36
|
+
|
|
37
|
+
Subclasses must implement the ingestion, cleaning-class selection,
|
|
38
|
+
and mapping of cleaned rows to Django model instances. The base
|
|
39
|
+
implementation wires these pieces together and calls the project's
|
|
40
|
+
bulk creation helper to persist the data.
|
|
22
41
|
|
|
23
|
-
|
|
24
|
-
|
|
42
|
+
Implementors typically only need to override the three abstract
|
|
43
|
+
methods documented below.
|
|
25
44
|
"""
|
|
26
45
|
|
|
27
46
|
@abstractmethod
|
|
28
47
|
def handle_import(self) -> pl.DataFrame:
|
|
29
|
-
"""
|
|
48
|
+
"""Read raw data from the import source.
|
|
30
49
|
|
|
31
|
-
|
|
32
|
-
|
|
50
|
+
This method should read data from whatever source the concrete
|
|
51
|
+
command targets (files, remote APIs, etc.) and return it as a
|
|
52
|
+
``polars.DataFrame``. No cleaning should be performed here;
|
|
53
|
+
cleaning is handled by the cleaning `CleaningDF` returned from
|
|
54
|
+
``get_cleaning_df_cls``.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
pl.DataFrame: Raw (uncleaned) tabular data to be cleaned and
|
|
58
|
+
mapped to model instances.
|
|
33
59
|
"""
|
|
34
60
|
|
|
35
61
|
@abstractmethod
|
|
36
62
|
def get_cleaning_df_cls(self) -> type[CleaningDF]:
|
|
37
|
-
"""
|
|
63
|
+
"""Return the `CleaningDF` subclass used to normalize the data.
|
|
64
|
+
|
|
65
|
+
The returned class will be instantiated with the raw DataFrame
|
|
66
|
+
returned from :meth:`handle_import` and must provide the
|
|
67
|
+
transformations required to prepare data for mapping into model
|
|
68
|
+
instances.
|
|
38
69
|
|
|
39
|
-
|
|
40
|
-
|
|
70
|
+
Returns:
|
|
71
|
+
type[CleaningDF]: A subclass of ``CleaningDF`` that performs
|
|
72
|
+
the necessary normalization and validation.
|
|
41
73
|
"""
|
|
42
74
|
|
|
43
75
|
@abstractmethod
|
|
44
76
|
def get_bulks_by_model(
|
|
45
77
|
self, df: pl.DataFrame
|
|
46
78
|
) -> dict[type[Model], Iterable[Model]]:
|
|
47
|
-
"""
|
|
79
|
+
"""Map the cleaned DataFrame to model-instance bulks.
|
|
48
80
|
|
|
49
|
-
The
|
|
50
|
-
|
|
51
|
-
|
|
81
|
+
The implementation should inspect the cleaned DataFrame and
|
|
82
|
+
return a mapping where keys are Django model classes and values
|
|
83
|
+
are iterables of unsaved model instances (or dataclass-like
|
|
84
|
+
objects accepted by the project's bulk-creation utility).
|
|
52
85
|
|
|
53
86
|
Args:
|
|
54
|
-
df (pl.DataFrame): The cleaned
|
|
55
|
-
|
|
87
|
+
df (pl.DataFrame): The cleaned and normalized DataFrame.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
dict[type[Model], Iterable[Model]]: Mapping from model classes
|
|
91
|
+
to iterables of instances that should be created.
|
|
56
92
|
"""
|
|
57
93
|
|
|
58
94
|
def handle_command(self) -> None:
|
|
59
|
-
"""Execute the import
|
|
95
|
+
"""Execute the full import lifecycle.
|
|
60
96
|
|
|
61
|
-
This method
|
|
62
|
-
|
|
63
|
-
and then
|
|
97
|
+
This template method reads raw data via :meth:`handle_import`,
|
|
98
|
+
wraps it with the cleaning class returned by
|
|
99
|
+
:meth:`get_cleaning_df_cls` and then persists the resulting
|
|
100
|
+
model bulks returned by :meth:`get_bulks_by_model`.
|
|
64
101
|
"""
|
|
65
102
|
data_df = self.handle_import()
|
|
66
103
|
|
|
@@ -70,7 +107,11 @@ class ImportDataBaseCommand(ABCBaseCommand):
|
|
|
70
107
|
self.import_to_db()
|
|
71
108
|
|
|
72
109
|
def import_to_db(self) -> None:
|
|
73
|
-
"""
|
|
110
|
+
"""Persist prepared model bulks to the database.
|
|
111
|
+
|
|
112
|
+
Calls the project's `bulk_create_bulks_in_steps` helper with the
|
|
113
|
+
mapping returned from :meth:`get_bulks_by_model`.
|
|
114
|
+
"""
|
|
74
115
|
bulks_by_model = self.get_bulks_by_model(df=self.cleaning_df.df)
|
|
75
116
|
|
|
76
117
|
bulk_create_bulks_in_steps(bulks_by_model)
|
winidjango/src/db/__init__.py
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Database utilities and common model helpers used in the project.
|
|
2
|
+
|
|
3
|
+
This package contains helpers for working with Django models, such as
|
|
4
|
+
topological sorting of model classes according to foreign-key
|
|
5
|
+
dependencies, a lightweight model hashing helper, and a project-wide
|
|
6
|
+
``BaseModel`` that adds common timestamp fields.
|
|
7
|
+
"""
|