Skip to content

model method

The primary interaction of!!!tip with the database is exposed through QuerySet objects exposed as Model.objects on each model, similar to django org.

1
To read more about **quering, joining tables, excluding fields etc. visit [queries][queries] section.**

Each model instance has a set of methods to save, update, or load itself.

The available methods are described below.

pedantic method

Note that each ormar.Model is also a pydantic.BaseModel, so all pydantic methods can also be used on models, especially the model_dump() and model_dump_json() methods, which can also accept exclusions, inclusions, and other parameters.

To read more, check out the pydantic documentation.

model_construct()

model_construct is the original equivalent of the__init__method used to construct new instances.

The difference is that model_construct skips validation, so you should use it when you know the data is correct and trustworthy. The benefit of using constructs is that they speed up execution due to skipping validation.

!!!note Note that contrary to the pydantic.model_construct method- ormar equivalent method will also handle nested dependent models.

Warning Keep in mind that because validation is skipped, the construction method does not perform any transformations, checks, etc. Therefore, it is your responsibility to provide valid data that can be used by the database.

1
2
3
4
The only two things that construct still performs are:

*  Providing a `default` value for not set fields
*  Initialize nested ormar models if you pass a dictionary or a primary key value

Model dump ()

Model_dump is a method inherited from pydantic, but ormar adds its own parameters and has some nuances when using defaults, so it is listed here for clarity.

model_dump Exports data from the model tree to the dictionary as the name implies.

model_dump Parameter description:

Include (or modify)

include: Union[Set, Dict] = None

The set of field names or dictionaries to include in the returned dictionary.

Note that pydantic has an unusual pattern of including/excluding fields from lists by index (as does nested models). If you want to exclude fields from all children, you need to pass the__all__key to the dictionary.

You can't exclude nested models in Sets in pydantic, but you can exclude them in ormar (by adding double underscores to the relationship names, i.e. excluding the category names of books you can use). exclude={"book__category__name"} )

ormar does not support index exclusion/inclusion and accepts a simplified and more user-friendly notation.

To examine how to include/exclude fields, including nested fields, see the Fields section with explanations and numerous examples.

!!!Note the fact that in ormar you can exclude nested models from the collection, and you can exclude from the entire model tree in response_model_exclude and response_model_include in fastapi!

Exclusion (or modification)

exclude: Union[Set, Dict] = None

A collection or dictionary of field names to exclude from the returned dictionary.

Note that pydantic has an unusual pattern of including/excluding fields from lists by index (as well as nested models). If you want to exclude fields from all children, you need to pass the__all__key to the dictionary.

You can't exclude nested models in Sets in pydantic, but you can exclude nested models in ormar (by adding double underscores to the relationship names, i.e. excluding the category names of books you use)exclude={"book__category__name"})

ormar does not support index exclusion/inclusion and accepts a simplified and more user-friendly notation.

To examine how to include/exclude fields, including nested fields, see the Fields section with explanations and numerous examples.

!!!Note the fact that in ormar you can exclude nested models from the collection, and you can exclude from the entire model tree in response_model_exclude and response_model_include in fastapi!

exclude_cancel settings

Excluding Not Set: Boolean = False

Flag Indicates whether fields that were not explicitly set when the model was created should be excluded from the returned dictionary.

!!!Note that after saving data to the database, each field has its own value->, which can be supplied by you, default, or none.

 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
That means that when you load the data from database, **all** fields are set, and this flag basically stop working!


class Category(ormar.Model):
    ormar_config = base_ormar_config.copy(tablename="categories")

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100, default="Test")
    visibility: bool = ormar.Boolean(default=True)


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

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100)
    price: float = ormar.Float(default=9.99)
    categories: List[Category] = ormar.ManyToMany(Category)

category = Category(name="Test 2")
assert category.model_dump() == {'id': None, 'items': [], 'name': 'Test 2',
                           'visibility': True}
assert category.model_dump(exclude_unset=True) == {'items': [], 'name': 'Test 2'}

await category.save()
category2 = await Category.objects.get()
assert category2.model_dump() == {'id': 1, 'items': [], 'name': 'Test 2',
                            'visibility': True}
# NOTE how after loading from db all fields are set explicitly
# as this is what happens when you populate a model from db
assert category2.model_dump(exclude_unset=True) == {'id': 1, 'items': [],
                                              'name': 'Test 2', 'visibility': True}

Excluding default values

exclude_defaults: bool = False

Flag indicating equal to its default value (whether set or not) should be excluded from the returned dictionary

 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
class Category(ormar.Model):
    ormar_config = base_ormar_config.copy(tablename="categories")

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100, default="Test")
    visibility: bool = ormar.Boolean(default=True)

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

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100)
    price: float = ormar.Float(default=9.99)
    categories: List[Category] = ormar.ManyToMany(Category)

category = Category()
# note that Integer pk is by default autoincrement so optional
assert category.model_dump() == {'id': None, 'items': [], 'name': 'Test', 'visibility': True}
assert category.model_dump(exclude_defaults=True) == {'items': []}

# save and reload the data
await category.save()
category2 = await Category.objects.get()

assert category2.model_dump() == {'id': 1, 'items': [], 'name': 'Test', 'visibility': True}
assert category2.model_dump(exclude_defaults=True) == {'id': 1, 'items': []}

Excluded_None

Exclude_None: Boolean = False

Flag indicates whether fields equal to None should be excluded from the returned dictionary.

 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
class Category(ormar.Model):
    ormar_config = base_ormar_config.copy(tablename="categories")

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100, default="Test", nullable=True)
    visibility: bool = ormar.Boolean(default=True)


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

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100)
    price: float = ormar.Float(default=9.99)
    categories: List[Category] = ormar.ManyToMany(Category)


category = Category(name=None)
assert category.model_dump() == {'id': None, 'items': [], 'name': None,
                           'visibility': True}
# note the id is not set yet so None and excluded
assert category.model_dump(exclude_none=True) == {'items': [], 'visibility': True}

await category.save()
category2 = await Category.objects.get()
assert category2.model_dump() == {'id': 1, 'items': [], 'name': None,
                            'visibility': True}
assert category2.model_dump(exclude_none=True) == {'id': 1, 'items': [],
                                             'visibility': True}

except_primary_keys (ormar only)

exclude_primary_keys: bool = False

Setting the flag to True excludes all primary key columns in the tree, including nested models.

1
2
3
4
5
6
7
8
9
class Item(ormar.Model):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100)

item1 = Item(id=1, name="Test Item")
assert item1.model_dump() == {"id": 1, "name": "Test Item"}
assert item1.model_dump(exclude_primary_keys=True) == {"name": "Test Item"}

except_through_models (ormar only)

exclude_through_models: bool = False

Passthrough models are automatically added for each many-to-many relationship and they save additional parameters on linked models/tables.

Setting except_through_models=True excludes all pass-through models, including pass-through models of submodels.

 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
class Category(ormar.Model):
    ormar_config = base_ormar_config.copy(tablename="categories")

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100)


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

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100)
    categories: List[Category] = ormar.ManyToMany(Category)

# tree defining the models
item_dict = {
            "name": "test",
            "categories": [{"name": "test cat"}, {"name": "test cat2"}],
        }
# save whole tree
await Item(**item_dict).save_related(follow=True, save_all=True)

# get the saved values
item = await Item.objects.select_related("categories").get()

# by default you can see the through models (itemcategory)
assert item.model_dump() == {'id': 1, 'name': 'test', 
                       'categories': [
                           {'id': 1, 'name': 'test cat', 
                            'itemcategory': {'id': 1, 'category': None, 'item': None}}, 
                           {'id': 2, 'name': 'test cat2', 
                            'itemcategory': {'id': 2, 'category': None, 'item': None}}
                       ]}

# you can exclude those fields/ models
assert item.model_dump(exclude_through_models=True) == {
                       'id': 1, 'name': 'test', 
                       'categories': [
                           {'id': 1, 'name': 'test cat'}, 
                           {'id': 2, 'name': 'test cat2'}
                       ]}

model_dump_json()

Model_dump_json() has exactly the same parameters as model_dump(), so check the above.

Of course, the end result is a string represented by json, not a dictionary.

Get_pydantic()

get_pydantic(include: Union[Set, Dict] = None, exclude: Union[Set, Dict] = None)

This method allows you to generate pydantic models from ormar models without having to re-enter all fields.

Note that if you have nested models, it will generate the entire pydantic model tree for you! but in a way that prevents circular reference problems.

In addition, you can pass exclusion and/or inclusion parameters to keep only the fields you want included in nested models.

This means that you can easily create pydantic models for requests and responses in fastapi.

!!! Note To read more about possible exclusions/inclusions and how to build an exclusion dictionary or setting, visit the Fields section of the documentation.

Given an example ormar model as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
base_ormar_config = ormar.OrmarConfig(
    metadata=sqlalchemy.MetaData(),
    database=databases.Database(DATABASE_URL, force_rollback=True),
)


class Category(ormar.Model):
    ormar_config = base_ormar_config.copy(tablename="categories")

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100)


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

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100, default="test")
    category: Optional[Category] = ormar.ForeignKey(Category, nullable=True)

You can generate pydantic models with a simple call.

1
PydanticCategory = Category.get_pydantic(include={"id", "name"})

This generates a model equivalent to:

1
2
3
class Category(BaseModel):
    id: Optional[int]
    name: Optional[str] = "test"

!!! Warning Note that having multiple classes with the same name in a module is not a good habit, and it breaks fastapi documentation. This is why ormar adds random 3 upper case letters to the class name. In the example above, this means that the class will actually be named Category_XIP(BaseModel).

To exclude or include nested fields, you can use a dictionary or double underscore.

1
2
3
# both calls are equivalent
PydanticCategory = Category.get_pydantic(include={"id", "items__id"})
PydanticCategory = Category.get_pydantic(include={"id": ..., "items": {"id"}})

The resulting structure is as follows:

1
2
3
4
5
6
class Item(BaseModel):
    id: Optional[int]

class Category(BaseModel):
    id: Optional[int]
    items: Optional[List[Item]]

Of course, you can also use deep nesting, and ormar will generate its pydantic equivalent for you (in a way that excludes loops).

Notice how the Item model above has no reference to Category, although the relationship is bidirectional in ormar (and ormar.Item has a categories field).

Note that the resulting pydantic model inherits all field validators from the original ormar model, including the ormar selection validator and validators defined using the pydantic.validator decorator.

1
2
But, at the same time all root validators present on `ormar` models will **NOT** be copied to the generated pydantic model. Since root validator can operate on all fields and a user can exclude some fields during generation of pydantic model it's not safe to copy those validators.
If required, you need to redefine/ manually copy them to generated pydantic model.

Loading ()

By default, when you query tables without prefetching correlation models, ormar will still construct your correlation models, but only populate them with pk values. You can load correlation models by calling the load() method.

Load() can also be used to refresh the model from the database if it has been changed by another process.

1
2
3
4
5
6
7
track = await Track.objects.get(name='The Bird')
track.album.pk # will return malibu album pk (1)
track.album.name # will return None

# you need to actually load the data first
await track.album.load()
track.album.name # will return 'Malibu'

Load all ()

load_all(follow: bool = False, exclude: Union[List, str, Set, Dict] = None) -> Model

The method works similarly to load(), but also iterates over all the relationships of the model calling the method and reloads them from the database.

By default, the load_all method loads only models that are directly related (one step away) to the model calling the method.

But you can specify the follow=True parameter to traverse nested models and load them all into the relational tree.

Warning To avoid recurring updates with the follow=True setting, load_all retains a set of visited models and does not perform nested loads on visited models.

1
2
3
4
5
6
So if you have a diamond or circular relations types you need to perform the loads in a manual way.

```python
# in example like this the second Street (coming from City) won't be load_all, so ZipCode won't be reloaded
Street -> District -> City -> Street -> ZipCode
```

The method also accepts an optional exclude parameter, which works exactly the same way as the exclude fields method in QuerySet. This allows you to delete fields from the correlation model being refreshed or skip the entire correlation model.

Method performs a database query and is therefore more efficient than nested calls to load() and all() on the correlation model.

!!!tip To learn more about exclusions, read except_fields

!!!Warning All relationships are cleared on load_all(), so if some nested models are excluded, they will be empty after the call.

Savings ()

save() -> self

You can create a new model using the QuerySet.create() method or initializing the model to a normal pydantic model and then calling the save() method.

Save() can also be used to save changes made to a model, if the primary key is not set or the model does not exist in the database.

The save() method does not check whether the model exists in the database, so if it does, you will receive an integrity error from the selected database backend if you try to save the model with an existing primary key.

1
2
3
4
5
track = Track(name='The Bird')
await track.save() # will persist the model in database

track = await Track.objects.get(name='The Bird')
await track.save() # will raise integrity error as pk is populated

Update ()

update(_columns: List[str] = None, **kwargs) -> self

You can update the model using the QuerySet.update() method or update model properties (fields) and call the update() method.

If you try to update the model without setting the primary key, a ModelPersistenceError exception is thrown.

To persist a newly created model, use the save() or upsert(**kwargs) methods.

1
2
track = await Track.objects.get(name='The Bird')
await track.update(name='The Bird Strikes Again')

To update only selected columns in the model to the database, provide a list of columns that should be updated to the_columns parameter.

In the example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Movie(ormar.Model):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100, nullable=False, name="title")
    year: int = ormar.Integer()
    profit: float = ormar.Float()

terminator = await Movie(name='Terminator', year=1984, profit=0.078).save()

terminator.name = "Terminator 2"
terminator.year = 1991
terminator.profit = 0.520

# update only name
await terminator.update(_columns=["name"])

# note that terminator instance was not reloaded so
assert terminator.year == 1991

# but once you load the data from db you see it was not updated
await terminator.load()
assert terminator.year == 1984

!!!warning Note that update() does not refresh instances of the model, so if you change more columns than the number of columns passed in the_columns list, your model instance will have different values than the database!

Update Insert ()

upsert(**kwargs) -> Self

It is a proxy for the save() or update(**kwargs) methods described above.

If the primary key is set to->, the update method is called.

If pk is not set, the save() method is called.

1
2
3
4
5
track = Track(name='The Bird')
await track.upsert() # will call save as the pk is empty

track = await Track.objects.get(name='The Bird')
await track.upsert(name='The Bird Strikes Again') # will call update as pk is already populated

Delete ()

You can delete a model using the QuerySet.delete() method or using your model and calling the delete() method.

1
2
track = await Track.objects.get(name='The Bird')
await track.delete() # will delete the model from database

!!!Tip Note that the trace object remains unchanged, only records in the database are deleted.

save_related(follow: bool = False, save_all: bool = False, exclude=Optional[Union[Set, Dict]]) -> None

Method iterates through all relationships of the model calling it and calls the upsert() method on each unsaved model.

To find out when a model is saved, see the Save Status section above.

By default, the save_lated method saves only models that are directly related (one step away) to the model calling the method.

But you can specify the follow=True parameter to traverse nested models and save them all in the relational tree.

By default, save_latedsaves only models in an unsaved state, which means they have been modified within the current scope.

If you want to force all related methods to be saved, use the save_all=True flag, which updates all related models inserted regardless of their save status.

If you want to skip saving certain relationships, you can pass exclusion parameters.

Exclude can be a set of model relationships of its own, or it can be a dictionary that contains nested terms.

!!!note Note that the exclusion parameter in save_lated accepts only the name of the relationship field, so if you pass any other fields, they are saved anyway

!!!note For more information about the structure of possible values passed to exclusions, check the Queryset.fields method documentation.

Warning To avoid recurring updates with the follow=True setting, save_lated retains a set of visited models on each branch of the relationship tree and does not nest save_lated on visited models.

1
So if you have circular relations types you need to perform the updates in a manual way.

Note that with save_all=True and follow=True you can save the entire relationship tree at once using save_lated().

Examples:

 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
class Department(ormar.Model):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    department_name: str = ormar.String(max_length=100)


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

    id: int = ormar.Integer(primary_key=True)
    course_name: str = ormar.String(max_length=100)
    completed: bool = ormar.Boolean()
    department: Optional[Department] = ormar.ForeignKey(Department)


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

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100)
    courses = ormar.ManyToMany(Course)

to_save = {
            "department_name": "Ormar",
            "courses": [
                {"course_name": "basic1",
                 "completed": True,
                 "students": [
                     {"name": "Jack"},
                     {"name": "Abi"}
                 ]},
                {"course_name": "basic2",
                 "completed": True,
                 "students": [
                     {"name": "Kate"},
                     {"name": "Miranda"}
                 ]
                 },
            ],
        }
# initialize whole tree
department = Department(**to_save)

# save all at once (one after another)
await department.save_related(follow=True, save_all=True)

department_check = await Department.objects.select_all(follow=True).get()

to_exclude = {
    "id": ...,
    "courses": {
        "id": ...,
        "students": {"id", "studentcourse"}
    }
}
# after excluding ids and through models you get exact same payload used to
# construct whole tree
assert department_check.model_dump(exclude=to_exclude) == to_save

Warning save_lated() iterates over all relationships and all models and updates them one by one, so it saves all models, but may not be optimal in terms of number of database queries.