Джанго. Менеджеры модели.

Джанго. Менеджеры модели.

Менеджеры модели предоставляют интерфейс для создания запросов к базе данных.

Класс менеджера формирует SQL запросы.

Установим дебагер для просмотра этих запросов.

ссылка на документацию

Каждый объект модели содержит менеджер и умолчанию он имеет имя objects.

Однако есть возможность изменить это имя атрибутом типа models.Manager() .

from django.db import models

class Person(models.Model):
    #...
    people = models.Manager()

При этом станет невозможным использовать имя objects.

Базовые функции Manager-ов.

ссылка на документацию

Создание объекта.

Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
b.save()

В одну строку.

Blog.objects.save(**kwargs)

Связанные объекты.

При связи один-ко-многим при помощи внешнего ключа мы присваеваем значению поля ForeignKey объект модели.

>>> from blog.models import Blog, Entry
>>> entry = Entry.objects.get(pk=1)
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()

При связи многие-ко-многим например:

authors = models.ManyToManyField(Author)

Мы используем встроенный метод add.

>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe)

Получение всех объектов.

Entry.objects.all()

Фильтрация объектов.

Model.objects.filter(kwargs)** - возвращает список при совпадении

Model.objects.exclude(kwargs)** - возвращает список при НЕ совпадении

При выборке объектов мы не можем использовать нечто вроде filter(name!=’Дима’) т.к. внутрь поступает приравненные переменные.

Поэтому если условие посложнее мы используем разные суфиксы, добавляя их к имени поля через двойное подчеркивание.

Entry.objects.all().filter(pub_date__year=2006)

q1.filter(pub_date__gte=datetime.date.today())

__gt - больше чем

__lt - больше чем

__gte - больше чем или равно

__lte - меньше чем или равно

Получение одной записи.

Entry.objects.get(pk=1)
Blog.objects.get(id__exact=14)  # Explicit form
Blog.objects.get(id=14)         # __exact is implied

Entry.objects.get(headline__exact="Cat bites dog")

Нечуствительный к регистру.

Blog.objects.get(name__iexact="beatles blog")

Получение среза (лимитирование).

Entry.objects.all()[5:10]

Сортировка.

Entry.objects.order_by('id')
Entry.objects.order_by('-id')

Поиск подстроки.

Entry.objects.get(headline__contains='Lennon')

Поиск в зависимых записях.

Джанго имеет интуитивный механиз моиска по связанным объектам модели.

Entry.objects.filter(blog__name='Beatles Blog')
Blog.objects.filter(entry__headline__contains='Lennon')

Поиск нулевых значений.

Blog.objects.filter(entry__authors__name__isnull=True)

Цепочки фильров.

Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)

Join-ы

UserFeed.objects.filter(user__gender='female').select_related('user')

Сырые запросы.

Person.objects.raw('SELECT * FROM myapp_person'):

OR - ы.

Кастомные менеджеры.

Мы можем создавать и использовать свои собственные классы менеджера если унаследуемся от models.Manager.

from django.db import models

class PollManager(models.Manager):
    def with_counts(self):
        from django.db import connection
        with connection.cursor() as cursor:
            cursor.execute("""
                SELECT p.id, p.question, p.poll_date, COUNT(*)
                FROM polls_opinionpoll p, polls_response r
                WHERE p.id = r.poll_id
                GROUP BY p.id, p.question, p.poll_date
                ORDER BY p.poll_date DESC""")
            result_list = []
            for row in cursor.fetchall():
                p = self.model(id=row[0], question=row[1], poll_date=row[2])
                p.num_responses = row[3]
                result_list.append(p)
        return result_list

class OpinionPoll(models.Model):
    question = models.CharField(max_length=200)
    poll_date = models.DateField()
    objects = PollManager()

При вызове objects.all() менеджер задействует метод get_queryset() который можно переопределить.

class PublishedManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(is_published=True)


class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

    objects = PublishedManager()

Таким образом можно к примеру создать поле для удаленных записей и фильторать их менеджером на всем сайте, включая админку но физически их не удаляя из базы.

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

class AuthorManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(role='author')

class EditorManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(role='editor')

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    role = models.CharField(max_length=10, choices=[('author', 'Author'), ('editor', 'Editor')])
    people = models.Manager()
    authors = AuthorManager()
    editors = EditorManager()

В данном примере people = models.Manager() является первым менеджером, поэтому Джанга его помечает как дефолтный и использует в некоторых сервисных функциях например dumpdata.

Базовый менеджер.

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

Для этого используется экземпляр свойства Model._base_manager.

Пользовательские QuerySet-ы.

Существует возможность определять свои классы QuerySet-ов и расширять ими менеджер модели.

class PersonQuerySet(models.QuerySet):
    def authors(self):
        return self.filter(role='A')

    def editors(self):
        return self.filter(role='E')