Django shortcuts – get_or_create and update_or_create
Django shortcuts, get_or_create() , and update_or_create() methods make our web application development even faster. Django implemented these methods due to easier readability and non-repetitiveness of the code.
What is the get_or_create() method in Django?
The get_or_create() method is used to get an existing object from the database or create it if it doesn’t exist. The get _or_create() method returns a tuple. A first element is an object, and the second one boolean value (created is True, not created is False).
What is the update_or_create() method in Django?
The update_or_create() method is used to update an object from the database if the object exists. If not, the object is created from provided parameters. The update_or_create() method returns tuple. A first element is an object, a second one boolean value (created is True, updated is False).
Before we dive into examples, we should define our models.
from django.db import models class Author(models.Model): first_name = models.CharField(max_length=512) last_name = models.CharField(max_length=512) class Book(models.Model): title = models.CharField(max_length=512) author = models.ForeignKey(Author, on_delete=models.CASCADE)
Django get_or_create() method
The get_or_create() method is the model Manager method that can receive keyword arguments that represent model fields.
- defaults – if an object doesn’t exist, defaults define object fields that are saved into the database.
- kwargs – defines fields used in the lookup for the get method, and if defaults are not defined, the same fields are used to create the method.
>>> Author.objects.get_or_create(first_name="William", last_name="Shakespeare") (, True) >>> Author.objects.get_or_create(first_name="William", last_name="Shakespeare") (, False)
We get two same commands but two different outputs. First, when we called get_or_create() method ‘William Shakespeare’ was not in the database, get_or_create() created Author and returned True in tuple providing us information that Author was created. The second time we called the get_or_create() method on the same arguments. We were provided with the Author created previously. The created flag is False, providing us information that Author was not created (only fetched from the database).
The authors table in the database looks like this:
id | first_name | last_name |
1 | William | Shakespeare |
WARNING - If the field that doesn't exist is provided, we get FieldError.
Now, we can look at the defaults argument that is provided alongside kwargs. As already mentioned, defaults don’t interfere while getting an object from the database – but if an object with given arguments doesn’t exist, defaults argument ‘overrides’ arguments and creates an object with given parameters. So let’s take a look at the following example.
>>> Author.objects.get_or_create(first_name="William", last_name="Shakespeare", defaults=) (, False)
As we can see, ‘William Shakespeare’ is already in the database, and no new object is created even though we defined defaults.
What happens if author doesn’t exist?
>>> Author.objects.get_or_create(first_name="Agatha", last_name="Christie", defaults=) (, True)
‘Agatha Christie’ was not yet created when calling the get_or_create() method in Django. We can see now that the Author object is created. Let’s check the database.
id | first_name | last_name |
1 | William | Shakespeare |
2 | Miguel | de Cervantes |
There is no mention of ‘Agatha Christie’; instead, we have ‘Miguel de Cervantes’ Author defined in the defaults argument. So, defaults define object fields when no object is found from the arguments we provided.
Django update_or_create() method
As the get_or_create() method, update_or_create() is also the model Manager method, but with a slightly different purpose. The update_or_create() updates object if found; if not, it’s created. The return value is a tuple with two values, object and created boolean flag.
- defaults – Defines values of fields that should be updated (or created)
- kwargs – Defines fields used in lookup for update method, and if defaults is not defined, same fields are used for creating object.
Let’s take a look at the following example:
>>> Author.objects.update_or_create(first_name="Miguel", defaults=) (, False)
This example looked for an object with first_name ‘Miguel’, as we have this Author in our database Django proceeds to iterate over defaults argument key-value pairs and updates given object.
Our database after this change looks like this:
id | first_name | last_name |
1 | William | Shakespeare |
2 | Agatha | Christie |
WARNING - If defaults is not defined, create method uses provided keyword arguments as fields!
>>> Author.objects.update_or_create(first_name="William", last_name="Shakespeare") (, True)
As seen in the example above, we can use update_or_create() in Django to create new objects without using the defaults argument. However, this doesn’t use its purpose because no object will be updated without defaults.
Recent Posts
Python get or create django
Рассмотрим добавление в базу данных и получение из нее на примере модели Person :
from django.db import models class Person(models.Model): name = models.CharField(max_length=20) age = models.IntegerField()
Добавление данных
create
Для добавления данных применяется метод create() :
tom = Person.objects.create(name="Tom", age=23)
Если добавление пройдет успешно, то объект будет иметь id, который можно получить через tom.id .
Асинхронная версия метода — acreate
from .models import Person import asyncio async def acreate_person(): person = await Person.objects.acreate(name="Tim", age=26) print(person.name) # запускаем асинхронную функцию acreate_person asyncio.run(acreate_person())
save
Однако в своей сути метод create() использует другой метод — save() , который мы также можем использовать отдельно для добавления объекта:
tom = Person(name="Tom", age=23) tom.save()
После успешного добавления также можно получить идентификатор добавленной записи с помощью tom.id .
bulk_create()
Метод bulk_create() (и его асинхронная версия abulk_create() ) позволяет добавить набор объектов, который передается в в метод в качестве параметра:
from .models import Person people = Person.objects.bulk_create([ Person(name="Kate", age=24), Person(name="Ann", age=21), ]) for person in people: print(f". ")
Получение из бд
Получение одного объекта
Метод get() возвращает один объект по определенному условию, которое передается в качестве параметра:
tom = Person.objects.get(name="Tom") # получаем запись, где name="Tom" bob = Person.objects.get(age=23) # получаем запись, где age=42
При использовании этого метода надо учитывать, что он предназначен для выборки таких объектов, которые имеются в единичном числе в базе данных. Если в таблице не окажется подобного объекта, то мы получим ошибку имя_модели.DoesNotExist . Если же в таблице будет несколько объектов, которые соответствуют условию, то будет сгенерированно исключение MultipleObjectsReturned . Поэтому следует применять данный метод с осторожностью, либо применять обработку соответствующих исключений:
from .models import Person from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned try: tom = Person.objects.get(name="Tom") # MultipleObjectsReturned alex = Person.objects.get(name="Alex") # ObjectDoesNotExist except ObjectDoesNotExist: print("Объект не сушествует") except MultipleObjectsReturned: print("Найдено более одного объекта")
Асинхронная версия метода называется aget :
from .models import Person import asyncio async def get_person(): person = await Person.objects.aget(id=1) print(person.name) # запускаем асинхронную функцию get_person asyncio.run(get_person())
get_or_create
Метод get_or_create() (и его асинхронная версия aget_or_create ) возвращает объект, а если его нет в бд, то добавляет в бд новый объект.
bob, created = Person.objects.get_or_create(name="Bob", age=24) print(created) print(bob.name) print(bob.age)
В данном случае, если в таблице нет объекта со значениями name=»Bob» и age=24 , то он добавляется. Если есть, то он возвращается.
Метод возвращает добавленный объект (в данном случае переменная bob) и булевое значение (created), которое хранит True, если добавление прошло успешно.
Стоит учитывать, что если в таблице уже есть несколько объектов (два и больше) с указанными значениями, то сгенерируется исключение MultipleObjectsReturned.
all()
Если необходимо получить все имеющиеся объекты, то применяется метод all() :
Данный метод возвращает объект типа QuerySet .
filter()
Если надо получить все объекты, которые соответствуют определенному критерию, то применяется метод filter() , который в качестве параметра принимает критерий выборки:
people = Person.objects.filter(age=23) # использование нескольких критериев people2 = Person.objects.filter(name="Tom", age=23)
Метод filter позволяет определять более сложные условия, но поскольку это отдельная большая тем, то подробнее будет рассмотрена в отдельной статье.
exclude()
Метод exclude() позволяют исключить из выборки записи, которые соответвуют переданному в качестве параметра критерию:
# исключаем пользователей, у которых age=23 people = Person.objects.exclude(age=23)
Можно комбинировать два выше рассмотренных метода:
# выбираем всех пользователей, у которых name="Tom" кроме тех, у которых age=23 people = Person.objects.filter(name="Tom").exclude(age=23)
in_bulk()
Метод in_bulk() (и его асинхронная версия ain_bulk ) является более эффективным способом для чтения большого количества записей. В качестве параметра в него можно передать список идентификаторов объектов, которые надо получить. В качестве результата он возвращает словарь, то есть объект dict:
# получаем все объекты people = Person.objects.in_bulk() for id in people: print(people[id].name) print(people[id].age) # получаем объекты с и = Person.objects.in_bulk([1,3]) for id in people2: print(people2[id].name) print(people2[id].age)
Метод in_bulk() возвращает словарь, где ключи представляют id объектов, а значения по этим ключам — собственно эти объекты, то есть в данном случае объекты Person.
Ограничение количества
С помощью синтаксиса списков можно получить определенную порцию данных из QuerySet:
from .models import Person people = Person.objects.all()[:5]
В данном случае выбираем первые 5 объектов, что на уровне базы данных транслируется в SQL-выражение LIMIT 5
Первый параметр указывает, сколько объектов надо пропустить:
from .models import Person people = Person.objects.all()[5:10] for person in people: print(f". - ")
В данном случае пропускаем первые 5 объектов и выбираем следующие 5 объектов до 10-го индекса, что на уровне базы данных транслируется в выражение OFFSET 5 LIMIT 5