Django ORM

ORM (Object Relational Mapping) является одной из наиболее важных функций, предоставляемых веб-фреймворком Django. ORM используется для взаимодействия с различными реляционными базами данных, такими как SQLite, PostgreSQL и MySQL, через наборы запросов.

Чтобы узнать больше об ORM, нажмите здесь

Операции CRUD

  1. Создать объект

  2. Извлечение объекта

  3. Обновить объект

  4. Удалить объект

Примерная модель

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from django.db import models

class Viewer(models.Model):
name = models.CharField(max_length = 255)
last_name = models.CharField(max_length = 255)


class Author(models.Model):
name = models.CharField(max_length = 255)
last_name = models.CharField(max_length = 255)

class Post(models.Model):
title = models.CharField(max_length = 255)
author = models.ForeignKey(Author,
related_name = "posts",
on_delete=models.CASCADE,
null=True, blank=True)
viewers = models.ManyToManyField(Viewer,
related_name="posts",
null=True, blank=True)

1. Создайте объект

Существует три способа создать новый объект в базе данных.

  1. create() method
  2. save() method
  3. get_or_create()

метод create()

1
2
3
4
Post.objects.create(title=“i love Django”)
# or
data = {'title': 'i love Django'}
Post.objects.create(**data)

метод save()

Чтобы создать объект, создайте его экземпляр, используя аргументы ключевого слова для класса модели, затем вызовите _save()_, чтобы сохранить его в базе данных.

1
2
3
4
5
6
post = Post(title="i love Django”)
post.save()
# Or
post = Post()
post.title = “i love Django”
post.save()

Django не попадает в базу данных, пока вы явно не вызовете _save()_ .

Метод _save()_ не имеет возвращаемого значения.

Сохранение ForeignKey

1
2
3
author_1 = Author.objects.get(id=1)
post = Post(title="i love Django", author=author_1)
post.save()

Сохранение ManyToManyField

1
2
3
4
5
6
7
8
9
10
_viewers = Viewer.objects.get(id=1)
post = Post(title="i love Django")
post.save()
post.viewers.add(_viewers)

# If you want to save multiple Viewer in a Post then
_viewers = Viewer.objects.filter(id__lte=2).values_list('id', flat=True)
post = Post(title="i love Django")
post.save()
post.viewers.add(* _viewers)

get_or_create()

метод get_or_create() сначала ищет объект с заданными параметрами kwargs и создает его, если не найден.
Возвращает кортеж (object, created), где object - это извлеченный или созданный объект, а created - логическое значение, указывающее, был ли создан новый объект.

Синтаксис

1
get_or_create(defaults=None, **kwargs)

Пример

1
obj, created = Post.objects.get_or_create(title="i love Django")

default используется, когда вы не хотите сравнивать значение, чтобы получить объект

1
obj, created = Author.objects.get_or_create(name=“Jone”, middle_name="Ram", defaults={‘last_name': “Bob”})

В приведенном выше примере он будет искать Author с name “Jone” и middle_name “Ram”, а не искать last_name.

Если он найдет его, он вернет этот объект, иначе создаст нового Author с name “Jone”, middle_name “Ram” и last_name “Bob”

2. Извлечение объекта

Чтобы получить данные из базы данных, вам необходимо создать Queryset запросов через Manger в классе model.

Методы набора запросов (QuerySet Methods)

Наиболее часто используемыми методами набора запросов являются:

  1. all()
  2. get()
  3. filter()
  4. exclude()

all()

метод all() возвращает набор запросов всех объектов в базе данных.

1
2
# get all the post
Post.objects.all()

Ограничение наборов запросов

Используйте подмножество синтаксиса нарезки массивов Python, чтобы ограничить ваш QuerySet запросов определенным количеством результатов. Это эквивалент предложений SQL LIMIT и OFFSET .

1
2
3
4
5
6
7
8
# return first 3 objects (LIMIT 3)
Post.objects.all()[:3]

# return the sixth through tenth objects (OFFSET 5 LIMIT 5):
Post.objects.all()[5:10]

# return first object
Post.objects.order_by(‘title’)[0]

get()


метод get() возвращает только один объект, соответствующий заданному запросу.

1
2
3
4
5
6
# retrieve a single post where id is 21
post = Post.objects.get(id=21)

# get post fields data
post.title
post.author.name

Если нет сообщения с идентификатором 21, то метод get() вызовет исключение DoesNotExist.

1
2
3
4
try: 
Post.objects.get(id=21)
except Post.DoesNotExist:
print('post not found’)

Если более одного элемента соответствует запросу, то это вызовет исключение MultipleObjectsReturned.

1
2
3
4
5
try:
Post.objects.get(title__contains="Django")
except Post.MultipleObjectsReturned:
print(‘Multiple Post found’)

get_object_or_404( )

метод get_object_or_404() сначала ищет объект с заданными kwargs, и если он не найден, то выдает ошибку Http404.

1
2
3
4
from django.shortcuts import get_object_or_404

def my_view(request):
obj = get_object_or_404(Post, pk=1)

filter ( )

метод filter() возвращает объекты набора запросов, которые соответствуют заданному запросу.

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
# search posts where title contains Django
Post.objects.filter(title__contains=“Django”)

# case-sensitive comparison where title exactly is Django
Post.objects.filter(title__exact=“Django”)

# case-insensitive comparison where title exactly is Django
Post.objects.filter(title__iexact=“Django”)

# relational operator
# filter post where publish date is greater than 3-1-2021
Post.objects.filter(pub_date__gt=datetime.date(2021, 1, 3))

# filter post where publish date is greater than or equal to than 3-1-2021
Post.objects.filter(pub_date__gte=datetime.date(2021, 1, 3))

# filter post where publish date is less than than 3-1-2021
Post.objects.filter(pub_date__lt=datetime.date(2021, 1, 3))

# filter post where publish date is less than or equal to than 3-1-2021
Post.objects.filter(pub_date__lte=datetime.date(2021, 1, 3))

# search Posts where title start with Django
Post.objects.filter(title__startswith=“Django”)

# search Posts where title ends with Django
Post.objects.filter(title__endswith=“Django”)

# get all posts where title is null
Post.objects.filter(title__isnull=True)

# _in is used to filter on multiple values.
Post.objects.filter(id__in=[1, 3, 7])

exclude( )

возвращать объекты QuerySet, которые не соответствуют заданным параметрам

1
2
# exclude post where publish date is greater than 3-1-2021
Post.objects.exclude(pub_date__gt=datetime.date(2021, 1, 3))

Другие важные методы набора запросов

first ( )

метод first() вернет один первый объект набора данных.

1
2
# retrieve fist post, return single object
Post.objects.first()

last( )

метод last() вернет один последний объект набора данных.

1
2
# retrieve last post, return single object
Post.objects.last()

exists ( )

метод exists() вернет True, если набор запросов содержит какой-либо результат, и False, если нет.

1
2
Post.objects.filter(title__isnull=True).exists()
>>> True

latest( )

Возвращает последний объект в таблице на основе заданных полей.

1
2
3
Post.objects.latest(‘pub_date’)

Post.objects.latest('pub_date', '-expire_date')

values ( )

метод values() используется, когда вы хотите выбрать несколько столбцов из таблицы.

метод values() возвращает набор запросов в виде dictionaries, а не экземпляра модели.

1
2
3
4
5
6
7
8
Post.objects.filter(title__isnull=True).values(‘id’, ‘title’)
>>> <QuerySet [{'id': 1, 'title': ‘title_1’}, {'id': 2, 'title': ‘title_2‘}]>

# get values with relationships
Post.objects.filter(title__isnull=True).values(‘id’, ‘author__name’)

# To get a values() of a single instance
Post.objects.values(‘id’, ‘author__name’).get(id=1)

values_list( )

метод values_list() также используется для выбора нескольких столбцов из таблицы. Но values_list() возвращает QuerySet запросов в виде tuples.

1
2
Post.objects.filter(title__isnull=True).values_list(‘id’, ‘title’)
>>> <QuerySet [(‘id': 1, 'title': ‘title_1’), (‘id': 2, 'title': ‘title_2’)]>

Если вы установите flat=True, то он будет возвращать результаты с одиночными значениями, а не с одним кортежем.

1
2
Post.objects.filter(title__isnull=True).values_list(‘id’, flat=True)
>>> <QuerySet [1, 2, 3]>

flat=True может использоваться только в том случае, если вы передаете одно поле. Если вы передадите более одного файла, Django выдаст сообщение об ошибке.
Если вы установите named=True то результатом будет namedtuple()

1
2
Post.objects.filter(title__isnull=True).values_list(‘id’, ‘title’, named=True)
>>> <QuerySet [Row(id=1, title=‘title_1'), Row(id=2, title=‘title_2')]>

Чтобы получить values_list() одного экземпляра

1
Post.objects.values_list(‘id’, ‘title’).get(id=1)

distinct( )

метод distinct() устраняет дублирующиеся данные из набора запросов.

1
2
3
4
5
6
7
8
Post.objects.distinct()

Post.objects.order_by(‘title’).distinct(‘title’)

Post.objects.order_by(‘title’, ‘author__name’).distinct(‘title’, ‘author__name’)

Post.objects.values(‘title’).distinct()

reverse( )

метод reverse() для изменения порядка, в котором возвращаются элементы набора запросов.

1
my_queryset.reverse()

order_by( )

Наборы запросов также позволяют упорядочивать список объектов с помощью метода order_by().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Post.objects.order_by(‘title’)

# You can also reverse the ordering by adding - in the beginning
Post.objects.order_by(‘-title’)

# To order randomly you can use ?
Post.objects.order_by(‘?’)

# To order by a field in a different model,
Post.objects.order_by(‘author__name’)
# ordering two fileds
Post.objects.order_by(‘title’, ’author__name’)

# how not to order two fileds, Each order call will clear previous ordering.
Post.objects.order_by(‘title’).order_by(’author__name’)

метод select_related() - это усилитель производительности, который возвращает набор запросов, которые соответствуют связям внешнего ключа, выбирая дополнительные данные связанных объектов при выполнении своего запроса.

1
2
3
4
5
6
7
8
9
10
11
12
# without select_related
post = Post.objects.get(pk=1) # Hit Database
post.author # Hit Database

# with select_related

# Hit Database with join to the author table
post = Post.objects.select_related(‘author’).get(pk=1)

# Doesn't hit the database, because post.author has been prepopulated
# in the previous query.
post.author

select_related() ограничен однозначными отношениями — внешним ключом и один-к-одному и не может использоваться для отношений «многие-ко-многим».

Возвращает QuerySet, который автоматически извлекает в одном пакете связанные объекты для каждого из указанных поисковых запросов.

1
2
3
# viewers is in many-to-many relationship with post
post = Post.objects.prefetch_related(‘viewers').get(id=1)
post.viewers.all() # Doesn't hit the database

функция prefetch_related() может использоваться для отношений внешнего ключа, “один к одному” и “многие ко многим”.

select_related() и prefetch_related() повышают производительность и предназначены для предотвращения потока запросов к базе данных, вызванных доступом к связанным объектам, но стратегия совершенно другая.

select_related() работает, создавая SQL INNER JOIN и включая поля связанного объекта в оператор SELECT.

prefetch_related(), с другой стороны, выполняет отдельный поиск для каждой связи и выполняет “joining” в Python.

1
2
3
4
5
Post.objects.select_related(‘author’).all()

# SQL query

SELECT "myapp_post"."id", "myapp_post"."title", "myapp_post"."author_id", "myapp_author"."id", “myapp_author"."name" FROM "myapp_post" INNER JOIN "myapp_author" ON (“myapp_post"."author_id" = "myapp_author"."id")
1
2
3
4
5
Post.objects.prefetch_related(‘viewers’).all()

# SQL query

SELECT "myapp_post"."id", "myapp_post"."title", "myapp_post"."author_id" FROM “myapp_post"

aggregate ( )

Возвращает словарь совокупных значений (средние значения, суммы и т. д.), рассчитанных по QuerySet.

Каждый аргумент aggregate() указывает значение, которое будет включено в возвращаемый словарь.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Return Count of all the viewers on all the posts

from django.db.models import Count
Post.objects.aggregate(total_viewers_count=Count("viewers"))
>>> {"total_viewers_count": 6}

# add filter in query
Post.objects.filter(id__in=[3, 6, 8]).aggregate(total_viewers_count=Count("viewers"))
>>> {"total_viewers_count": 4}

# Return maximum, minimum and average viewers on posts

from django.db.models import Max, Min, Avg
Post.objects.aggregate(Max("viewers"), Min("viewers"), Avg("viewers"))
>>> {"viewers__max": 4, "viewers__min": 1, 'viewers__avg': 2.0}


# Get difference between maximum viewers count and minimum viewers count

from django.db.models import Max, Min
Post.objects.aggregate(viewers_diff=Max("viewers") - Min("viewers"))
>>> {"viewers_diff": 3}

Чтобы узнать больше, нажмите здесь

Aggregatev функции

Django включает в себя семь агрегатных функций:

  • Avg Возвращает среднее значение выражения.
  • Count. Подсчитывает количество возвращенных объектов.
  • Max. Возвращает максимальное значение выражения.
  • Min. Возвращает минимальное значение выражения.
  • StdDev. Возвращает стандартное отклонение совокупности данных в выражении.
  • Sum. Возвращает сумму всех значений в выражении.
  • Variance. Возвращает дисперсию совокупности данных в выражении.

annotate( )

Аннотирует каждый объект в QuerySet предоставленным списком выражений запроса.

1
2
3
4
5
6
7
8
9
10
# Return Count of all the viewers on Post

from django.db.models import Count
Post.objects.annotate(viewers_count=Count(‘viewers’))[2].__dict__
>>> {
id’: 2,
‘title': 'p3',
'author_id': 3,
'viewers_count': 4
}

метод annotate() подсчитывает количество viewers для каждого Post и аннотирует количество для каждого объекта QuerySet. Где Aggregate подсчет просмотров для всех сообщений в наборе запросов.

extra ( )

метод extra() используется для переименования столбцов в ORM.

1
2
Post.objects.extra(select={'post_title': 'title'}).values('id', ‘post_title')[0]
>>> {'post_title': 'title_name_1', 'id': 1}

Иногда синтаксис запроса Django сам по себе не может легко выразить сложное предложение WHERE. Для этих крайних случаев Django предоставляет модификатор extra() QuerySet — хук для внедрения определенных предложений в SQL, сгенерированный QuerySet.

1
2
Post.objects.extra(select={'post_author_name': 'Select name FROM myapp_author where myapp_author.id = myapp_post.author_id'}).values('title', 'post_author_name')[0] 
>>> {'post_author_name': ‘Jone', 'title': ‘title_name_1’}

Чтобы узнать больше, нажмите здесь

Сложные запросы с Q объектами


Объекты Q используются для операций AND , OR и NOT.

Вы можете импортировать объект Q из django.db.models

1
from django.db.models import Q

OR

| используется для операции OR между 2 Q выражениями.

1
2
3
4
# return post where title contains Django or python
Post.objects.filter(Q(title__icontains="django") | Q(title__icontains=“python”))

Post.objects.filter(Q(title__icontains=“django”), Q(author_id=2) | Q(title__icontains=“python"))

AND

& используется для операции AND между 2 Q выражениями.

1
2
# return post where title contains Django and python
Post.objects.filter(Q(title__icontains="django") & Q(title__icontains=“python”))

NOT

~ используется для NOT операции между 2 Q выражениями.

1
2
# return post where title contains Django and not python
Post.objects.filter(Q(title__icontains="django") & ~Q(title__icontains="python"))

Выражения запросов

Выражения запроса описывают вычисление или значение, используемое как часть другого запроса. Встроенные выражения запросов:

  • F(). Представляет значение поля модели или аннотированного столбца.
  • Func(). Базовый тип для функций базы данных, таких как LOWER и SUM.
  • Aggregate(). Все агрегатные функции наследуются от Aggregate().
  • Value(). Значение выражения. Не используется напрямую.
  • ExpressionWrapper(). Используется для переноса выражений разных типов.
  • SubQuery(). Добавьте подзапрос к набору запросов.

Выражения F()

Выражения F() используются для ссылки на значения полей модели непосредственно в базе данных.

1
User.objects.annotate(_age=F(‘age’)*2).values(‘id’,’first_name’,’_age’)

3. Обновить объект

Обновить один объект

1
2
3
post = Post.objects.get(id=1)
post.title = “new title”
post.save()

Обновить объект ForeignKey

1
2
3
4
5
post = Post.objects.get(id=1)
post.title = “new title”
post.author.name = “new name”
post.save()
post.author.save()

Обновить объект ManyToManyKey

1
2
post = Post.objects.get(id=1)
post.viewers.update(name=“new name")

Обновить несколько объектов

1
Post.objects.filter(author__id__in=[1,2]).update(title="new title”)

update_or_create( )

метод update_or_create(), используемый для обновления объекта с помощью заданных kwargs, создавая новый, если это необходимо.

Возвращает кортеж (object, created), где object - это созданный или обновленный объект, а created - логическое значение, указывающее, был ли создан новый объект.

Метод update_or_create пытается извлечь объект из базы данных на основе заданных kwargs. Если совпадение найдено, оно обновляет поля, переданные в словаре по defaults.

1
obj, created = Author.objects.update_or_create(name=‘Jone’, defaults={'last_name': 'Bob'})

В приведенном выше примере он будет искать Author с именем “Jone”, и если он будет найден, он обновит last_name до “Bob”. И созданная переменная будет иметь значение False. Если есть два или более автора с именем “Jone”. Это вызовет ошибку.

Если Author с именем “Jone” не найден, то будет создан новый Author с именем “Jone” и фамилией “Bob”. И созданная переменная будет иметь значение True.

4. Удалить объект

Удалить один объект

1
2
post = Post.objects.get(id=1)
post.delete()

Удаление нескольких объектов

1
Post.objects.filter(author__id__in=[1,2]).delete()