Skip to content

helpers

check_required_meta_parameters(new_model)

Verifies if ormar.Model has database and metadata set.

Recreates Connection pool for sqlite3

Parameters:

Name Type Description Default
new_model Type[Model]

newly declared ormar Model

required
Source code in ormar\models\helpers\models.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
def check_required_meta_parameters(new_model: Type["Model"]) -> None:
    """
    Verifies if ormar.Model has database and metadata set.

    Recreates Connection pool for sqlite3

    :param new_model: newly declared ormar Model
    :type new_model: Model class
    """
    if not hasattr(new_model.Meta, "database"):
        if not getattr(new_model.Meta, "abstract", False):
            raise ormar.ModelDefinitionError(
                f"{new_model.__name__} does not have database defined."
            )

    else:
        substitue_backend_pool_for_sqlite(new_model=new_model)

    if not hasattr(new_model.Meta, "metadata"):
        if not getattr(new_model.Meta, "abstract", False):
            raise ormar.ModelDefinitionError(
                f"{new_model.__name__} does not have metadata defined."
            )

expand_reverse_relationships(model)

Iterates through model_fields of given model and verifies if all reverse relation have been populated on related models.

If the reverse relation has not been set before it's set here.

Parameters:

Name Type Description Default
model Type[Model]

model on which relation should be checked and registered

required
Source code in ormar\models\helpers\relations.py
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
def expand_reverse_relationships(model: Type["Model"]) -> None:
    """
    Iterates through model_fields of given model and verifies if all reverse
    relation have been populated on related models.

    If the reverse relation has not been set before it's set here.

    :param model: model on which relation should be checked and registered
    :type model: Model class
    """
    model_fields = list(model.Meta.model_fields.values())
    for model_field in model_fields:
        if model_field.is_relation and not model_field.has_unresolved_forward_refs():
            model_field = cast("ForeignKeyField", model_field)
            expand_reverse_relationship(model_field=model_field)

extract_annotations_and_default_vals(attrs)

Extracts annotations from class namespace dict and triggers extraction of ormar model_fields.

Parameters:

Name Type Description Default
attrs Dict

namespace of the class created

required

Returns:

Type Description
Tuple[Dict, Dict]

namespace of the class updated, dict of extracted model_fields

Source code in ormar\models\helpers\models.py
131
132
133
134
135
136
137
138
139
140
141
142
143
144
def extract_annotations_and_default_vals(attrs: Dict) -> Tuple[Dict, Dict]:
    """
    Extracts annotations from class namespace dict and triggers
    extraction of ormar model_fields.

    :param attrs: namespace of the class created
    :type attrs: Dict
    :return: namespace of the class updated, dict of extracted model_fields
    :rtype: Tuple[Dict, Dict]
    """
    key = "__annotations__"
    attrs[key] = attrs.get(key, {})
    attrs, model_fields = populate_pydantic_default_values(attrs)
    return attrs, model_fields

get_potential_fields(attrs)

Gets all the fields in current class namespace that are Fields.

Parameters:

Name Type Description Default
attrs Union[Dict, MappingProxyType]

current class namespace

required

Returns:

Type Description
Dict

extracted fields that are ormar Fields

Source code in ormar\models\helpers\pydantic.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
def get_potential_fields(attrs: Union[Dict, MappingProxyType]) -> Dict:
    """
    Gets all the fields in current class namespace that are Fields.

    :param attrs: current class namespace
    :type attrs: Dict
    :return: extracted fields that are ormar Fields
    :rtype: Dict
    """
    return {
        k: v
        for k, v in attrs.items()
        if (lenient_issubclass(v, BaseField) or isinstance(v, BaseField))
    }

get_pydantic_base_orm_config()

Returns empty pydantic Config with orm_mode set to True.

Returns:

Type Description
pydantic Config

empty default config with orm_mode set.

Source code in ormar\models\helpers\pydantic.py
126
127
128
129
130
131
132
133
134
135
136
137
138
def get_pydantic_base_orm_config() -> Type[pydantic.BaseConfig]:
    """
    Returns empty pydantic Config with orm_mode set to True.

    :return: empty default config with orm_mode set.
    :rtype: pydantic Config
    """

    class Config(pydantic.BaseConfig):
        orm_mode = True
        validate_assignment = True

    return Config

get_pydantic_field(field_name, model)

Extracts field type and if it's required from Model model_fields by passed field_name. Returns a pydantic field with type of field_name field type.

Parameters:

Name Type Description Default
field_name str

field name to fetch from Model and name of pydantic field

required
model Type[Model]

type of field to register

required

Returns:

Type Description
pydantic.ModelField

newly created pydantic field

Source code in ormar\models\helpers\pydantic.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
def get_pydantic_field(field_name: str, model: Type["Model"]) -> "ModelField":
    """
    Extracts field type and if it's required from Model model_fields by passed
    field_name. Returns a pydantic field with type of field_name field type.

    :param field_name: field name to fetch from Model and name of pydantic field
    :type field_name: str
    :param model: type of field to register
    :type model: Model class
    :return: newly created pydantic field
    :rtype: pydantic.ModelField
    """
    type_ = model.Meta.model_fields[field_name].__type__
    return ModelField(
        name=field_name,
        type_=type_,  # type: ignore
        model_config=model.__config__,
        required=not model.Meta.model_fields[field_name].nullable,
        class_validators={},
    )

merge_or_generate_pydantic_config(attrs, name)

Checks if the user provided pydantic Config, and if he did merges it with the default one.

Updates the attrs in place with a new config.

Source code in ormar\models\helpers\pydantic.py
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
def merge_or_generate_pydantic_config(attrs: Dict, name: str) -> None:
    """
    Checks if the user provided pydantic Config,
    and if he did merges it with the default one.

    Updates the attrs in place with a new config.

    :rtype: None
    """
    DefaultConfig = get_pydantic_base_orm_config()
    if "Config" in attrs:
        ProvidedConfig = attrs["Config"]
        if not inspect.isclass(ProvidedConfig):
            raise ModelDefinitionError(
                f"Config provided for class {name} has to be a class."
            )

        class Config(ProvidedConfig, DefaultConfig):  # type: ignore
            pass

        attrs["Config"] = Config
    else:
        attrs["Config"] = DefaultConfig

meta_field_not_set(model, field_name)

Checks if field with given name is already present in model.Meta. Then check if it's set to something truthful (in practice meaning not None, as it's non or ormar Field only).

Parameters:

Name Type Description Default
model Type[Model]

newly constructed model

required
field_name str

name of the ormar field

required

Returns:

Type Description
bool

result of the check

Source code in ormar\models\helpers\models.py
179
180
181
182
183
184
185
186
187
188
189
190
191
192
def meta_field_not_set(model: Type["Model"], field_name: str) -> bool:
    """
    Checks if field with given name is already present in model.Meta.
    Then check if it's set to something truthful
    (in practice meaning not None, as it's non or ormar Field only).

    :param model: newly constructed model
    :type model: Model class
    :param field_name: name of the ormar field
    :type field_name: str
    :return: result of the check
    :rtype: bool
    """
    return not hasattr(model.Meta, field_name) or not getattr(model.Meta, field_name)

populate_choices_validators(model)

Checks if Model has any fields with choices set. If yes it adds choices validation into pre root validators.

Parameters:

Name Type Description Default
model Type[Model]

newly constructed Model

required
Source code in ormar\models\helpers\validation.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
def populate_choices_validators(model: Type["Model"]) -> None:  # noqa CCR001
    """
    Checks if Model has any fields with choices set.
    If yes it adds choices validation into pre root validators.

    :param model: newly constructed Model
    :type model: Model class
    """
    fields_with_choices = []
    if not meta_field_not_set(model=model, field_name="model_fields"):
        if not hasattr(model, "_choices_fields"):
            model._choices_fields = set()
        for name, field in model.Meta.model_fields.items():
            if check_if_field_has_choices(field) and name not in model._choices_fields:
                fields_with_choices.append(name)
                validator = make_generic_validator(generate_validator(field))
                model.__fields__[name].validators.append(validator)
                model._choices_fields.add(name)

        if fields_with_choices:
            model.Config.schema_extra = construct_modify_schema_function(
                fields_with_choices=fields_with_choices
            )
        else:
            model.Config.schema_extra = construct_schema_function_without_choices()

populate_default_options_values(new_model, model_fields)

Sets all optional Meta values to it's defaults and set model_fields that were already previously extracted.

Here should live all options that are not overwritten/set for all models.

Current options are: * constraints = [] * abstract = False

Parameters:

Name Type Description Default
new_model Type[Model]

newly constructed Model

required
model_fields Dict

dict of model fields

required
Source code in ormar\models\helpers\models.py
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
def populate_default_options_values(  # noqa: CCR001
    new_model: Type["Model"], model_fields: Dict
) -> None:
    """
    Sets all optional Meta values to it's defaults
    and set model_fields that were already previously extracted.

    Here should live all options that are not overwritten/set for all models.

    Current options are:
    * constraints = []
    * abstract = False

    :param new_model: newly constructed Model
    :type new_model: Model class
    :param model_fields: dict of model fields
    :type model_fields: Union[Dict[str, type], Dict]
    """
    defaults = {
        "queryset_class": ormar.QuerySet,
        "constraints": [],
        "model_fields": model_fields,
        "abstract": False,
        "extra": Extra.forbid,
        "orders_by": [],
        "exclude_parent_fields": [],
    }
    for key, value in defaults.items():
        if not hasattr(new_model.Meta, key):
            setattr(new_model.Meta, key, value)

    if any(
        is_field_an_forward_ref(field) for field in new_model.Meta.model_fields.values()
    ):
        new_model.Meta.requires_ref_update = True
    else:
        new_model.Meta.requires_ref_update = False

    new_model._json_fields = {
        name
        for name, field in new_model.Meta.model_fields.items()
        if field.__type__ == pydantic.Json
    }
    new_model._bytes_fields = {
        name
        for name, field in new_model.Meta.model_fields.items()
        if field.__type__ == bytes
    }

    new_model.__relation_map__ = None

populate_meta_sqlalchemy_table_if_required(meta)

Constructs sqlalchemy table out of columns and parameters set on Meta class. It populates name, metadata, columns and constraints.

Parameters:

Name Type Description Default
meta ModelMeta

Meta class of the Model without sqlalchemy table constructed

required
Source code in ormar\models\helpers\sqlalchemy.py
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
def populate_meta_sqlalchemy_table_if_required(meta: "ModelMeta") -> None:
    """
    Constructs sqlalchemy table out of columns and parameters set on Meta class.
    It populates name, metadata, columns and constraints.

    :param meta: Meta class of the Model without sqlalchemy table constructed
    :type meta: Model class Meta
    """
    if not hasattr(meta, "table") and check_for_null_type_columns_from_forward_refs(
        meta
    ):
        set_constraint_names(meta=meta)
        table = sqlalchemy.Table(
            meta.tablename, meta.metadata, *meta.columns, *meta.constraints
        )
        meta.table = table

populate_meta_tablename_columns_and_pk(name, new_model)

Sets Model tablename if it's not already set in Meta. Default tablename if not present is class name lower + s (i.e. Bed becomes -> beds)

Checks if Model's Meta have pkname and columns set. If not calls the sqlalchemy_columns_from_model_fields to populate columns from ormar.fields definitions.

Parameters:

Name Type Description Default
name str

name of the current Model

required
new_model Type[Model]

currently constructed Model

required

Returns:

Type Description
ormar.models.metaclass.ModelMetaclass

Model with populated pkname and columns in Meta

Raises:

Type Description
ModelDefinitionError

if pkname is not present raises ModelDefinitionError. Each model has to have pk.

Source code in ormar\models\helpers\sqlalchemy.py
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
def populate_meta_tablename_columns_and_pk(
    name: str, new_model: Type["Model"]
) -> Type["Model"]:
    """
    Sets Model tablename if it's not already set in Meta.
    Default tablename if not present is class name lower + s (i.e. Bed becomes -> beds)

    Checks if Model's Meta have pkname and columns set.
    If not calls the sqlalchemy_columns_from_model_fields to populate
    columns from ormar.fields definitions.

    :raises ModelDefinitionError: if pkname is not present raises ModelDefinitionError.
    Each model has to have pk.

    :param name: name of the current Model
    :type name: str
    :param new_model: currently constructed Model
    :type new_model: ormar.models.metaclass.ModelMetaclass
    :return: Model with populated pkname and columns in Meta
    :rtype: ormar.models.metaclass.ModelMetaclass
    """
    tablename = name.lower() + "s"
    new_model.Meta.tablename = (
        new_model.Meta.tablename if hasattr(new_model.Meta, "tablename") else tablename
    )
    pkname: Optional[str]

    if hasattr(new_model.Meta, "columns"):
        columns = new_model.Meta.columns
        pkname = new_model.Meta.pkname
    else:
        pkname, columns = sqlalchemy_columns_from_model_fields(
            new_model.Meta.model_fields, new_model
        )

    if pkname is None:
        raise ormar.ModelDefinitionError("Table has to have a primary key.")

    new_model.Meta.columns = columns
    new_model.Meta.pkname = pkname
    if not new_model.Meta.orders_by:
        # by default we sort by pk name if other option not provided
        new_model.Meta.orders_by.append(pkname)
    return new_model

register_relation_in_alias_manager(field)

Registers the relation (and reverse relation) in alias manager. The m2m relations require registration of through model between actual end models of the relation.

Delegates the actual registration to: m2m - register_many_to_many_relation_on_build fk - register_relation_on_build

Parameters:

Name Type Description Default
field ForeignKeyField

relation field

required
Source code in ormar\models\helpers\relations.py
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
def register_relation_in_alias_manager(field: "ForeignKeyField") -> None:
    """
    Registers the relation (and reverse relation) in alias manager.
    The m2m relations require registration of through model between
    actual end models of the relation.

    Delegates the actual registration to:
    m2m - register_many_to_many_relation_on_build
    fk - register_relation_on_build

    :param field: relation field
    :type field: ForeignKey or ManyToManyField class
    """
    if field.is_multi:
        if field.has_unresolved_forward_refs():
            return
        field = cast("ManyToManyField", field)
        register_many_to_many_relation_on_build(field=field)
    elif field.is_relation and not field.is_through:
        if field.has_unresolved_forward_refs():
            return
        register_relation_on_build(field=field)

remove_excluded_parent_fields(model)

Removes pydantic fields that should be excluded from parent models

Parameters:

Name Type Description Default
model Type[Model]
required
Source code in ormar\models\helpers\pydantic.py
157
158
159
160
161
162
163
164
165
166
167
168
def remove_excluded_parent_fields(model: Type["Model"]) -> None:
    """
    Removes pydantic fields that should be excluded from parent models

    :param model:
    :type model: Type["Model"]
    """
    excludes = {*model.Meta.exclude_parent_fields} - {*model.Meta.model_fields.keys()}
    if excludes:
        model.__fields__ = {
            k: v for k, v in model.__fields__.items() if k not in excludes
        }

sqlalchemy_columns_from_model_fields(model_fields, new_model)

Iterates over declared on Model model fields and extracts fields that should be treated as database fields.

If the model is empty it sets mandatory id field as primary key (used in through models in m2m relations).

Triggers a validation of relation_names in relation fields. If multiple fields are leading to the same related model only one can have empty related_name param. Also related_names have to be unique.

Trigger validation of primary_key - only one and required pk can be set, cannot be pydantic_only.

Append fields to columns if it's not pydantic_only, virtual ForeignKey or ManyToMany field.

Sets owner on each model_field as reference to newly created Model.

Parameters:

Name Type Description Default
model_fields Dict

dictionary of declared ormar model fields

required
new_model Type[Model]
required

Returns:

Type Description
Tuple[Optional[str], List[sqlalchemy.Column]]

pkname, list of sqlalchemy columns

Raises:

Type Description
ModelDefinitionError

if validation of related_names fail, or pkname validation fails.

Source code in ormar\models\helpers\sqlalchemy.py
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
def sqlalchemy_columns_from_model_fields(
    model_fields: Dict, new_model: Type["Model"]
) -> Tuple[Optional[str], List[sqlalchemy.Column]]:
    """
    Iterates over declared on Model model fields and extracts fields that
    should be treated as database fields.

    If the model is empty it sets mandatory id field as primary key
    (used in through models in m2m relations).

    Triggers a validation of relation_names in relation fields. If multiple fields
    are leading to the same related model only one can have empty related_name param.
    Also related_names have to be unique.

    Trigger validation of primary_key - only one and required pk can be set,
    cannot be pydantic_only.

    Append fields to columns if it's not pydantic_only,
    virtual ForeignKey or ManyToMany field.

    Sets `owner` on each model_field as reference to newly created Model.

    :raises ModelDefinitionError: if validation of related_names fail,
    or pkname validation fails.
    :param model_fields: dictionary of declared ormar model fields
    :type model_fields: Dict[str, ormar.Field]
    :param new_model:
    :type new_model: Model class
    :return: pkname, list of sqlalchemy columns
    :rtype: Tuple[Optional[str], List[sqlalchemy.Column]]
    """
    if len(model_fields.keys()) == 0:
        model_fields["id"] = ormar.Integer(name="id", primary_key=True)
        logging.warning(
            f"Table {new_model.Meta.tablename} had no fields so auto "
            "Integer primary key named `id` created."
        )
    validate_related_names_in_relations(model_fields, new_model)
    return _process_fields(model_fields=model_fields, new_model=new_model)