Skip to content

foreign_key

ForeignKeyConstraint dataclass

Internal container to store ForeignKey definitions used later to produce sqlalchemy.ForeignKeys

Source code in ormar\fields\foreign_key.py
184
185
186
187
188
189
190
191
192
193
194
@dataclass
class ForeignKeyConstraint:
    """
    Internal container to store ForeignKey definitions used later
    to produce sqlalchemy.ForeignKeys
    """

    reference: Union[str, sqlalchemy.Column]
    name: Optional[str]
    ondelete: Optional[str]
    onupdate: Optional[str]

ForeignKeyField

Bases: BaseField

Actual class returned from ForeignKey function call and stored in model_fields.

Source code in ormar\fields\foreign_key.py
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
class ForeignKeyField(BaseField):
    """
    Actual class returned from ForeignKey function call and stored in model_fields.
    """

    def __init__(self, **kwargs: Any) -> None:
        if TYPE_CHECKING:  # pragma: no cover
            self.__type__: type
            self.to: Type["Model"]
        self.ondelete: str = kwargs.pop("ondelete", None)
        self.onupdate: str = kwargs.pop("onupdate", None)
        super().__init__(**kwargs)

    def get_source_related_name(self) -> str:
        """
        Returns name to use for source relation name.
        For FK it's the same, differs for m2m fields.
        It's either set as `related_name` or by default it's owner model. get_name + 's'
        :return: name of the related_name or default related name.
        :rtype: str
        """
        return self.get_related_name()

    def get_related_name(self) -> str:
        """
        Returns name to use for reverse relation.
        It's either set as `related_name` or by default it's owner model. get_name + 's'
        :return: name of the related_name or default related name.
        :rtype: str
        """
        return self.related_name or self.owner.get_name() + "s"

    def default_target_field_name(self) -> str:
        """
        Returns default target model name on through model.
        :return: name of the field
        :rtype: str
        """
        prefix = "from_" if self.self_reference else ""
        return self.through_reverse_relation_name or f"{prefix}{self.to.get_name()}"

    def default_source_field_name(self) -> str:
        """
        Returns default target model name on through model.
        :return: name of the field
        :rtype: str
        """
        prefix = "to_" if self.self_reference else ""
        return self.through_relation_name or f"{prefix}{self.owner.get_name()}"

    def get_filter_clause_target(self) -> Type["Model"]:
        return self.to

    def get_model_relation_fields(self, use_alias: bool = False) -> str:
        """
        Extract names of the database columns or model fields that are connected
        with given relation based on use_alias switch and which side of the relation
        the current field is - reverse or normal.

        :param use_alias: use db names aliases or model fields
        :type use_alias: bool
        :return: name or names of the related columns/ fields
        :rtype: Union[str, List[str]]
        """
        if use_alias:
            return self._get_model_relation_fields_alias()
        return self._get_model_relation_fields_name()

    def _get_model_relation_fields_name(self) -> str:
        if self.virtual:
            return self.owner.ormar_config.pkname
        return self.name

    def _get_model_relation_fields_alias(self) -> str:
        if self.virtual:
            return self.owner.ormar_config.model_fields[
                self.owner.ormar_config.pkname
            ].get_alias()
        return self.get_alias()

    def get_related_field_alias(self) -> str:
        """
        Extract names of the related database columns or that are connected
        with given relation based to use as a target in filter clause.

        :return: name or names of the related columns/ fields
        :rtype: Union[str, Dict[str, str]]
        """
        if self.virtual:
            field_name = self.get_related_name()
            field = self.to.ormar_config.model_fields[field_name]
            return field.get_alias()
        target_field = self.to.get_column_alias(self.to.ormar_config.pkname)
        return target_field

    def get_related_field_name(self) -> Union[str, List[str]]:
        """
        Returns name of the relation field that should be used in prefetch query.
        This field is later used to register relation in prefetch query,
        populate relations dict, and populate nested model in prefetch query.

        :return: name(s) of the field
        :rtype: Union[str, List[str]]
        """
        if self.virtual:
            return self.get_related_name()
        return self.to.ormar_config.pkname

    def _evaluate_forward_ref(
        self, globalns: Any, localns: Any, is_through: bool = False
    ) -> None:
        target = "through" if is_through else "to"
        target_obj = getattr(self, target)
        if sys.version_info.minor <= 8:  # pragma: no cover
            evaluated = target_obj._evaluate(globalns, localns)
        else:  # pragma: no cover
            evaluated = target_obj._evaluate(globalns, localns, set())
        setattr(self, target, evaluated)

    def evaluate_forward_ref(self, globalns: Any, localns: Any) -> None:
        """
        Evaluates the ForwardRef to actual Field based on global and local namespaces

        :param globalns: global namespace
        :type globalns: Any
        :param localns: local namespace
        :type localns: Any
        :return: None
        :rtype: None
        """
        if self.to.__class__ == ForwardRef:
            self._evaluate_forward_ref(globalns, localns)
            (
                self.__type__,
                self.constraints,
                self.column_type,
                self.to_pk_only,
            ) = populate_fk_params_based_on_to_model(
                to=self.to,
                nullable=self.nullable,
                ondelete=self.ondelete,
                onupdate=self.onupdate,
            )

    def _extract_model_from_sequence(
        self, value: List, child: "Model", to_register: bool
    ) -> List["Model"]:
        """
        Takes a list of Models and registers them on parent.
        Registration is mutual, so children have also reference to parent.

        Used in reverse FK relations.

        :param value: list of Model
        :type value: List
        :param child: child/ related Model
        :type child: Model
        :param to_register: flag if the relation should be set in RelationshipManager
        :type to_register: bool
        :return: list (if needed) registered Models
        :rtype: List["Model"]
        """
        return [
            self.expand_relationship(  # type: ignore
                value=val, child=child, to_register=to_register
            )
            for val in value
        ]

    def _register_existing_model(
        self, value: "Model", child: "Model", to_register: bool
    ) -> "Model":
        """
        Takes already created instance and registers it for parent.
        Registration is mutual, so children have also reference to parent.

        Used in reverse FK relations and normal FK for single models.

        :param value: already instantiated Model
        :type value: Model
        :param child: child/ related Model
        :type child: Model
        :param to_register: flag if the relation should be set in RelationshipManager
        :type to_register: bool
        :return: (if needed) registered Model
        :rtype: Model
        """
        if to_register:
            self.register_relation(model=value, child=child)
        return value

    def _construct_model_from_dict(
        self, value: dict, child: "Model", to_register: bool
    ) -> "Model":
        """
        Takes a dictionary, creates a instance and registers it for parent.
        If dictionary contains only one field and it's a pk it is a __pk_only__ model.
        Registration is mutual, so children have also reference to parent.

        Used in normal FK for dictionaries.

        :param value: dictionary of a Model
        :type value: dict
        :param child: child/ related Model
        :type child: Model
        :param to_register: flag if the relation should be set in RelationshipManager
        :type to_register: bool
        :return: (if needed) registered Model
        :rtype: Model
        """
        pk_only_model = None
        keys = set(value.keys())
        own_keys = keys - self.to.extract_related_names()
        if (
            len(own_keys) == 1
            and list(own_keys)[0] == self.to.ormar_config.pkname
            and value.get(self.to.ormar_config.pkname) is not None
            and not self.is_through
        ):
            value["__pk_only__"] = True
            pk_only_model = self.to_pk_only(**value)
        model = self.to(**value)
        if to_register:
            self.register_relation(model=model, child=child)
        return pk_only_model if pk_only_model is not None else model

    def _construct_model_from_pk(
        self, value: Any, child: "Model", to_register: bool
    ) -> "Model":
        """
        Takes a pk value, creates a dummy instance and registers it for parent.
        Registration is mutual, so children have also reference to parent.

        Used in normal FK for dictionaries.

        :param value: value of a related pk / fk column
        :type value: Any
        :param child: child/ related Model
        :type child: Model
        :param to_register: flag if the relation should be set in RelationshipManager
        :type to_register: bool
        :return: (if needed) registered Model
        :rtype: Model
        """
        if self.to.pk_type() == uuid.UUID and isinstance(value, str):  # pragma: nocover
            value = uuid.UUID(value)
        if not isinstance(value, self.to.pk_type()):
            if isinstance(value, self.to_pk_only):
                value = getattr(value, self.to.ormar_config.pkname)
            else:
                raise RelationshipInstanceError(
                    f"Relationship error - ForeignKey {self.to.__name__} "
                    f"is of type {self.to.pk_type()} "
                    f"while {type(value)} passed as a parameter."
                )
        model = create_dummy_instance(fk=self.to, pk=value)
        if to_register:
            self.register_relation(model=model, child=child)
        return model

    def register_relation(self, model: "Model", child: "Model") -> None:
        """
        Registers relation between parent and child in relation manager.
        Relation manager is kep on each model (different instance).

        Used in Metaclass and sometimes some relations are missing
        (i.e. cloned Models in fastapi might miss one).

        :param model: parent model (with relation definition)
        :type model: Model class
        :param child: child model
        :type child: Model class
        """
        model._orm.add(parent=model, child=child, field=self)

    def has_unresolved_forward_refs(self) -> bool:
        """
        Verifies if the filed has any ForwardRefs that require updating before the
        model can be used.

        :return: result of the check
        :rtype: bool
        """
        return self.to.__class__ == ForwardRef

    def expand_relationship(
        self,
        value: Any,
        child: Union["Model", "NewBaseModel"],
        to_register: bool = True,
    ) -> Optional[Union["Model", List["Model"]]]:
        """
        For relations the child model is first constructed (if needed),
        registered in relation and returned.
        For relation fields the value can be a pk value (Any type of field),
        dict (from Model) or actual instance/list of a "Model".

        Selects the appropriate constructor based on a passed value.

        :param value: a Model field value, returned untouched for non relation fields.
        :type value: Any
        :param child: a child Model to register
        :type child: Union["Model", "NewBaseModel"]
        :param to_register: flag if the relation should be set in RelationshipManager
        :type to_register: bool
        :return: returns a Model or a list of Models
        :rtype: Optional[Union["Model", List["Model"]]]
        """
        if value is None:
            return None if not self.virtual else []
        constructors = {
            f"{self.to.__name__}": self._register_existing_model,
            "dict": self._construct_model_from_dict,
            "list": self._extract_model_from_sequence,
        }

        model = constructors.get(  # type: ignore
            value.__class__.__name__, self._construct_model_from_pk
        )(value, child, to_register)
        return model

    def get_relation_name(self) -> str:  # pragma: no cover
        """
        Returns name of the relation, which can be a own name or through model
        names for m2m models

        :return: result of the check
        :rtype: bool
        """
        return self.name

    def get_source_model(self) -> Type["Model"]:  # pragma: no cover
        """
        Returns model from which the relation comes -> either owner or through model

        :return: source model
        :rtype: Type["Model"]
        """
        return self.owner

default_source_field_name()

Returns default target model name on through model.

Returns:

Type Description
str

name of the field

Source code in ormar\fields\foreign_key.py
360
361
362
363
364
365
366
367
def default_source_field_name(self) -> str:
    """
    Returns default target model name on through model.
    :return: name of the field
    :rtype: str
    """
    prefix = "to_" if self.self_reference else ""
    return self.through_relation_name or f"{prefix}{self.owner.get_name()}"

default_target_field_name()

Returns default target model name on through model.

Returns:

Type Description
str

name of the field

Source code in ormar\fields\foreign_key.py
351
352
353
354
355
356
357
358
def default_target_field_name(self) -> str:
    """
    Returns default target model name on through model.
    :return: name of the field
    :rtype: str
    """
    prefix = "from_" if self.self_reference else ""
    return self.through_reverse_relation_name or f"{prefix}{self.to.get_name()}"

evaluate_forward_ref(globalns, localns)

Evaluates the ForwardRef to actual Field based on global and local namespaces

Parameters:

Name Type Description Default
globalns Any

global namespace

required
localns Any

local namespace

required

Returns:

Type Description
None

None

Source code in ormar\fields\foreign_key.py
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
def evaluate_forward_ref(self, globalns: Any, localns: Any) -> None:
    """
    Evaluates the ForwardRef to actual Field based on global and local namespaces

    :param globalns: global namespace
    :type globalns: Any
    :param localns: local namespace
    :type localns: Any
    :return: None
    :rtype: None
    """
    if self.to.__class__ == ForwardRef:
        self._evaluate_forward_ref(globalns, localns)
        (
            self.__type__,
            self.constraints,
            self.column_type,
            self.to_pk_only,
        ) = populate_fk_params_based_on_to_model(
            to=self.to,
            nullable=self.nullable,
            ondelete=self.ondelete,
            onupdate=self.onupdate,
        )

expand_relationship(value, child, to_register=True)

For relations the child model is first constructed (if needed), registered in relation and returned. For relation fields the value can be a pk value (Any type of field), dict (from Model) or actual instance/list of a "Model".

Selects the appropriate constructor based on a passed value.

Parameters:

Name Type Description Default
value Any

a Model field value, returned untouched for non relation fields.

required
child Union[Model, NewBaseModel]

a child Model to register

required
to_register bool

flag if the relation should be set in RelationshipManager

True

Returns:

Type Description
Optional[Union["Model", List["Model"]]]

returns a Model or a list of Models

Source code in ormar\fields\foreign_key.py
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
def expand_relationship(
    self,
    value: Any,
    child: Union["Model", "NewBaseModel"],
    to_register: bool = True,
) -> Optional[Union["Model", List["Model"]]]:
    """
    For relations the child model is first constructed (if needed),
    registered in relation and returned.
    For relation fields the value can be a pk value (Any type of field),
    dict (from Model) or actual instance/list of a "Model".

    Selects the appropriate constructor based on a passed value.

    :param value: a Model field value, returned untouched for non relation fields.
    :type value: Any
    :param child: a child Model to register
    :type child: Union["Model", "NewBaseModel"]
    :param to_register: flag if the relation should be set in RelationshipManager
    :type to_register: bool
    :return: returns a Model or a list of Models
    :rtype: Optional[Union["Model", List["Model"]]]
    """
    if value is None:
        return None if not self.virtual else []
    constructors = {
        f"{self.to.__name__}": self._register_existing_model,
        "dict": self._construct_model_from_dict,
        "list": self._extract_model_from_sequence,
    }

    model = constructors.get(  # type: ignore
        value.__class__.__name__, self._construct_model_from_pk
    )(value, child, to_register)
    return model

get_model_relation_fields(use_alias=False)

Extract names of the database columns or model fields that are connected with given relation based on use_alias switch and which side of the relation the current field is - reverse or normal.

Parameters:

Name Type Description Default
use_alias bool

use db names aliases or model fields

False

Returns:

Type Description
Union[str, List[str]]

name or names of the related columns/ fields

Source code in ormar\fields\foreign_key.py
372
373
374
375
376
377
378
379
380
381
382
383
384
385
def get_model_relation_fields(self, use_alias: bool = False) -> str:
    """
    Extract names of the database columns or model fields that are connected
    with given relation based on use_alias switch and which side of the relation
    the current field is - reverse or normal.

    :param use_alias: use db names aliases or model fields
    :type use_alias: bool
    :return: name or names of the related columns/ fields
    :rtype: Union[str, List[str]]
    """
    if use_alias:
        return self._get_model_relation_fields_alias()
    return self._get_model_relation_fields_name()

Extract names of the related database columns or that are connected with given relation based to use as a target in filter clause.

Returns:

Type Description
Union[str, Dict[str, str]]

name or names of the related columns/ fields

Source code in ormar\fields\foreign_key.py
399
400
401
402
403
404
405
406
407
408
409
410
411
412
def get_related_field_alias(self) -> str:
    """
    Extract names of the related database columns or that are connected
    with given relation based to use as a target in filter clause.

    :return: name or names of the related columns/ fields
    :rtype: Union[str, Dict[str, str]]
    """
    if self.virtual:
        field_name = self.get_related_name()
        field = self.to.ormar_config.model_fields[field_name]
        return field.get_alias()
    target_field = self.to.get_column_alias(self.to.ormar_config.pkname)
    return target_field

Returns name of the relation field that should be used in prefetch query. This field is later used to register relation in prefetch query, populate relations dict, and populate nested model in prefetch query.

Returns:

Type Description
Union[str, List[str]]

name(s) of the field

Source code in ormar\fields\foreign_key.py
414
415
416
417
418
419
420
421
422
423
424
425
def get_related_field_name(self) -> Union[str, List[str]]:
    """
    Returns name of the relation field that should be used in prefetch query.
    This field is later used to register relation in prefetch query,
    populate relations dict, and populate nested model in prefetch query.

    :return: name(s) of the field
    :rtype: Union[str, List[str]]
    """
    if self.virtual:
        return self.get_related_name()
    return self.to.ormar_config.pkname

Returns name to use for reverse relation. It's either set as related_name or by default it's owner model. get_name + 's'

Returns:

Type Description
str

name of the related_name or default related name.

Source code in ormar\fields\foreign_key.py
342
343
344
345
346
347
348
349
def get_related_name(self) -> str:
    """
    Returns name to use for reverse relation.
    It's either set as `related_name` or by default it's owner model. get_name + 's'
    :return: name of the related_name or default related name.
    :rtype: str
    """
    return self.related_name or self.owner.get_name() + "s"

get_relation_name()

Returns name of the relation, which can be a own name or through model names for m2m models

Returns:

Type Description
bool

result of the check

Source code in ormar\fields\foreign_key.py
640
641
642
643
644
645
646
647
648
def get_relation_name(self) -> str:  # pragma: no cover
    """
    Returns name of the relation, which can be a own name or through model
    names for m2m models

    :return: result of the check
    :rtype: bool
    """
    return self.name

get_source_model()

Returns model from which the relation comes -> either owner or through model

Returns:

Type Description
Type["Model"]

source model

Source code in ormar\fields\foreign_key.py
650
651
652
653
654
655
656
657
def get_source_model(self) -> Type["Model"]:  # pragma: no cover
    """
    Returns model from which the relation comes -> either owner or through model

    :return: source model
    :rtype: Type["Model"]
    """
    return self.owner

Returns name to use for source relation name. For FK it's the same, differs for m2m fields. It's either set as related_name or by default it's owner model. get_name + 's'

Returns:

Type Description
str

name of the related_name or default related name.

Source code in ormar\fields\foreign_key.py
332
333
334
335
336
337
338
339
340
def get_source_related_name(self) -> str:
    """
    Returns name to use for source relation name.
    For FK it's the same, differs for m2m fields.
    It's either set as `related_name` or by default it's owner model. get_name + 's'
    :return: name of the related_name or default related name.
    :rtype: str
    """
    return self.get_related_name()

has_unresolved_forward_refs()

Verifies if the filed has any ForwardRefs that require updating before the model can be used.

Returns:

Type Description
bool

result of the check

Source code in ormar\fields\foreign_key.py
594
595
596
597
598
599
600
601
602
def has_unresolved_forward_refs(self) -> bool:
    """
    Verifies if the filed has any ForwardRefs that require updating before the
    model can be used.

    :return: result of the check
    :rtype: bool
    """
    return self.to.__class__ == ForwardRef

register_relation(model, child)

Registers relation between parent and child in relation manager. Relation manager is kep on each model (different instance).

Used in Metaclass and sometimes some relations are missing (i.e. cloned Models in fastapi might miss one).

Parameters:

Name Type Description Default
model Model

parent model (with relation definition)

required
child Model

child model

required
Source code in ormar\fields\foreign_key.py
579
580
581
582
583
584
585
586
587
588
589
590
591
592
def register_relation(self, model: "Model", child: "Model") -> None:
    """
    Registers relation between parent and child in relation manager.
    Relation manager is kep on each model (different instance).

    Used in Metaclass and sometimes some relations are missing
    (i.e. cloned Models in fastapi might miss one).

    :param model: parent model (with relation definition)
    :type model: Model class
    :param child: child model
    :type child: Model class
    """
    model._orm.add(parent=model, child=child, field=self)

ForeignKey(to, *, name=None, unique=False, nullable=True, related_name=None, virtual=False, onupdate=None, ondelete=None, **kwargs)

Despite a name it's a function that returns constructed ForeignKeyField. This function is actually used in model declaration (as ormar.ForeignKey(ToModel)).

Accepts number of relation setting parameters as well as all BaseField ones.

Parameters:

Name Type Description Default
to Union[Type[T], ForwardRef]

target related ormar Model

required
name Optional[str]

name of the database field - later called alias

None
unique bool

parameter passed to sqlalchemy.ForeignKey, unique flag

False
nullable bool

marks field as optional/ required

True
related_name Optional[str]

name of reversed FK relation populated for you on to model

None
virtual bool

marks if relation is virtual. It is for reversed FK and auto generated FK on through model in Many2Many relations.

False
onupdate Union[ReferentialAction, str, None]

parameter passed to sqlalchemy.ForeignKey. How to treat child rows on update of parent (the one where FK is defined) model.

None
ondelete Union[ReferentialAction, str, None]

parameter passed to sqlalchemy.ForeignKey. How to treat child rows on delete of parent (the one where FK is defined) model.

None
kwargs Any

all other args to be populated by BaseField

{}

Returns:

Type Description
ForeignKeyField

ormar ForeignKeyField with relation to selected model

Source code in ormar\fields\foreign_key.py
207
208
209
210
211
212
213
214
215
216
217
218
219
220
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
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
def ForeignKey(  # type: ignore # noqa CFQ002
    to: Union[Type["T"], "ForwardRef"],
    *,
    name: Optional[str] = None,
    unique: bool = False,
    nullable: bool = True,
    related_name: Optional[str] = None,
    virtual: bool = False,
    onupdate: Union[ReferentialAction, str, None] = None,
    ondelete: Union[ReferentialAction, str, None] = None,
    **kwargs: Any,
) -> "T":
    """
    Despite a name it's a function that returns constructed ForeignKeyField.
    This function is actually used in model declaration (as ormar.ForeignKey(ToModel)).

    Accepts number of relation setting parameters as well as all BaseField ones.

    :param to: target related ormar Model
    :type to: Model class
    :param name: name of the database field - later called alias
    :type name: str
    :param unique: parameter passed to sqlalchemy.ForeignKey, unique flag
    :type unique: bool
    :param nullable: marks field as optional/ required
    :type nullable: bool
    :param related_name: name of reversed FK relation populated for you on to model
    :type related_name: str
    :param virtual: marks if relation is virtual.
    It is for reversed FK and auto generated FK on through model in Many2Many relations.
    :type virtual: bool
    :param onupdate: parameter passed to sqlalchemy.ForeignKey.
    How to treat child rows on update of parent (the one where FK is defined) model.
    :type onupdate: Union[ReferentialAction, str]
    :param ondelete: parameter passed to sqlalchemy.ForeignKey.
    How to treat child rows on delete of parent (the one where FK is defined) model.
    :type ondelete: Union[ReferentialAction, str]
    :param kwargs: all other args to be populated by BaseField
    :type kwargs: Any
    :return: ormar ForeignKeyField with relation to selected model
    :rtype: ForeignKeyField
    """

    onupdate = validate_referential_action(action=onupdate)
    ondelete = validate_referential_action(action=ondelete)

    owner = kwargs.pop("owner", None)
    self_reference = kwargs.pop("self_reference", False)

    orders_by = kwargs.pop("orders_by", None)
    related_orders_by = kwargs.pop("related_orders_by", None)

    skip_reverse = kwargs.pop("skip_reverse", False)
    skip_field = kwargs.pop("skip_field", False)

    sql_nullable = kwargs.pop("sql_nullable", None)
    sql_nullable = nullable if sql_nullable is None else sql_nullable

    index = kwargs.pop("index", False)

    validate_not_allowed_fields(kwargs)
    pk_only_model = None
    if to.__class__ == ForwardRef:
        __type__ = to if not nullable else Optional[to]
        constraints: List = []
        column_type = None
    else:
        (
            __type__,
            constraints,
            column_type,
            pk_only_model,
        ) = populate_fk_params_based_on_to_model(
            to=to,  # type: ignore
            nullable=nullable,
            ondelete=ondelete,
            onupdate=onupdate,
        )

    namespace = dict(
        __type__=__type__,
        to=to,
        to_pk_only=pk_only_model,
        through=None,
        alias=name,
        name=kwargs.pop("real_name", None),
        nullable=nullable,
        sql_nullable=sql_nullable,
        constraints=constraints,
        unique=unique,
        column_type=column_type,
        related_name=related_name,
        virtual=virtual,
        primary_key=False,
        index=index,
        default=None,
        server_default=None,
        onupdate=onupdate,
        ondelete=ondelete,
        owner=owner,
        self_reference=self_reference,
        is_relation=True,
        orders_by=orders_by,
        related_orders_by=related_orders_by,
        skip_reverse=skip_reverse,
        skip_field=skip_field,
    )

    Field = type("ForeignKey", (ForeignKeyField, BaseField), {})
    return Field(**namespace)

create_dummy_instance(fk, pk=None)

Ormar never returns you a raw data. So if you have a related field that has a value populated it will construct you a Model instance out of it.

Creates a "fake" instance of passed Model from pk value. The instantiated Model has only pk value filled. To achieve this pk_only flag has to be passed as it skips the validation.

If the nested related Models are required they are set with -1 as pk value.

Parameters:

Name Type Description Default
fk Type[T]

class of the related Model to which instance should be constructed

required
pk Any

value of the primary_key column

None

Returns:

Type Description
Model

Model instance populated with only pk

Source code in ormar\fields\foreign_key.py
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
def create_dummy_instance(fk: Type["T"], pk: Any = None) -> "T":
    """
    Ormar never returns you a raw data.
    So if you have a related field that has a value populated
    it will construct you a Model instance out of it.

    Creates a "fake" instance of passed Model from pk value.
    The instantiated Model has only pk value filled.
    To achieve this __pk_only__ flag has to be passed as it skips the validation.

    If the nested related Models are required they are set with -1 as pk value.

    :param fk: class of the related Model to which instance should be constructed
    :type fk: Model class
    :param pk: value of the primary_key column
    :type pk: Any
    :return: Model instance populated with only pk
    :rtype: Model
    """
    init_dict = {
        **{fk.ormar_config.pkname: pk or -1, "__pk_only__": True},
        **{
            k: create_dummy_instance(v.to)
            for k, v in fk.ormar_config.model_fields.items()
            if v.is_relation and not v.nullable and not v.virtual
        },
    }
    return fk(**init_dict)

create_dummy_model(base_model, pk_field)

Used to construct a dummy pydantic model for type hints and pydantic validation. Populates only pk field and set it to desired type.

Parameters:

Name Type Description Default
base_model Type[T]

class of target dummy model

required
pk_field Union[BaseField, ForeignKeyField, ManyToManyField]

ormar Field to be set on pydantic Model

required

Returns:

Type Description
pydantic.BaseModel

constructed dummy model

Source code in ormar\fields\foreign_key.py
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
def create_dummy_model(
    base_model: Type["T"],
    pk_field: Union[BaseField, "ForeignKeyField", "ManyToManyField"],
) -> Type["BaseModel"]:
    """
    Used to construct a dummy pydantic model for type hints and pydantic validation.
    Populates only pk field and set it to desired type.

    :param base_model: class of target dummy model
    :type base_model: Model class
    :param pk_field: ormar Field to be set on pydantic Model
    :type pk_field: Union[BaseField, "ForeignKeyField", "ManyToManyField"]
    :return: constructed dummy model
    :rtype: pydantic.BaseModel
    """
    alias = (
        "".join(choices(string.ascii_uppercase, k=6))  # + uuid.uuid4().hex[:4]
    ).lower()
    fields = {f"{pk_field.name}": (pk_field.__type__, None)}

    dummy_model = create_model(  # type: ignore
        f"PkOnly{base_model.get_name(lower=False)}{alias}",
        __module__=base_model.__module__,
        **fields,  # type: ignore
    )
    return dummy_model

populate_fk_params_based_on_to_model(to, nullable, onupdate=None, ondelete=None)

Based on target to model to which relation leads to populates the type of the pydantic field to use, ForeignKey constraint and type of the target column field.

Parameters:

Name Type Description Default
to Type[T]

target related ormar Model

required
nullable bool

marks field as optional/ required

required
onupdate Optional[str]

parameter passed to sqlalchemy.ForeignKey. How to treat child rows on update of parent (the one where FK is defined) model.

None
ondelete Optional[str]

parameter passed to sqlalchemy.ForeignKey. How to treat child rows on delete of parent (the one where FK is defined) model.

None

Returns:

Type Description
Tuple[Any, List, Any]

tuple with target pydantic type, list of fk constraints and target col type

Source code in ormar\fields\foreign_key.py
 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
def populate_fk_params_based_on_to_model(
    to: Type["T"],
    nullable: bool,
    onupdate: Optional[str] = None,
    ondelete: Optional[str] = None,
) -> Tuple[Any, List, Any, Any]:
    """
    Based on target to model to which relation leads to populates the type of the
    pydantic field to use, ForeignKey constraint and type of the target column field.

    :param to: target related ormar Model
    :type to: Model class
    :param nullable: marks field as optional/ required
    :type nullable: bool
    :param onupdate: parameter passed to sqlalchemy.ForeignKey.
    How to treat child rows on update of parent (the one where FK is defined) model.
    :type onupdate: str
    :param ondelete: parameter passed to sqlalchemy.ForeignKey.
    How to treat child rows on delete of parent (the one where FK is defined) model.
    :type ondelete: str
    :return: tuple with target pydantic type, list of fk constraints and target col type
    :rtype: Tuple[Any, List, Any]
    """
    fk_string = (
        to.ormar_config.tablename + "." + to.get_column_alias(to.ormar_config.pkname)
    )
    to_field = to.ormar_config.model_fields[to.ormar_config.pkname]
    pk_only_model = create_dummy_model(to, to_field)
    __type__ = (
        Union[to_field.__type__, to, pk_only_model]
        if not nullable
        else Optional[Union[to_field.__type__, to, pk_only_model]]
    )
    constraints = [
        ForeignKeyConstraint(
            reference=fk_string, ondelete=ondelete, onupdate=onupdate, name=None
        )
    ]
    column_type = to_field.column_type
    return __type__, constraints, column_type, pk_only_model

validate_not_allowed_fields(kwargs)

Verifies if not allowed parameters are set on relation models. Usually they are omitted later anyway but this way it's explicitly notify the user that it's not allowed/ supported.

Parameters:

Name Type Description Default
kwargs Dict

dict of kwargs to verify passed to relation field

required

Raises:

Type Description
ModelDefinitionError

if any forbidden field is set

Source code in ormar\fields\foreign_key.py
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
def validate_not_allowed_fields(kwargs: Dict) -> None:
    """
    Verifies if not allowed parameters are set on relation models.
    Usually they are omitted later anyway but this way it's explicitly
    notify the user that it's not allowed/ supported.

    :raises ModelDefinitionError: if any forbidden field is set
    :param kwargs: dict of kwargs to verify passed to relation field
    :type kwargs: Dict
    """
    default = kwargs.pop("default", None)
    encrypt_secret = kwargs.pop("encrypt_secret", None)
    encrypt_backend = kwargs.pop("encrypt_backend", None)
    encrypt_custom_backend = kwargs.pop("encrypt_custom_backend", None)
    overwrite_pydantic_type = kwargs.pop("overwrite_pydantic_type", None)

    not_supported = [
        default,
        encrypt_secret,
        encrypt_backend,
        encrypt_custom_backend,
        overwrite_pydantic_type,
    ]
    if any(x is not None for x in not_supported):
        raise ModelDefinitionError(
            f"Argument {next((x for x in not_supported if x is not None))} "
            f"is not supported "
            "on relation fields!"
        )

validate_referential_action(action)

Validation onupdate and ondelete action cast to a string value

Parameters:

Name Type Description Default
action Optional[Union[ReferentialAction, str]]

referential action attribute or name string

required

Raises:

Type Description
ModelDefinitionError

if action is a not valid name string value

Source code in ormar\fields\foreign_key.py
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
def validate_referential_action(
    action: Optional[Union[ReferentialAction, str]],
) -> Optional[str]:
    """
    Validation `onupdate` and `ondelete` action cast to a string value

    :raises ModelDefinitionError: if action is a not valid name string value
    :param action: referential action attribute or name string
    :type action: Optional[Union[ReferentialAction, str]]
    :rtype: Optional[str]
    """

    if action is not None and not isinstance(action, ReferentialAction):
        try:
            action = ReferentialAction(action.upper())
        except (ValueError, AttributeError):
            raise ModelDefinitionError(f"{action} ReferentialAction not supported.")

    return action.value if action is not None else None