django-model-seeder 2.6.1__tar.gz
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.
- django_model_seeder-2.6.1/LICENSE +21 -0
- django_model_seeder-2.6.1/MANIFEST.in +2 -0
- django_model_seeder-2.6.1/PKG-INFO +44 -0
- django_model_seeder-2.6.1/README.md +19 -0
- django_model_seeder-2.6.1/django_model_seeder.egg-info/PKG-INFO +44 -0
- django_model_seeder-2.6.1/django_model_seeder.egg-info/SOURCES.txt +18 -0
- django_model_seeder-2.6.1/django_model_seeder.egg-info/dependency_links.txt +1 -0
- django_model_seeder-2.6.1/django_model_seeder.egg-info/requires.txt +7 -0
- django_model_seeder-2.6.1/django_model_seeder.egg-info/top_level.txt +1 -0
- django_model_seeder-2.6.1/django_seeder/__init__.py +19 -0
- django_model_seeder-2.6.1/django_seeder/config_parser.py +81 -0
- django_model_seeder-2.6.1/django_seeder/loaders.py +84 -0
- django_model_seeder-2.6.1/django_seeder/management/__init__.py +0 -0
- django_model_seeder-2.6.1/django_seeder/management/commands/__init__.py +0 -0
- django_model_seeder-2.6.1/django_seeder/management/commands/clear_seeds.py +76 -0
- django_model_seeder-2.6.1/django_seeder/management/commands/list_seeds.py +61 -0
- django_model_seeder-2.6.1/django_seeder/management/commands/seed_models.py +196 -0
- django_model_seeder-2.6.1/django_seeder/seeder.py +113 -0
- django_model_seeder-2.6.1/pyproject.toml +40 -0
- django_model_seeder-2.6.1/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Christian Garcia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: django-model-seeder
|
|
3
|
+
Version: 2.6.1
|
|
4
|
+
Summary: A Django library powered by Data Seed PH for generating realistic, synthetic Philippine-based datasets directly into Django models.
|
|
5
|
+
Author-email: "Christian G. Garcia" <iyaniyan03112003@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/christiangarcia0311/django-model-seeder
|
|
8
|
+
Project-URL: Repository, https://github.com/christiangarcia0311/django-model-seeder.git
|
|
9
|
+
Keywords: synthetic-data,database-seeding,testing
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: django
|
|
19
|
+
Requires-Dist: data-seed-ph
|
|
20
|
+
Requires-Dist: pyyaml
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-django>=4.0; extra == "dev"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
## Django Model Seeder
|
|
27
|
+
|
|
28
|
+
[](https://github.com/christiangarcia0311/django-model-seeder/stargazers)
|
|
29
|
+
[](https://github.com/christiangarcia0311/django-model-seeder/issues)
|
|
30
|
+

|
|
31
|
+

|
|
32
|
+

|
|
33
|
+

|
|
34
|
+

|
|
35
|
+
[](https://github.com/christiangarcia0311/django-model-seeder/commits/main)
|
|
36
|
+
[](https://github.com/christiangarcia0311/django-model-seeder/releases/latest)
|
|
37
|
+
|
|
38
|
+
**Command-based synthetic data generation and seeding for Django models using [data-seed-ph](https://github.com/christiangarcia0311/data-seed-ph).**
|
|
39
|
+
|
|
40
|
+
>[!NOTE]
|
|
41
|
+
> Designed for rapid database population in development environments, API testing, QA automation, and demo applications with seamless integration to Django's management command system.
|
|
42
|
+
|
|
43
|
+
View full documentation: [Github](https://github.com/christiangarcia0311/django-model-seeder)
|
|
44
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
## Django Model Seeder
|
|
2
|
+
|
|
3
|
+
[](https://github.com/christiangarcia0311/django-model-seeder/stargazers)
|
|
4
|
+
[](https://github.com/christiangarcia0311/django-model-seeder/issues)
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+

|
|
9
|
+

|
|
10
|
+
[](https://github.com/christiangarcia0311/django-model-seeder/commits/main)
|
|
11
|
+
[](https://github.com/christiangarcia0311/django-model-seeder/releases/latest)
|
|
12
|
+
|
|
13
|
+
**Command-based synthetic data generation and seeding for Django models using [data-seed-ph](https://github.com/christiangarcia0311/data-seed-ph).**
|
|
14
|
+
|
|
15
|
+
>[!NOTE]
|
|
16
|
+
> Designed for rapid database population in development environments, API testing, QA automation, and demo applications with seamless integration to Django's management command system.
|
|
17
|
+
|
|
18
|
+
View full documentation: [Github](https://github.com/christiangarcia0311/django-model-seeder)
|
|
19
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: django-model-seeder
|
|
3
|
+
Version: 2.6.1
|
|
4
|
+
Summary: A Django library powered by Data Seed PH for generating realistic, synthetic Philippine-based datasets directly into Django models.
|
|
5
|
+
Author-email: "Christian G. Garcia" <iyaniyan03112003@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/christiangarcia0311/django-model-seeder
|
|
8
|
+
Project-URL: Repository, https://github.com/christiangarcia0311/django-model-seeder.git
|
|
9
|
+
Keywords: synthetic-data,database-seeding,testing
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: django
|
|
19
|
+
Requires-Dist: data-seed-ph
|
|
20
|
+
Requires-Dist: pyyaml
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-django>=4.0; extra == "dev"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
## Django Model Seeder
|
|
27
|
+
|
|
28
|
+
[](https://github.com/christiangarcia0311/django-model-seeder/stargazers)
|
|
29
|
+
[](https://github.com/christiangarcia0311/django-model-seeder/issues)
|
|
30
|
+

|
|
31
|
+

|
|
32
|
+

|
|
33
|
+

|
|
34
|
+

|
|
35
|
+
[](https://github.com/christiangarcia0311/django-model-seeder/commits/main)
|
|
36
|
+
[](https://github.com/christiangarcia0311/django-model-seeder/releases/latest)
|
|
37
|
+
|
|
38
|
+
**Command-based synthetic data generation and seeding for Django models using [data-seed-ph](https://github.com/christiangarcia0311/data-seed-ph).**
|
|
39
|
+
|
|
40
|
+
>[!NOTE]
|
|
41
|
+
> Designed for rapid database population in development environments, API testing, QA automation, and demo applications with seamless integration to Django's management command system.
|
|
42
|
+
|
|
43
|
+
View full documentation: [Github](https://github.com/christiangarcia0311/django-model-seeder)
|
|
44
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
pyproject.toml
|
|
5
|
+
django_model_seeder.egg-info/PKG-INFO
|
|
6
|
+
django_model_seeder.egg-info/SOURCES.txt
|
|
7
|
+
django_model_seeder.egg-info/dependency_links.txt
|
|
8
|
+
django_model_seeder.egg-info/requires.txt
|
|
9
|
+
django_model_seeder.egg-info/top_level.txt
|
|
10
|
+
django_seeder/__init__.py
|
|
11
|
+
django_seeder/config_parser.py
|
|
12
|
+
django_seeder/loaders.py
|
|
13
|
+
django_seeder/seeder.py
|
|
14
|
+
django_seeder/management/__init__.py
|
|
15
|
+
django_seeder/management/commands/__init__.py
|
|
16
|
+
django_seeder/management/commands/clear_seeds.py
|
|
17
|
+
django_seeder/management/commands/list_seeds.py
|
|
18
|
+
django_seeder/management/commands/seed_models.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
django_seeder
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Django Model Seeder
|
|
3
|
+
-------------------
|
|
4
|
+
|
|
5
|
+
A Django library powered by Data Seed PH for generating realistic,
|
|
6
|
+
synthetic Philippine-based datasets directly into Django models
|
|
7
|
+
|
|
8
|
+
Information
|
|
9
|
+
-----------
|
|
10
|
+
@author: Christian G. Garcia
|
|
11
|
+
@github: github.com/christiangarcia0311/django-model-seeder
|
|
12
|
+
'''
|
|
13
|
+
|
|
14
|
+
from .seeder import ModelSeeder
|
|
15
|
+
from .loaders import BulkModelLoader
|
|
16
|
+
from .config_parser import ConfigParser
|
|
17
|
+
|
|
18
|
+
__version__ = '3.1.0'
|
|
19
|
+
__all__ = ['ModelSeeder', 'BulkModelLoader', 'ConfigParser']
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Config Parser
|
|
3
|
+
|
|
4
|
+
This module provides utilities for loading, validating, and merging configuration
|
|
5
|
+
files in JSON and YAML formats. It also includes preprocessing logic to convert
|
|
6
|
+
numeric range lists into tuples for easier downstream data handling.
|
|
7
|
+
|
|
8
|
+
Information
|
|
9
|
+
-----------
|
|
10
|
+
@author: Christian G. Garcia
|
|
11
|
+
@github: github.com/christiangarcia/django-model-seeder
|
|
12
|
+
'''
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import yaml
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Dict, Any
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ConfigParser:
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def load_json(filepath: str) -> Dict[str, Any]:
|
|
25
|
+
with open(filepath, 'r') as f:
|
|
26
|
+
data = json.load(f)
|
|
27
|
+
return ConfigParser._convert_to_tuples(data)
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def load_yaml(filepath: str) -> Dict[str, Any]:
|
|
31
|
+
with open(filepath, 'r') as f:
|
|
32
|
+
return yaml.safe_load(f)
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def _convert_to_tuples(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
36
|
+
for model_name, model_config in config.items():
|
|
37
|
+
if isinstance(model_config, dict) and 'mapping' in model_config:
|
|
38
|
+
mapping = model_config['mapping']
|
|
39
|
+
for field_name, value in mapping.items():
|
|
40
|
+
if isinstance(value, list) and len(value) >= 2:
|
|
41
|
+
if all(isinstance(v, (int, float)) or v == 'float' for v in value):
|
|
42
|
+
mapping[field_name] = tuple(value)
|
|
43
|
+
return config
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def load_config(filepath: str) -> Dict[str, Any]:
|
|
47
|
+
path = Path(filepath)
|
|
48
|
+
|
|
49
|
+
if path.suffix == '.json':
|
|
50
|
+
return ConfigParser.load_json(filepath)
|
|
51
|
+
elif path.suffix in ['.yaml', '.yml']:
|
|
52
|
+
return ConfigParser.load_yaml(filepath)
|
|
53
|
+
else:
|
|
54
|
+
raise ValueError(f"Unsupported config format: {path.suffix}")
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def validate_config(config: Dict[str, Any]) -> bool:
|
|
58
|
+
if not isinstance(config, dict):
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
for model_name, model_config in config.items():
|
|
62
|
+
if not isinstance(model_config, dict):
|
|
63
|
+
return False
|
|
64
|
+
if 'rows' not in model_config or 'mapping' not in model_config:
|
|
65
|
+
return False
|
|
66
|
+
if not isinstance(model_config['mapping'], dict):
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def merge_configs(base_config: Dict[str, Any], override_config: Dict[str, Any]) -> Dict[str, Any]:
|
|
73
|
+
merged = base_config.copy()
|
|
74
|
+
|
|
75
|
+
for model_name, model_config in override_config.items():
|
|
76
|
+
if model_name in merged:
|
|
77
|
+
merged[model_name].update(model_config)
|
|
78
|
+
else:
|
|
79
|
+
merged[model_name] = model_config
|
|
80
|
+
|
|
81
|
+
return merged
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Loaders module
|
|
3
|
+
|
|
4
|
+
Provides a bulk loader utility for registering Django models and
|
|
5
|
+
executing seed operations using ModelSeeder instances
|
|
6
|
+
|
|
7
|
+
Information
|
|
8
|
+
-----------
|
|
9
|
+
@author: Christian G. Garcia
|
|
10
|
+
@github: github.com/christiangarcia0311/django-model-seeder
|
|
11
|
+
'''
|
|
12
|
+
|
|
13
|
+
from django.apps import apps
|
|
14
|
+
from django.db.models import Model
|
|
15
|
+
from typing import Dict, List, Type, Any, Callable
|
|
16
|
+
from .seeder import ModelSeeder
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class BulkModelLoader:
|
|
20
|
+
|
|
21
|
+
def __init__(self):
|
|
22
|
+
self.seeders = {}
|
|
23
|
+
self.results = {}
|
|
24
|
+
|
|
25
|
+
def register(self, model: Type[Model])-> ModelSeeder:
|
|
26
|
+
seeder = ModelSeeder(model)
|
|
27
|
+
self.seeders[model.__name__] = seeder
|
|
28
|
+
return seeder
|
|
29
|
+
|
|
30
|
+
def load_model(self, app_label: str, model_name: str) -> ModelSeeder:
|
|
31
|
+
model = apps.get_model(app_label, model_name)
|
|
32
|
+
return self.register(model)
|
|
33
|
+
|
|
34
|
+
def seed_all(
|
|
35
|
+
self,
|
|
36
|
+
configurations: Dict[str, Dict[str, Any]],
|
|
37
|
+
relations_map: Dict[str, Dict[str, Callable]] = None
|
|
38
|
+
) -> Dict[str, List[Model]]:
|
|
39
|
+
|
|
40
|
+
relations_map = relations_map or {}
|
|
41
|
+
results = {}
|
|
42
|
+
|
|
43
|
+
for model_name, config in configurations.items():
|
|
44
|
+
if model_name in self.seeders:
|
|
45
|
+
seeder = self.seeders[model_name]
|
|
46
|
+
rows = config.get('rows', 10)
|
|
47
|
+
mapping = config.get('mapping', {})
|
|
48
|
+
relations = relations_map.get(model_name, {})
|
|
49
|
+
|
|
50
|
+
if relations:
|
|
51
|
+
results[model_name] = seeder.seed_with_relations(rows, mapping, relations)
|
|
52
|
+
else:
|
|
53
|
+
results[model_name] = seeder.seed(rows, mapping)
|
|
54
|
+
|
|
55
|
+
self.results[model_name] = results[model_name]
|
|
56
|
+
|
|
57
|
+
return results
|
|
58
|
+
|
|
59
|
+
def seed_model(self, model_name: str, rows: int, mapping: Dict[str, Any]) -> List[Model]:
|
|
60
|
+
if model_name in self.seeders:
|
|
61
|
+
result = self.seeders[model_name].seed(rows, mapping, relations_map=None)
|
|
62
|
+
self.results[model_name] = result
|
|
63
|
+
return result
|
|
64
|
+
return []
|
|
65
|
+
|
|
66
|
+
def get_results(self, model_name: str = None) -> Dict[str, List[Model]] | List[Model]:
|
|
67
|
+
if model_name:
|
|
68
|
+
return self.results.get(model_name, [])
|
|
69
|
+
return self.results
|
|
70
|
+
|
|
71
|
+
def clear(self, model_name: str = None):
|
|
72
|
+
if model_name and model_name in self.seeders:
|
|
73
|
+
self.seeders[model_name].model.objects.all().delete()
|
|
74
|
+
|
|
75
|
+
if model_name in self.results:
|
|
76
|
+
del self.results[model_name]
|
|
77
|
+
else:
|
|
78
|
+
for seeder in self.seeders.values():
|
|
79
|
+
seeder.model.objects.all().delete()
|
|
80
|
+
self.results.clear()
|
|
81
|
+
|
|
82
|
+
def list_models(self) -> List[str]:
|
|
83
|
+
return list(self.seeders.keys())
|
|
84
|
+
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Clear command
|
|
3
|
+
|
|
4
|
+
This management command provides utilities to delete (clear) seeded or existing
|
|
5
|
+
data from Django models. It supports clearing a specific model or all models
|
|
6
|
+
across all installed apps, with optional confirmation prompts for safety.
|
|
7
|
+
'''
|
|
8
|
+
|
|
9
|
+
from django.core.management.base import BaseCommand, CommandError
|
|
10
|
+
from django.apps import apps
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Command(BaseCommand):
|
|
14
|
+
help = 'Clear seeded data from Django models'
|
|
15
|
+
|
|
16
|
+
def add_arguments(self, parser):
|
|
17
|
+
parser.add_argument(
|
|
18
|
+
'--model',
|
|
19
|
+
type=str,
|
|
20
|
+
help='Specific model to clear (format: ModelName)'
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
parser.add_argument(
|
|
24
|
+
'--app',
|
|
25
|
+
type=str,
|
|
26
|
+
help='Django app label'
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
parser.add_argument(
|
|
30
|
+
'--all',
|
|
31
|
+
action='store_true',
|
|
32
|
+
help='Clear all data from all models (dangerous)'
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
parser.add_argument(
|
|
36
|
+
'--confirm',
|
|
37
|
+
action='store_true',
|
|
38
|
+
help='Skip confirmation prompt'
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def handle(self, *args, **options):
|
|
42
|
+
if options['all']:
|
|
43
|
+
if not options['confirm']:
|
|
44
|
+
confirm = input('Clear ALL data from ALL models? (yes/no): ')
|
|
45
|
+
if confirm.lower() != 'yes':
|
|
46
|
+
self.stdout.write('Aborted')
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
count = 0
|
|
50
|
+
for app_config in apps.get_app_configs():
|
|
51
|
+
for model in app_config.get_models():
|
|
52
|
+
deleted, _ = model.objects.all().delete()
|
|
53
|
+
count += deleted
|
|
54
|
+
self.stdout.write(f'Cleared {model.__name__}')
|
|
55
|
+
|
|
56
|
+
self.stdout.write(self.style.SUCCESS(f'Total records deleted: {count}'))
|
|
57
|
+
|
|
58
|
+
elif options['model'] and options['app']:
|
|
59
|
+
try:
|
|
60
|
+
model = apps.get_model(options['app'], options['model'])
|
|
61
|
+
count = model.objects.count()
|
|
62
|
+
|
|
63
|
+
if not options['confirm']:
|
|
64
|
+
confirm = input(f'Clear {count} records from {options["model"]}? (yes/no): ')
|
|
65
|
+
if confirm.lower() != 'yes':
|
|
66
|
+
self.stdout.write('Aborted')
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
model.objects.all().delete()
|
|
70
|
+
self.stdout.write(self.style.SUCCESS(f'Cleared {options["model"]}'))
|
|
71
|
+
except LookupError:
|
|
72
|
+
raise CommandError(f'Model not found: {options["app"]}.{options["model"]}')
|
|
73
|
+
else:
|
|
74
|
+
self.stdout.write(
|
|
75
|
+
self.style.ERROR('Either --all or both --model and --app must be provided')
|
|
76
|
+
)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'''
|
|
2
|
+
List command
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
This management command lists all available Django models that can be used
|
|
6
|
+
for seeding or data operations. It supports filtering by app and optionally
|
|
7
|
+
displaying record counts for each model.
|
|
8
|
+
'''
|
|
9
|
+
|
|
10
|
+
from django.core.management.base import BaseCommand, CommandError
|
|
11
|
+
from django.apps import apps
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Command(BaseCommand):
|
|
15
|
+
|
|
16
|
+
help = 'List all available Django models for seeding'
|
|
17
|
+
|
|
18
|
+
def add_arguments(self, parser):
|
|
19
|
+
parser.add_argument(
|
|
20
|
+
'--app',
|
|
21
|
+
type=str,
|
|
22
|
+
default=None,
|
|
23
|
+
help='Filter by specific app label name'
|
|
24
|
+
)
|
|
25
|
+
parser.add_argument(
|
|
26
|
+
'--count',
|
|
27
|
+
action='store_true',
|
|
28
|
+
help='Show record count each model'
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
def handle(self, *args, **options):
|
|
32
|
+
app_name_filter = options.get('app')
|
|
33
|
+
show_record_count = options.get('count', False)
|
|
34
|
+
|
|
35
|
+
self.stdout.write(
|
|
36
|
+
self.style.SUCCESS('Available for seeding:\n')
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
for app_config in apps.get_app_configs():
|
|
40
|
+
if app_name_filter and app_config.label != app_name_filter:
|
|
41
|
+
continue
|
|
42
|
+
|
|
43
|
+
models = app_config.get_models()
|
|
44
|
+
|
|
45
|
+
if models:
|
|
46
|
+
self.stdout.write(
|
|
47
|
+
self.style.WARNING(f'{app_config.label}: ')
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
for model in models:
|
|
51
|
+
model_name = model.__name__
|
|
52
|
+
count = model.objects.count() if show_record_count else None
|
|
53
|
+
|
|
54
|
+
if show_record_count:
|
|
55
|
+
self.stdout.write(
|
|
56
|
+
f'{model_name} ({count} records)'
|
|
57
|
+
)
|
|
58
|
+
else:
|
|
59
|
+
self.stdout.write(f'{model_name}')
|
|
60
|
+
self.stdout.write('')
|
|
61
|
+
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Seed command
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
This management command seeds Django models with synthetic data using a configurable
|
|
6
|
+
mapping system. It supports JSON/YAML configuration files, direct model targeting,
|
|
7
|
+
data clearing, dry-run previews, and relational data seeding.
|
|
8
|
+
'''
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
from django.core.management.base import BaseCommand, CommandError
|
|
13
|
+
from django.apps import apps
|
|
14
|
+
from django_seeder import BulkModelLoader, ConfigParser
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Command(BaseCommand):
|
|
18
|
+
|
|
19
|
+
help = 'Seed Django models with synthetic data'
|
|
20
|
+
|
|
21
|
+
def add_arguments(self, parser):
|
|
22
|
+
parser.add_argument(
|
|
23
|
+
'--config',
|
|
24
|
+
type=str,
|
|
25
|
+
default=None,
|
|
26
|
+
help='Path to seed configuration file (JSON or YAML)'
|
|
27
|
+
)
|
|
28
|
+
parser.add_argument(
|
|
29
|
+
'--model',
|
|
30
|
+
type=str,
|
|
31
|
+
default=None,
|
|
32
|
+
help='Seed specific model (format: app_label.ModelName)'
|
|
33
|
+
)
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
'--rows',
|
|
36
|
+
type=int,
|
|
37
|
+
default=10,
|
|
38
|
+
help='Number of rows to seed (default: 10)'
|
|
39
|
+
)
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
'--app',
|
|
42
|
+
type=str,
|
|
43
|
+
default=None,
|
|
44
|
+
help='Django app label name'
|
|
45
|
+
)
|
|
46
|
+
parser.add_argument(
|
|
47
|
+
'--clear',
|
|
48
|
+
action='store_true',
|
|
49
|
+
help='Clear existing data in models before seeding'
|
|
50
|
+
)
|
|
51
|
+
parser.add_argument(
|
|
52
|
+
'--dry-run',
|
|
53
|
+
action='store_true',
|
|
54
|
+
help='Show what would be seeded without actually seeding'
|
|
55
|
+
)
|
|
56
|
+
parser.add_argument(
|
|
57
|
+
'--verbose',
|
|
58
|
+
action='store_true',
|
|
59
|
+
help='Show detailed output'
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def handle(self, *args, **options):
|
|
63
|
+
loader = BulkModelLoader()
|
|
64
|
+
config = None
|
|
65
|
+
|
|
66
|
+
if options['config']:
|
|
67
|
+
try:
|
|
68
|
+
config = ConfigParser.load_config(options['config'])
|
|
69
|
+
|
|
70
|
+
if not ConfigParser.validate_config(config):
|
|
71
|
+
raise CommandError('Invalid file configuration format')
|
|
72
|
+
|
|
73
|
+
for model_name in config.keys():
|
|
74
|
+
try:
|
|
75
|
+
app_label = config[model_name].get('app_label', 'core')
|
|
76
|
+
model = apps.get_model(app_label, model_name)
|
|
77
|
+
loader.register(model)
|
|
78
|
+
except LookupError:
|
|
79
|
+
raise CommandError(f'Model not found: {app_label}.{model_name}')
|
|
80
|
+
|
|
81
|
+
self.stdout.write(
|
|
82
|
+
self.style.SUCCESS(f'Loaded config from {options["config"]}')
|
|
83
|
+
)
|
|
84
|
+
except FileNotFoundError:
|
|
85
|
+
raise CommandError(f'Configuration file not found: {options["config"]}')
|
|
86
|
+
except Exception as e:
|
|
87
|
+
raise CommandError(f'Error loading config: {str(e)}')
|
|
88
|
+
|
|
89
|
+
elif options['model'] and options['app']:
|
|
90
|
+
app_label = options['app']
|
|
91
|
+
model_name = options['model']
|
|
92
|
+
rows = options['rows']
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
model = apps.get_model(app_label, model_name)
|
|
96
|
+
loader.register(model)
|
|
97
|
+
|
|
98
|
+
config = {
|
|
99
|
+
model_name: {
|
|
100
|
+
'rows': rows,
|
|
101
|
+
'mapping': self._get_default_mapping(model)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
self.stdout.write(
|
|
106
|
+
self.style.WARNING(
|
|
107
|
+
f'No mapping provided. Using auto-detected fields for {model_name}'
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
except LookupError:
|
|
111
|
+
raise CommandError(f'Model not found: {app_label}.{model_name}')
|
|
112
|
+
|
|
113
|
+
else:
|
|
114
|
+
raise CommandError('Either --config or both --model and --app must be provided')
|
|
115
|
+
|
|
116
|
+
if not config:
|
|
117
|
+
raise CommandError('No configuration available')
|
|
118
|
+
|
|
119
|
+
if options['clear']:
|
|
120
|
+
self.stdout.write(
|
|
121
|
+
self.style.WARNING('Clearing existing data...')
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
for model_name in loader.seeders:
|
|
125
|
+
loader.clear(model_name)
|
|
126
|
+
|
|
127
|
+
self.stdout.write(
|
|
128
|
+
self.style.SUCCESS('Data successfully cleared')
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
if options['dry_run']:
|
|
132
|
+
self.stdout.write(
|
|
133
|
+
self.style.WARNING('DRY RUN MODE')
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
for model_name, model_config in config.items():
|
|
137
|
+
rows = model_config.get('rows', 10)
|
|
138
|
+
|
|
139
|
+
self.stdout.write(
|
|
140
|
+
f' Would seed {rows} rows in {model_name}'
|
|
141
|
+
)
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
try:
|
|
145
|
+
results = {}
|
|
146
|
+
|
|
147
|
+
for model_name, model_config in config.items():
|
|
148
|
+
seeder = loader.seeders[model_name]
|
|
149
|
+
rows = model_config.get('rows', 10)
|
|
150
|
+
mapping = model_config.get('mapping', {})
|
|
151
|
+
relations = model_config.get('relations', {})
|
|
152
|
+
|
|
153
|
+
if relations:
|
|
154
|
+
relation_resolvers = {}
|
|
155
|
+
for rel_field, rel_model_name in relations.items():
|
|
156
|
+
if rel_model_name in results:
|
|
157
|
+
instances = results[rel_model_name]
|
|
158
|
+
relation_resolvers[rel_field] = lambda: instances[__import__('random').randint(0, len(instances) - 1)]
|
|
159
|
+
|
|
160
|
+
result = seeder.seed_with_relations(rows, mapping, relation_resolvers)
|
|
161
|
+
else:
|
|
162
|
+
result = seeder.seed(rows, mapping)
|
|
163
|
+
|
|
164
|
+
results[model_name] = result
|
|
165
|
+
|
|
166
|
+
self.stdout.write(
|
|
167
|
+
self.style.SUCCESS('Seeding successfully completed')
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
for model_name, instances in results.items():
|
|
171
|
+
self.stdout.write(
|
|
172
|
+
f'{model_name}: {len(instances)} rows seeded'
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
if options['verbose']:
|
|
176
|
+
self.stdout.write('\nDetailed Results')
|
|
177
|
+
|
|
178
|
+
for model_name, instances in results.items():
|
|
179
|
+
self.stdout.write(f'\n{model_name}')
|
|
180
|
+
|
|
181
|
+
for index, instance in enumerate(instances[:3], 1):
|
|
182
|
+
self.stdout.write(f' {index}. {instance}')
|
|
183
|
+
|
|
184
|
+
if len(instances) > 3:
|
|
185
|
+
self.stdout.write(f' ... and {len(instances) - 3} more')
|
|
186
|
+
|
|
187
|
+
except Exception as e:
|
|
188
|
+
raise CommandError(f'Error during seeding: {str(e)}')
|
|
189
|
+
|
|
190
|
+
def _get_default_mapping(self, model):
|
|
191
|
+
mapping = {}
|
|
192
|
+
for field in model._meta.get_fields():
|
|
193
|
+
if field.many_to_one or field.one_to_one or field.many_to_many:
|
|
194
|
+
continue
|
|
195
|
+
mapping[field.name] = field.name
|
|
196
|
+
return mapping
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Seeder Module
|
|
3
|
+
|
|
4
|
+
This module provides the core functionality for generating and inserting
|
|
5
|
+
synthetic data into Django models. It is designed to simplify database
|
|
6
|
+
seeding during development and testing by automatically generating
|
|
7
|
+
structured datasets and converting them into Django ORM model instances.
|
|
8
|
+
|
|
9
|
+
Information
|
|
10
|
+
-----------
|
|
11
|
+
@author: Christian G. Garcia
|
|
12
|
+
@github: github.com/christiangarcia0311/django-model-seeder
|
|
13
|
+
'''
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
from seed import Dataset
|
|
17
|
+
from django.db.models import Model
|
|
18
|
+
from typing import Dict, List, Type, Any, Callable
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ModelSeeder:
|
|
22
|
+
|
|
23
|
+
def __init__(self, model: Type[Model]):
|
|
24
|
+
self.model = model
|
|
25
|
+
self.dataset = Dataset()
|
|
26
|
+
self.field_mapping = {}
|
|
27
|
+
|
|
28
|
+
self._introspect_fields()
|
|
29
|
+
|
|
30
|
+
def _introspect_fields(self):
|
|
31
|
+
for field in self.model._meta.get_fields():
|
|
32
|
+
if field.many_to_one or field.one_to_one:
|
|
33
|
+
continue
|
|
34
|
+
|
|
35
|
+
self.field_mapping[field.name] = None
|
|
36
|
+
|
|
37
|
+
def seed(self, rows: int, mapping: Dict[str, Any]) -> List[Model]:
|
|
38
|
+
data_generator = self.dataset.generate(rows, mapping)
|
|
39
|
+
|
|
40
|
+
instances = []
|
|
41
|
+
|
|
42
|
+
for index, row in data_generator.iterrows():
|
|
43
|
+
kwargs = {}
|
|
44
|
+
|
|
45
|
+
for field_name, value in row.to_dict().items():
|
|
46
|
+
if field_name in self.field_mapping:
|
|
47
|
+
kwargs[field_name] = value
|
|
48
|
+
|
|
49
|
+
instance = self.model(**kwargs)
|
|
50
|
+
instances.append(instance)
|
|
51
|
+
|
|
52
|
+
self.model.objects.bulk_create(instances)
|
|
53
|
+
return instances
|
|
54
|
+
|
|
55
|
+
def seed_with_relations(
|
|
56
|
+
self,
|
|
57
|
+
rows: int,
|
|
58
|
+
mapping: Dict[str, Any],
|
|
59
|
+
relations: Dict[str, Callable] = None
|
|
60
|
+
) -> List[Model]:
|
|
61
|
+
data_generator = self.dataset.generate(rows, mapping)
|
|
62
|
+
|
|
63
|
+
instances = []
|
|
64
|
+
|
|
65
|
+
for index, row in data_generator.iterrows():
|
|
66
|
+
kwargs = {}
|
|
67
|
+
|
|
68
|
+
for field_name, value in row.to_dict().items():
|
|
69
|
+
if field_name in self.field_mapping:
|
|
70
|
+
kwargs[field_name] = value
|
|
71
|
+
|
|
72
|
+
if relations:
|
|
73
|
+
for relation_field, resolver_func in relations.items():
|
|
74
|
+
try:
|
|
75
|
+
kwargs[relation_field] = resolver_func()
|
|
76
|
+
except Exception as e:
|
|
77
|
+
print(f'Warning: Could not resolve relation {relation_field}: {e}')
|
|
78
|
+
continue
|
|
79
|
+
|
|
80
|
+
instance = self.model(**kwargs)
|
|
81
|
+
instances.append(instance)
|
|
82
|
+
|
|
83
|
+
self.model.objects.bulk_create(instances)
|
|
84
|
+
return instances
|
|
85
|
+
|
|
86
|
+
def seed_with_validation(
|
|
87
|
+
self,
|
|
88
|
+
rows: int,
|
|
89
|
+
mapping: Dict[str, Any],
|
|
90
|
+
validator: Callable = None
|
|
91
|
+
) -> List[Model]:
|
|
92
|
+
data_generator = self.dataset.generate(rows, mapping)
|
|
93
|
+
|
|
94
|
+
instances = []
|
|
95
|
+
|
|
96
|
+
for index, row in data_generator.iterrows():
|
|
97
|
+
kwargs = {}
|
|
98
|
+
|
|
99
|
+
for field_name, value in row.to_dict().items():
|
|
100
|
+
if field_name in self.field_mapping:
|
|
101
|
+
kwargs[field_name] = value
|
|
102
|
+
|
|
103
|
+
if validator and not validator(kwargs):
|
|
104
|
+
continue
|
|
105
|
+
|
|
106
|
+
instance = self.model(**kwargs)
|
|
107
|
+
instances.append(instance)
|
|
108
|
+
|
|
109
|
+
self.model.objects.bulk_create(instances)
|
|
110
|
+
return instances
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "django-model-seeder"
|
|
7
|
+
version = "2.6.1"
|
|
8
|
+
description = "A Django library powered by Data Seed PH for generating realistic, synthetic Philippine-based datasets directly into Django models."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{name = "Christian G. Garcia", email = "iyaniyan03112003@gmail.com"}]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"django",
|
|
15
|
+
"data-seed-ph",
|
|
16
|
+
"pyyaml",
|
|
17
|
+
]
|
|
18
|
+
keywords = ["synthetic-data", "database-seeding", "testing"]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 4 - Beta",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
dev = [
|
|
29
|
+
"pytest>=7.0",
|
|
30
|
+
"pytest-django>=4.0",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/christiangarcia0311/django-model-seeder"
|
|
35
|
+
Repository = "https://github.com/christiangarcia0311/django-model-seeder.git"
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
where = ["."]
|
|
39
|
+
include = ["django_seeder*"]
|
|
40
|
+
exclude = ["fixtures*", "tests*"]
|