10 KiB
Queries
QuerySet
Each Model is auto registered with a QuerySet that represents the underlaying query and it's options.
Most of the methods are also available through many to many relation interface.
Given the Models like this
--8<-- "../docs_src/queries/docs001.py"
we can demonstrate available methods to fetch and save the data into the database.
create
create(**kwargs): -> Model
Creates the model instance, saves it in a database and returns the updates model (with pk populated if not passed and autoincrement is set).
The allowed kwargs are Model fields names and proper value types.
malibu = await Album.objects.create(name="Malibu")
await Track.objects.create(album=malibu, title="The Bird", position=1)
The alternative is a split creation and persistence of the Model.
malibu = Album(name="Malibu")
await malibu.save()
!!!tip
Check other Model methods in models
get
get(**kwargs): -> Model
Get's the first row from the db meeting the criteria set by kwargs.
If no criteria set it will return the first row in db.
Passing a criteria is actually calling filter(**kwargs) method described below.
track = await Track.objects.get(name='The Bird')
# note that above is equivalent to await Track.objects.filter(name='The Bird').get()
track2 = track = await Track.objects.get()
track == track2 # True since it's the only row in db in our example
!!!warning
If no row meets the criteria NoMatch exception is raised.
If there are multiple rows meeting the criteria the `MultipleMatches` exception is raised.
get_or_create
get_or_create(**kwargs) -> Model
Combination of create and get methods.
Tries to get a row meeting the criteria and if NoMatch exception is raised it creates a new one with given kwargs.
album = await Album.objects.get_or_create(name='The Cat')
# object is created as it does not exist
album2 = await Album.objects.get_or_create(name='The Cat')
assert album == album2
# return True as the same db row is returned
!!!warning
Despite being a equivalent row from database the album and album2 in example above are 2 different python objects!
Updating one of them will not refresh the second one until you excplicitly load() the fresh data from db.
!!!note Note that if you want to create a new object you either have to pass pk column value or pk column has to be set as autoincrement
update
update(each: bool = False, **kwargs) -> int
QuerySet level update is used to update multiple records with the same value at once.
You either have to filter the QuerySet first or provide a each=True flag to update whole table.
If you do not provide this flag or a filter a QueryDefinitionError will be raised.
Return number of rows updated.
import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Book(ormar.Model):
class Meta:
tablename = "books"
metadata = metadata
database = database
id: ormar.Integer(primary_key=True)
title: ormar.String(max_length=200)
author: ormar.String(max_length=100)
genre: ormar.String(max_length=100, default='Fiction', choices=['Fiction', 'Adventure', 'Historic', 'Fantasy'])
await Book.objects.create(title='Tom Sawyer', author="Twain, Mark", genre='Adventure')
await Book.objects.create(title='War and Peace', author="Tolstoy, Leo", genre='Fiction')
await Book.objects.create(title='Anna Karenina', author="Tolstoy, Leo", genre='Fiction')
# queryset needs to be filtered before deleting to prevent accidental overwrite
# to update whole database table each=True needs to be provided as a safety switch
await Book.objects.update(each=True, genre='Fiction')
all_books = await Book.objects.filter(genre='Fiction').all()
assert len(all_books) == 3
update_or_create
update_or_create(**kwargs) -> Model
bulk_create
bulk_create(objects: List["Model"]) -> None
Allows you to create multiple objects at once.
A valid list of Model objects needs to be passed.
import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class ToDo(ormar.Model):
class Meta:
tablename = "todos"
metadata = metadata
database = database
id: ormar.Integer(primary_key=True)
text: ormar.String(max_length=500)
completed: ormar.Boolean(default=False)
# create multiple instances at once with bulk_create
await ToDo.objects.bulk_create(
[
ToDo(text="Buy the groceries."),
ToDo(text="Call Mum.", completed=True),
ToDo(text="Send invoices.", completed=True),
]
)
todoes = await ToDo.objects.all()
assert len(todoes) == 3
bulk_update
bulk_update(objects: List["Model"], columns: List[str] = None) -> None
Allows to update multiple instance at once.
All Models passed need to have primary key column populated.
You can also select which fields to update by passing columns list as a list of string names.
# continuing the example from bulk_create
# update objects
for todo in todoes:
todo.completed = False
# perform update of all objects at once
# objects need to have pk column set, otherwise exception is raised
await ToDo.objects.bulk_update(todoes)
completed = await ToDo.objects.filter(completed=False).all()
assert len(completed) == 3
delete
delete(each: bool = False, **kwargs) -> int
QuerySet level delete is used to delete multiple records at once.
You either have to filter the QuerySet first or provide a each=True flag to delete whole table.
If you do not provide this flag or a filter a QueryDefinitionError will be raised.
Return number of rows deleted.
import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Book(ormar.Model):
class Meta:
tablename = "books"
metadata = metadata
database = database
id: ormar.Integer(primary_key=True)
title: ormar.String(max_length=200)
author: ormar.String(max_length=100)
genre: ormar.String(max_length=100, default='Fiction', choices=['Fiction', 'Adventure', 'Historic', 'Fantasy'])
await Book.objects.create(title='Tom Sawyer', author="Twain, Mark", genre='Adventure')
await Book.objects.create(title='War and Peace in Space', author="Tolstoy, Leo", genre='Fantasy')
await Book.objects.create(title='Anna Karenina', author="Tolstoy, Leo", genre='Fiction')
# delete accepts kwargs that will be used in filter
# acting in same way as queryset.filter(**kwargs).delete()
await Book.objects.delete(genre='Fantasy') # delete all fantasy books
all_books = await Book.objects.all()
assert len(all_books) == 2
all
Returns all rows from a database for given model
tracks = await Track.objects.select_related("album").all()
# will return a list of all Tracks
filter
Allows you to filter by any Model attribute/field
as well as to fetch instances, with a filter across an FK relationship.
track = Track.objects.filter(name="The Bird").get()
# will return a track with name equal to 'The Bird'
tracks = Track.objects.filter(album__name="Fantasies").all()
# will return all tracks where the columns album name = 'Fantasies'
You can use special filter suffix to change the filter operands:
- exact - like
album__name__exact='Malibu'(exact match) - iexact - like
album__name__iexact='malibu'(exact match case insensitive) - contains - like
album__name__conatins='Mal'(sql like) - icontains - like
album__name__icontains='mal'(sql like case insensitive) - in - like
album__name__in=['Malibu', 'Barclay'](sql in) - gt - like
position__gt=3(sql >) - gte - like
position__gte=3(sql >=) - lt - like
position__lt=3(sql <) - lte - like
position__lte=3(sql <=)
!!!note
filter(), select_related(), limit() and offset() returns a QueySet instance so you can chain them together.
Something like `Track.object.select_related("album").filter(album__name="Malibu").offset(1).limit(1).all()`
exclude
select_related
Allows to prefetch related models.
To fetch related model use ForeignKey names.
To chain related Models relation use double underscore.
album = await Album.objects.select_related("tracks").all()
# will return album will all columns tracks
You can provide a string or a list of strings
classes = await SchoolClass.objects.select_related(
["teachers__category", "students"]).all()
# will return classes with teachers and teachers categories
# as well as classes students
!!!warning
If you set ForeignKey field as not nullable (so required) during
all queries the not nullable Models will be auto prefetched, even if you do not include them in select_related.
!!!note
filter(), select_related(), limit() and offset() returns a QueySet instance so you can chain them together.
Something like `Track.object.select_related("album").filter(album__name="Malibu").offset(1).limit(1).all()`
limit
You can limit the results to desired number of rows.
tracks = await Track.objects.limit(1).all()
# will return just one Track
!!!note
filter(), select_related(), limit() and offset() returns a QueySet instance so you can chain them together.
Something like `Track.object.select_related("album").filter(album__name="Malibu").offset(1).limit(1).all()`
offset
You can also offset the results by desired number of rows.
tracks = await Track.objects.offset(1).limit(1).all()
# will return just one Track, but this time the second one
count
exists
fields
!!!note
filter(), select_related(), limit() and offset() returns a QueySet instance so you can chain them together.
Something like `Track.object.select_related("album").filter(album__name="Malibu").offset(1).limit(1).all()`