Skip to content

deferred comment

self-referential model

When you want to reference the same model during declaration to create a relationship, you need to declare the referenced model as ForwardRef, because the class is not ready during declaration and python does not let you reference it by default.

Although you might want to use future comments or simply refer to the name with "", it won't work because ormar is intended to be used with explicitly declared ForwardRef.

First, you need to import the required references from the input.

1
from typing import ForwardRef

Now we need an example model and references to the same model that will be used to create self-referential relationships.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# create the forwardref to model Person
PersonRef = ForwardRef("Person")


class Person(ormar.Model):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100)
    # use the forwardref as to parameter
    supervisor: PersonRef = ormar.ForeignKey(PersonRef, related_name="employees")

That's it. But before you can use the model, you need to manually update references so that they generate the actual model.

!!!warns that if you try to use a model without updating references, a ModelError exception will be thrown. Therefore, in the example above, any call like the following will result in the exception python # creation of model - exception await Person.objects.create(name="Test") # initialization of model - exception Person2(name="Test") # usage of model's QuerySet - exception await Person2.objects.get()

To update references, call the update_forward_refs method on each model that has forward references only after all related models have been declared.

So, in order for our previous example to work, we only need one extra line.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
PersonRef = ForwardRef("Person")


class Person(ormar.Model):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100)
    supervisor: PersonRef = ormar.ForeignKey(PersonRef, related_name="employees")


Person.update_forward_refs()

Of course, the same can be done for the ManyToMany relationship in exactly the same way for the to and through parameters.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# declare the reference
ChildRef = ForwardRef("Child")

class ChildFriend(ormar.Model):
    ormar_config = base_ormar_config.copy()

class Child(ormar.Model):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100)
    # use it in relation
    friends = ormar.ManyToMany(ChildRef, through=ChildFriend,
                               related_name="also_friends")


Child.update_forward_refs()

cross-model relationship

The same mechanisms and logic as self-referencing models can be used to link multiple different models between each other.

Of course, ormar creates the inverse relationship for both sides of your link relationship, using the specified (or default) related_name.

However, if you need two (or more) relationships between any two models that, for whatever reason, should be stored on both sides (so declare one relationship on one model and the other on the second), you need to use ForwardRef to implement that.

Consider the following simple example.

 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
# teacher is not yet defined
TeacherRef = ForwardRef("Teacher")


class Student(ormar.Model):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100)
    # so we use reference instead of actual model
    primary_teacher: TeacherRef = ormar.ForeignKey(TeacherRef,
                                                   related_name="own_students")


class StudentTeacher(ormar.Model):
    ormar_config = base_ormar_config.copy(tablename='students_x_teachers')


class Teacher(ormar.Model):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100)
    # we need students for other relation hence the order
    students = ormar.ManyToMany(Student, through=StudentTeacher,
                                related_name="teachers")

# now the Teacher model is already defined we can update references
Student.update_forward_refs()

!!! Warning Remember that related_name must be unique in the related model regardless of how many relationships are defined.