1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
|
"""
This is the facade of all features of DDF.
Module that contains wrappers and shortcuts (aliases).
"""
import warnings
from django.apps import apps
from django_dynamic_fixture.ddf import DynamicFixture, Copier, Mask, DDFLibrary, \
set_pre_save_receiver, set_post_save_receiver
from django_dynamic_fixture.django_helper import print_field_values, django_greater_than
from django_dynamic_fixture.fixture_algorithms.sequential_fixture import SequentialDataFixture, \
StaticSequentialDataFixture
from django_dynamic_fixture.global_settings import DDF_DEFAULT_DATA_FIXTURE, DDF_FILL_NULLABLE_FIELDS, DDF_FK_MIN_DEPTH, \
DDF_IGNORE_FIELDS, DDF_VALIDATE_MODELS, \
DDF_DEBUG_MODE, DDF_FIELD_FIXTURES
from django_dynamic_fixture.script_ddf_checkings import ddf_check_models
__version__ = '4.0.1'
if not django_greater_than(4, 0):
warnings.warn("DDF 4.* officially supports only Django 4 or higher.", DeprecationWarning)
LOOKUP_SEP = '__'
def look_up_alias(ddf_as_f=True, **kwargs):
"""
Example of parameters:
a__b__c=1 => {a: {b: {c: 1}}}
"""
field_dict = {}
params_to_be_skipped = []
for alias, value in kwargs.items():
parts = alias.split(LOOKUP_SEP)
if len(parts) == 1:
params_to_be_skipped.append(alias)
level_dict = field_dict
for part in parts[:-1]:
level_dict = level_dict.setdefault(part, {})
level_dict[parts[-1]] = value
if ddf_as_f:
for root, value in field_dict.items():
if root in params_to_be_skipped:
field_dict[root] = value
else:
field_dict[root] = dict_to_f(value)
return field_dict
def dict_to_f(value):
"""
Example:
1 => 1
{b: 1} => F(b=1)
{b: {c: 1}} => F(b=F(c=1))
"""
if not isinstance(value, dict):
return value
else:
kwargs = {}
for k, v in value.items():
if not isinstance(v, dict):
kwargs[k] = v
else:
kwargs[k] = dict_to_f(v)
return F(**kwargs)
def fixture(**kwargs):
"""
DynamicFixture factory: It instantiate a DynamicFixture using global configurations.
Same as F(...)
"""
kwargs = look_up_alias(**kwargs)
f = DynamicFixture(data_fixture=kwargs.pop('data_fixture', DDF_DEFAULT_DATA_FIXTURE),
fill_nullable_fields=kwargs.pop('fill_nullable_fields', DDF_FILL_NULLABLE_FIELDS),
ignore_fields=kwargs.pop('ignore_fields', []),
fk_min_depth=kwargs.pop('fk_min_depth', DDF_FK_MIN_DEPTH),
validate_models=kwargs.pop('validate_models', DDF_VALIDATE_MODELS),
print_errors=kwargs.pop('print_errors', True),
debug_mode=kwargs.pop('debug_mode', DDF_DEBUG_MODE),
**kwargs)
return f
# Wrappers
def _new(model, n=1, ddf_lesson=None, persist_dependencies=False, **kwargs):
"""
Return one or many valid instances of Django Models with fields filled with auto generated or customized data.
All instances will NOT be persisted in the database, except its dependencies, in case @persist_dependencies is True.
@model: The class of the Django model. It can be a string `<app_label>.<model_name>`
@n: number of instances to be created with the given configuration. Default is 1.
@ddf_lesson: use a custom ddf_lesson to build the model object.
@persist_dependencies: If True, save internal dependencies, otherwise just instantiate them. Default is False.
@data_fixture: override DDF_DEFAULT_DATA_FIXTURE configuration. Default is SequentialDataFixture().
@fill_nullable_fields: override DDF_FILL_NULLABLE_FIELDS global configuration. Default is True.
@ignore_fields: List of fields that will be ignored by DDF. It will be concatenated with the global list DDF_IGNORE_FIELDS. Default is [].
@fk_min_depth: override DDF_FK_MIN_DEPTH global configuration. Default 0.
@validate_models: override DDF_VALIDATE_MODELS global configuration. Default is False.
@print_errors: print on console all instance values if DDF can not generate a valid object with the given configuration.
Wrapper for the method DynamicFixture.new
"""
if isinstance(model, str):
model = apps.get_model(model)
kwargs = look_up_alias(**kwargs)
d = fixture(**kwargs)
if n == 1:
return d.new(model, ddf_lesson=ddf_lesson, persist_dependencies=persist_dependencies, **kwargs)
instances = []
for _ in range(n):
instances.append(d.new(model, ddf_lesson=ddf_lesson, persist_dependencies=persist_dependencies, **kwargs))
return instances
def _get(model, n=1, ddf_lesson=None, **kwargs):
"""
Return one or many valid instances of Django Models with fields filled with auto generated or customized data.
All instances will be persisted in the database.
@model: The class of the Django model. It can be a string `<app_label>.<model_name>`
@n: number of instances to be created with the given configuration. Default is 1.
@ddf_lesson: use a custom ddf_lesson to build the model object.
@data_fixture: override DDF_DEFAULT_DATA_FIXTURE configuration. Default is SequentialDataFixture().
@fill_nullable_fields: override DDF_FILL_NULLABLE_FIELDS global configuration. Default is True.
@ignore_fields: List of fields that will be ignored by DDF. It will be concatenated with the global list DDF_IGNORE_FIELDS. Default is [].
@fk_min_depth: override DDF_FK_MIN_DEPTH global configuration. Default 0.
@validate_models: override DDF_VALIDATE_MODELS global configuration. Default is False.
@print_errors: print on console all instance values if DDF can not generate a valid object with the given configuration.
Wrapper for the method DynamicFixture.get
"""
if isinstance(model, str):
model = apps.get_model(model)
kwargs = look_up_alias(**kwargs)
d = fixture(**kwargs)
if n == 1:
return d.get(model, ddf_lesson=ddf_lesson, **kwargs)
instances = []
for _ in range(n):
instances.append(d.get(model, ddf_lesson=ddf_lesson, **kwargs))
return instances
def _teach(model, ddf_lesson=None, **kwargs):
'''
@model: The class of the Django model. It can be a string `<app_label>.<model_name>`
@ddf_lesson: Name of custom ddf_lesson to be created.
@raise an CantOverrideddf_lesson error if the same model/ddf_lesson were called twice.
Sometimes DDF can't create an model instance because the particularities of the model.
The workaround for this is to teach DDF how to create it.
Basically, we use the same object creation approach that will be saved as a template
for the next DDF calls.
Use this method to teach DDF how to create an instance.
New metaphor for the shelve/library feature.
`Shelve` becomes `Teach`
`Library` becomes `Lessons`
'''
if isinstance(model, str):
model = apps.get_model(model)
kwargs = look_up_alias(**kwargs)
d = fixture(**kwargs)
return d.teach(model, ddf_lesson=ddf_lesson, **kwargs)
# Shortcuts
N = new = _new
G = get = _get
T = teach = _teach
F = fixture
C = Copier
M = Mask
P = print_field_values
DDFLibrary = DDFLibrary
PRE_SAVE = set_pre_save_receiver
POST_SAVE = set_post_save_receiver
import typing
INSTANCE_TYPE = typing.TypeVar('INSTANCE')
def new(model: typing.Type[INSTANCE_TYPE], n: int = 1, ddf_lesson = None, persist_dependencies: bool = True, **kwargs) -> INSTANCE_TYPE:
return _new(model, n=n, ddf_lesson=ddf_lesson, persist_dependencies=persist_dependencies, **kwargs)
def get(model: typing.Type[INSTANCE_TYPE], n: int=1, ddf_lesson=None, **kwargs) -> INSTANCE_TYPE:
return _get(model, n=n, ddf_lesson=ddf_lesson, **kwargs)
def teach(model: typing.Type[INSTANCE_TYPE], ddf_lesson=None, **kwargs):
return _teach(model, ddf_lesson=ddf_lesson, **kwargs)
N = new
G = get
T = teach
|