Использование related_name во view-функции
Оно не должно совпадать с другими related_name для указанной модели.
В примере из вашего кода, что я привел выше, для модели User доступно обращение к связанным объектам модели Post по имени posts . Т.о. Больше нигде вы не сможете указать related_name=’posts’ если это связь с моделью User .
Если обращение из другой модели не требуется, можно указать related_name=’+’
Зачем
related_name позволяет обращаться к из связанных объектов к тем, от которых эта связь была создана.
class Post(models.Model): text = models.TextField() pub_date = models.DateTimeField(auto_now_add=True) author = models.ForeignKey( User, on_delete=models.CASCADE, related_name='posts' ) group = models.ForeignKey(Group, on_delete=models.CASCADE, max_length=200, blank=True, null=True, related_name='group')
related_name дожно быть posts , чтобы из модели Group , а вернее из объектов этой модели, можно было достать все связанные объекты Post по логичному наименованию.
В общем должно быть так
class Post(models.Model): text = models.TextField() pub_date = models.DateTimeField(auto_now_add=True) author = models.ForeignKey( User, on_delete=models.CASCADE, related_name='posts' ) group = models.ForeignKey(Group, on_delete=models.CASCADE, max_length=200, blank=True, null=True, related_name='posts')
И тогда для группы можно будет получить все посты следущим способом
posts = group.posts.all()
Ревью
Что касается ревью, то на основе описанного выше должно получиться так
def group_posts(request, slug): group = get_object_or_404(Group, slug=slug) posts = group.posts.order_by('-pub_date')[:10] context = < 'group': group, 'posts': posts, >return render(request, 'posts/group_list.html', context)
Надеюсь объяснил доступно
Как работают select_related и prefetch_related в Django
Часто определение разницы между select_related и prefetch_related звучит как “первый для ForeignKey полей, второй для ManyToMany”, однако это описание не раскрывает суть работы этих методов. Ниже я попробовал на примерах показать разницу между этими методами и какое влияние они оказывают на сгенерированный SQL для получения данных.
TLDR: Статья будет в первую очередь полезна тем кто начинает свое знакомство с Django, а также тем, кто использует select_related/prefetch_related в ежедневной работе, но не углублялся в глубь Django.
Перед тем как пойти дальше, небольшое описание моделей, которые будут использовать для примеров кода
from django.db import models class Department(models.Model): name = models.CharField(max_length=64) description = models.TextField() class Country(models.Model): name = models.CharField(max_length=64) class City(models.Model): name = models.CharField(max_length=64) country = models.ForeignKey(Country, on_delete=models.CASCADE, related_name="cities") class Employee(models.Model): first_name = models.CharField(max_length=64) last_name = models.CharField(max_length=64) department = models.ForeignKey(Department, on_delete=models.CASCADE, related_name="employees") email = models.EmailField() city = models.ForeignKey(City, on_delete=models.CASCADE, related_name="employees")
Какую проблему решают select_related/prefetch_related
По умолчанию Django не загружает связанные объекты вместе с основным запросом, а использует ленивую загрузку и откладывает запрос в базу до обращения к связанным объектам.
Такой подход упрощает работу со связанными объектами, но так же может привести к проблеме N + 1, когда для каждой связанной сущности генерируется дополнительный запрос в базу.
Например мы хотим вывести на экран список сотрудников и отдел в котором они работают:
for employee in Employee.objects.all(): print(employee.id, employee.first_name, employee.last_name, employee.department.name)
В таком случае будет выполнен запрос на получение записей из таблице сотрудников и для каждого сотрудника будет дополнительно сформирован запрос на получение его отдела.
Решение проблемы с помощью select_related
Чтобы решить проблему выше, мы можем использовать select_related. Метод select_related загружает связанные объекты используя JOIN.
Как будет выглядеть запрос:
for employee in Employee.objects.all().select_related("department"): print(employee.id, employee.first_name, employee.last_name, employee.department.name)
SQL запрос в данном примере будет выглядеть примерно вот так:
SELECT "employee_employee"."id", "employee_employee"."first_name", "employee_employee"."last_name", "employee_employee"."department_id", "employee_employee"."email", "employee_employee"."city_id", "employee_department"."id", "employee_department"."name", "employee_department"."description" FROM "employee_employee" INNER JOIN "employee_department" ON ("employee_employee"."department_id" = "employee_department"."id")
Теперь при обращении к атрибуту department не будет создаваться дополнительный запрос в базу.
В качестве параметров select_related принимает имена ForeignKey/OneToOne полей или related_name поля OneToOne в связанной таблице. Также можно передавать имена полей в связанных через отношение внешнего ключа таблицах, например:
Employee.objects.all().select_related("city", "city__country") # или вот так Employee.objects.all().select_related("city").select_related("city__country") # или вот так Employee.objects.all().select_related("city__country")
В последнем случае данные для поля city также будут загружены.
В качестве параметра можно передать None, чтобы очистить список связанных сущностей которые нужно загрузить вместе с основным запросом. Например в данном случае вместе с основным запросом будут загружены данные только из модели City:
Employee.objects.all() .select_related("city", "city__country") .select_related(None) .select_related("city")
Минус select_related
Несмотря на то что select_related используют для оптимизаци, использование select_related также может привести замедлению выполнения запроса. Для загрузки данных select_related использует JOIN, в случае если в основной таблице записей много и они ссылаются на одни и те же данные в связанной таблице, в результирующей таблице данные будут повторяться.
Например результатом вот такого запроса:
Employee.objects.all().select_related("city", "city__country")
Может быть вот такая таблица:
Значения в колонке department__name будут постоянно повторятся. Чем больше данных мы загружаем таким образом, тем больше вероятность что базе данных потребуется дополнительная память для обработки запроса.
Альтернативный подход — prefetch_related
В отличие от select_related, prefetch_related загружает связанные объекты отдельным запросом для каждого поля переданного в качестве параметра и производит связывание объектов внутри python.
Такой подход позволяет загружать загружать объекты для ManyToMany полей и записи которые ссылаются на нашу таблицу через ForeignKey поле используя related_name.
Однако prefetch_related можно также использовать там, где мы используем select_related, чтобы загрузить связанные записи используя дополнительный запрос, вместо JOIN.
Используя prefetch_related, можно ускорить выполнение запроса из прошлого примера:
Employee.objects.all().prefetch_related("city", "city__country")
Результатом будет вот такой SQL:
SELECT "employee_employee"."id", "employee_employee"."first_name", "employee_employee"."last_name", "employee_employee"."department_id", "employee_employee"."email", "employee_employee"."city_id" FROM "employee_employee" SELECT "employee_city"."id", "employee_city"."name", "employee_city"."country_id" FROM "employee_city" WHERE "employee_city"."id" IN (22, 23, 25, 26, 27, 28) SELECT "employee_country"."id", "employee_country"."name" FROM "employee_country" WHERE "employee_country"."id" IN (4, 5, 6)
В итоге получилось 3 запроса: 1 для получения данных из основной модели, один для получения данных из модели City и один для получения данных из таблицы Country.
Не смотря на то что было выполнено 3 запроса, суммарное время может получиться меньше, чем время выполнения с использованием select_related в некоторых случаях. Главный повод задуматься об использовании prefetch_related вместо select_related это большое количество повторений в таблице которая присоединяется через JOIN.
Пример с загрузкой объектов которые ссылаются на основную модель: для каждого отдела выводим список сотрудников, используем prefetch_related для оптимизации:
for department in Department.objects.all().prefetch_related("employees"): print(department.name, "":") for employee in department.employees.all(): print(" ", employee.first_name, employee.last_name)
SELECT "employee_department"."id", "employee_department"."name", "employee_department"."description" FROM "employee_department" SELECT "employee_employee"."id", "employee_employee"."first_name", "employee_employee"."last_name", "employee_employee"."department_id", "employee_employee"."email", "employee_employee"."city_id" FROM "employee_employee" WHERE "employee_employee"."department_id" IN (13, 14, 15, 16)
Интересный момент, в случае такого комбинированного QuerySet, в итоге выполнится 2 запроса в базу, так как Django определит что объекты для отношения city были загружены через select_related:
Employee.objects.all().select_related("city").prefetch_related("city__country")
SELECT "employee_employee"."id", "employee_employee"."first_name", "employee_employee"."last_name", "employee_employee"."department_id", "employee_employee"."email", "employee_employee"."city_id", "employee_city"."id", "employee_city"."name", "employee_city"."country_id" FROM "employee_employee" INNER JOIN "employee_city" ON ("employee_employee"."city_id" = "employee_city"."id") SELECT "employee_country"."id", "employee_country"."name" FROM "employee_country" WHERE "employee_country"."id" IN (4, 5, 6)
Аналогично select_related, список отношений для загрузки можно очистить:
Employee.objects.all().prefetch_related("city", "city__country").prefetch_related(None)
Дополнительные оптимизации, объект Prefetch
Иногда мы хотим произвести дополнительную настройку объектов которые мы хотим загрузить с использованием prefetch_related. Для таких случаев Django предоставляет нам возможность передавать в качестве параметра prefetch_related специальный объект Prefetch.
Объект Prefetch получает на вход 3 параметра:
- lookup для поиска отношения, аналогично строке которую мы передаем в случае если не используем объект Prefetch;
- queryset, опциональный параметр для настройки QuerySet который будет использоваться для загрузки связанных объектов;
- to_attr, опциональный параметр с помощью которого можно изменить поле в которое будут загружены связанные объекты, загруженные таким образом объекты будут собраны в список, а не объект QuerySet.
Главное ограничение на передаваемый queryset, это запрет на использование методов values и values_list, так как в таком случае результирующим объектом перестанет быть экземпляр модели, однако можно применять фильтрации и аннотации как и к обычному queryset.
В качестве примера загрузим список сотрудников для каждого департамента, которые живут в определенной стране и используем аннотацию для добавления информации о городе, а также исключим поле email, которое мы не используем в нашем скрипте:
employees = Employee.objects.defer("email") .filter(city__country_id=4) .annotate(city_name=F("city__name")) queryset = Department.objects.all().prefetch_related(Prefetch("employees", queryset=employees)) for department in queryset: print(department.name, ":") for employee in department.employees.all(): print(" ", employee.first_name, employee.last_name, employee.city_name)
SELECT "employee_department"."id", "employee_department"."name", "employee_department"."description" FROM "employee_department" SELECT "employee_employee"."id", "employee_employee"."first_name", "employee_employee"."last_name", "employee_employee"."department_id", "employee_employee"."city_id", "employee_city"."name" AS "city_name" FROM "employee_employee" INNER JOIN "employee_city" ON ("employee_employee"."city_id" = "employee_city"."id") WHERE ("employee_city"."country_id" = 4 AND "employee_employee"."department_id" IN (13, 14, 15, 16))
Как случайно поломать оптимизацию
Используя prefetch_related нужно помнить о том что мы работаем с объектами QuerySet, чтобы избежать неожиданного поведения.
Фильтрация загруженных объектов приведет к инвалидации кеша на уровне объекта QuerySet, а значит приведет к дополнительному запросу.
В примере ниже будет выполнен дополнительный запрос для каждого объекта department:
for department in Department.objects.all().prefetch_related("employees"): print(department.name, ":") for employee in department.employees.filter(city__country_id=4): print(" ", employee.first_name, employee.last_name)
Так как в случае prefetch_related связывание объектов происходит на уровне python, исключение поля содержащего ссылку на основную модель с помощью методов only/defer приведет к дополнительным запросом для получения этого поля для каждого загруженного объекта в момент связывания.
Например в случае такого скрипта:
employees = Employee.objects.only("first_name", "last_name") .filter(city__country_id=4) .annotate(city_name=F("city__name")) queryset = Department.objects.all().prefetch_related(Prefetch("employees", queryset=employees)) for department in queryset: print(department.name, ":") for employee in department.employees.all(): print(" ", employee.first_name, employee.last_name, employee.city_name)
Будет сделано 2 запроса для загрузки объектов Department и Employee
SELECT "employee_department"."id", "employee_department"."name", "employee_department"."description" FROM "employee_department" SELECT "employee_employee"."id", "employee_employee"."first_name", "employee_employee"."last_name", "employee_city"."name" AS "city_name" FROM "employee_employee" INNER JOIN "employee_city" ON ("employee_employee"."city_id" = "employee_city"."id") WHERE ("employee_city"."country_id" = 4 AND "employee_employee"."department_id" IN (13, 14, 15, 16))
и множество одинаковых запросов для получения department_id:
SELECT "employee_employee"."id", "employee_employee"."department_id" FROM "employee_employee" WHERE "employee_employee"."id" = 63265 LIMIT 21
Заключение
В заключение хочу сказать что описанные выше примеры покрывают основные случаи использования select_related/prefetch_related.
Более детальное описание методов и объекта Prefetch можно найти в документации:
Модели ¶
Модель — это единственный исчерпывающий источник информации о ваших данных. Он содержит основные поля и поведение данных, которые вы храните. Как правило, каждая модель отображается в одну таблицу базы данных.
- Каждая модель представляет собой подклассы Python django.db.models.Model .
- Каждый атрибут модели представляет собой поле базы данных.
- При всем этом Django предоставляет автоматически сгенерированный API доступа к базе данных; см. Выполнение запросов .
Быстрый пример ¶
В этом примере модели определяется a Person , у которого есть first_name и last_name :
from django.db import models class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30)
first_name и last_name являются полями модели. Каждое поле указывается как атрибут класса, и каждый атрибут сопоставляется со столбцом базы данных.
Вышеупомянутая Person модель создаст такую таблицу базы данных:
CREATE TABLE myapp_person ( "id" serial NOT NULL PRIMARY KEY, "first_name" varchar(30) NOT NULL, "last_name" varchar(30) NOT NULL );
Некоторые технические примечания:
- Имя таблицы myapp_person автоматически получается из некоторых метаданных модели, но может быть переопределено. Дополнительные сведения см. В разделе Имена таблиц .
- id Поле добавляется автоматически, но это поведение может быть переопределены. См. Автоматические поля первичного ключа .
- SQL в этом примере отформатирован с использованием синтаксиса PostgreSQL, но стоит отметить , Django использует SQL специально для хранения базы данных , указанных в вашем файле настроек . CREATE TABLE
Использование моделей ¶
После того, как вы определили свои модели, вам нужно сообщить Django, что вы собираетесь использовать эти модели. Сделайте это, отредактировав файл настроек и изменив INSTALLED_APPS настройку, добавив имя модуля, который содержит ваш models.py .
Например, если модели для вашего приложения myapp.models находятся в модуле (структура пакета, которая создается для приложения с помощью сценария), следует частично читать: manage.py startapp INSTALLED_APPS
INSTALLED_APPS = [ #. 'myapp', #. ]
Когда вы добавляете новые приложения INSTALLED_APPS , обязательно запустите их , при желании сначала сделав для них миграции с . manage.py migrate manage.py makemigrations
Поля ¶
Самая важная часть модели — и единственная необходимая часть модели — это список полей базы данных, которые она определяет. Поля задаются атрибутами класса. Будьте осторожны , чтобы не выбирать имена полей , которые конфликтуют с моделями API , как clean , save или delete .
from django.db import models class Musician(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) instrument = models.CharField(max_length=100) class Album(models.Model): artist = models.ForeignKey(Musician, on_delete=models.CASCADE) name = models.CharField(max_length=100) release_date = models.DateField() num_stars = models.IntegerField()
Типы полей ¶
Каждое поле в вашей модели должно быть экземпляром соответствующего Field класса. Django использует типы классов полей для определения нескольких вещей:
- Тип столбца, который указывает базу данных , какие данные в хранилище (например INTEGER , VARCHAR , TEXT ).
- Виджет HTML по умолчанию для использования при рендеринге поля формы (например , ).
- Минимальные требования к валидации, используемые в админке Django и в автоматически сгенерированных формах.
Django поставляется с десятками встроенных типов полей; вы можете найти полный список в справочнике по полю модели . Вы можете легко написать свои собственные поля, если встроенные в Django не помогают; см. Написание настраиваемых полей модели .
Параметры поля ¶
Каждое поле принимает определенный набор аргументов, зависящих от поля (задокументированных в справочнике по полям модели ). Например, CharField (и его подклассы) требуют max_length аргумента, который указывает размер поля VARCHAR базы данных, используемого для хранения данных.
Также есть набор общих аргументов, доступных для всех типов полей. Все не обязательны. Они полностью объяснены в справочнике , но вот краткое изложение наиболее часто используемых:
null Если True , Django будет хранить пустые значения, как NULL в базе данных. По умолчанию False . blank
Если True , поле может быть пустым. По умолчанию False .
Обратите внимание, что это отличается от null . null относится исключительно к базе данных, тогда как blank — к проверке. Если поле имеет blank=True , проверка формы позволит ввести пустое значение. Если в поле есть blank=False , поле будет обязательным.
Последовательность из 2-кортежей , чтобы использовать в качестве вариантов для этой области. Если это задано, виджет формы по умолчанию будет полем выбора вместо стандартного текстового поля и ограничит выбор предоставленными вариантами.
Список вариантов выглядит так:
YEAR_IN_SCHOOL_CHOICES = [ ('FR', 'Freshman'), ('SO', 'Sophomore'), ('JR', 'Junior'), ('SR', 'Senior'), ('GR', 'Graduate'), ]
Новая миграция создается каждый раз при choices изменении порядка изменения.
Первый элемент в каждом кортеже — это значение, которое будет храниться в базе данных. Второй элемент отображается виджетом формы поля.
Учитывая экземпляр модели, choices можно получить доступ к отображаемому значению для поля с помощью get_FOO_display() метода. Например:
from django.db import models class Person(models.Model): SHIRT_SIZES = ( ('S', 'Small'), ('M', 'Medium'), ('L', 'Large'), ) name = models.CharField(max_length=60) shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L") >>> p.save() >>> p.shirt_size 'L' >>> p.get_shirt_size_display() 'Large'
Вы также можете использовать классы перечисления для choices краткого определения:
from django.db import models class Runner(models.Model): MedalType = models.TextChoices('MedalType', 'GOLD SILVER BRONZE') name = models.CharField(max_length=60) medal = models.CharField(blank=True, choices=MedalType.choices, max_length=10)
Дополнительные примеры доступны в справочнике по полям модели .
default Значение по умолчанию для поля. Это может быть значение или вызываемый объект. Если вызываемый, он будет вызываться каждый раз при создании нового объекта. help_text Дополнительный текст «справки», отображаемый в виджете формы. Это полезно для документации, даже если ваше поле не используется в форме. primary_key
Если True это поле является первичным ключом для модели.
Если вы не укажете primary_key=True какие-либо поля в своей модели, Django автоматически добавит элемент IntegerField для хранения первичного ключа, поэтому вам не нужно устанавливать primary_key=True какие-либо поля, если вы не хотите переопределить поведение первичного ключа по умолчанию. Дополнительные сведения см. В разделе Автоматические поля первичного ключа .
Поле первичного ключа доступно только для чтения. Если вы измените значение первичного ключа на существующем объекте, а затем сохраните его, новый объект будет создан рядом со старым. Например:
from django.db import models class Fruit(models.Model): name = models.CharField(max_length=100, primary_key=True)
>>> fruit = Fruit.objects.create(name=‘Apple’) >>> fruit.name = ‘Pear’ >>> fruit.save() >>> Fruit.objects.values_list(‘name’, flat=True)
unique Если True , это поле должно быть уникальным во всей таблице.
Опять же, это всего лишь краткое описание наиболее распространенных параметров поля. Полную информацию можно найти в справочнике по опциям полей общей модели .
Автоматические поля первичного ключа ¶
По умолчанию Django предоставляет каждой модели автоматически увеличивающийся первичный ключ с типом, указанным для каждого приложения в AppConfig.default_auto_field или глобально в DEFAULT_AUTO_FIELD настройке. Например:
id = models.BigAutoField(primary_key=True)
Если вы хотите указать настраиваемый первичный ключ, укажите его primary_key=True в одном из полей. Если Django увидит, что вы явно установили Field.primary_key , он не будет добавлять автоматический id столбец.
Каждая модель требует наличия ровно одного поля primary_key=True (либо явно объявленного, либо автоматически добавленного).
Изменено в Django 3.2:
В более старых версиях автоматически создаваемые поля первичного ключа всегда были AutoField s.
Подробные имена полей ¶
Каждый тип поля, за исключением ForeignKey , ManyToManyField и OneToOneField , принимает необязательный первый позиционный аргумент — многословным именем. Если подробное имя не указано, Django автоматически создаст его, используя имя атрибута поля, преобразовав подчеркивание в пробелы.
В этом примере подробное имя : «person’s first name»
first_name = models.CharField("person's first name", max_length=30)
В этом примере подробное имя : «first name»
first_name = models.CharField(max_length=30)
ForeignKey , ManyToManyField и OneToOneField требуется, чтобы первый аргумент был классом модели, поэтому используйте verbose_name аргумент ключевого слова:
poll = models.ForeignKey( Poll, on_delete=models.CASCADE, verbose_name="the related poll", ) sites = models.ManyToManyField(Site, verbose_name="list of sites") place = models.OneToOneField( Place, on_delete=models.CASCADE, verbose_name="related place", )
По соглашению первая буква verbose_name . Django автоматически сделает первую букву заглавной там, где это необходимо.
Отношения ¶
Ясно, что сила реляционных баз данных заключается в связывании таблиц друг с другом. Django предлагает способы определения трех наиболее распространенных типов отношений между базами данных: многие-к-одному, многие-ко-многим и один-к-одному.
Отношения многие-к-одному ¶
Чтобы определить отношение «многие к одному», используйте django.db.models.ForeignKey . Вы используете его так же, как и любой другой Field тип: включив его как атрибут класса вашей модели.
ForeignKey требуется позиционный аргумент: класс, к которому относится модель.
Например, если у Car модели есть Manufacturer — то есть, a Manufacturer производит несколько автомобилей, но у каждой Car есть только одна Manufacturer — используйте следующие определения:
from django.db import models class Manufacturer(models.Model): # . pass class Car(models.Model): manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE) # .
Вы также можете создавать рекурсивные отношения (объект с отношением «многие к одному» с самим собой) и отношения с еще не определенными моделями ; подробности см. в справочнике по полям модели .
Предлагается, но не обязательно, чтобы имя ForeignKey поля ( manufacturer в приведенном выше примере) было именем модели в нижнем регистре. Вы можете называть поле как хотите. Например:
class Car(models.Model): company_that_makes_it = models.ForeignKey( Manufacturer, on_delete=models.CASCADE, ) # .
ForeignKey поля принимают ряд дополнительных аргументов, которые описаны в справочнике по полям модели . Эти параметры помогают определить, как должны работать отношения; все необязательны.
Отношения многие-ко-многим ¶
Чтобы определить отношение «многие ко многим», используйте ManyToManyField . Вы используете его так же, как и любой другой Field тип: включив его как атрибут класса вашей модели.
ManyToManyField требуется позиционный аргумент: класс, к которому относится модель.
Например, если a Pizza имеет несколько Topping объектов — то есть Topping может быть на нескольких пиццах, и у каждого Pizza есть несколько начинок — вот как вы это представите:
from django.db import models class Topping(models.Model): # . pass class Pizza(models.Model): # . toppings = models.ManyToManyField(Topping)
Как и в случае с ForeignKey , вы также можете создавать рекурсивные отношения (объект с отношением «многие ко многим» с самим собой) и отношения с еще не определенными моделями .
Предлагается, но не обязательно, чтобы имя a ManyToManyField ( toppings в приведенном выше примере) было множественным числом, описывающим набор связанных объектов модели.
Неважно, в какой модели он установлен ManyToManyField , но вы должны использовать его только в одной из моделей, а не в обеих.
Как правило, ManyToManyField экземпляры должны входить в объект, который будет редактироваться в форме. В приведенном выше примере toppings используется Pizza (вместо того, чтобы Topping иметь pizzas ManyToManyField ), потому что более естественно думать о пицце с начинкой, чем о добавке к нескольким пиццам. Как указано выше, Pizza форма позволяет пользователям выбирать начинки.
ManyToManyField Поля также принимают ряд дополнительных аргументов, которые описаны в справочнике по полям модели . Эти параметры помогают определить, как должны работать отношения; все необязательны.
Дополнительные поля для отношений «многие ко многим» ¶
Когда вы имеете дело только с отношениями «многие ко многим», такими как смешивание и сопоставление пиццы и начинки, стандарт ManyToManyField — это все, что вам нужно. Однако иногда может потребоваться связать данные с отношениями между двумя моделями.
Например, рассмотрим случай приложения, отслеживающего музыкальные группы, к которым принадлежат музыканты. Между человеком и группами, членами которых он является, существует связь «многие ко многим», поэтому вы можете использовать a ManyToManyField для представления этой связи. Однако есть много деталей о членстве, которые вы, возможно, захотите собрать, например, дата, когда человек присоединился к группе.
В таких ситуациях Django позволяет указать модель, которая будет использоваться для управления отношениями «многие ко многим». Затем вы можете добавить дополнительные поля в промежуточную модель. Промежуточная модель связана с ManyToManyField использованием through аргумента, чтобы указать на модель, которая будет действовать как посредник. Для нашего примера с музыкантом код будет выглядеть примерно так:
from django.db import models class Person(models.Model): name = models.CharField(max_length=128) def __str__(self): return self.name class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField(Person, through='Membership') def __str__(self): return self.name class Membership(models.Model): person = models.ForeignKey(Person, on_delete=models.CASCADE) group = models.ForeignKey(Group, on_delete=models.CASCADE) date_joined = models.DateField() invite_reason = models.CharField(max_length=64)
При настройке модели-посредника вы явно указываете внешние ключи для моделей, которые участвуют в отношении «многие ко многим». Это явное объявление определяет, как связаны две модели.
На промежуточную модель есть несколько ограничений:
- Ваша промежуточная модель должна содержать один и только один внешний ключ к исходной модели (это будет Group в нашем примере), или вы должны явно указать внешние ключи, которые Django должен использовать для использования связи ManyToManyField.through_fields . Если у вас более одного внешнего ключа, и through_fields он не указан, возникнет ошибка проверки. Аналогичное ограничение применяется к внешнему ключу целевой модели (это будет Person в нашем примере).
- Для модели, которая имеет отношение «многие ко многим» через посредническую модель, разрешены два внешних ключа к одной и той же модели, но они будут рассматриваться как две (разные) стороны отношения «многие ко многим». Если существует более двух внешних ключей, вы также должны указать, through_fields как указано выше, иначе возникнет ошибка проверки.
Теперь, когда вы настроили ManyToManyField использование своей модели-посредника ( Membership в данном случае), вы готовы приступить к созданию некоторых отношений «многие ко многим». Вы делаете это, создавая экземпляры промежуточной модели:
>>> ringo = Person.objects.create(name="Ringo Starr") >>> paul = Person.objects.create(name="Paul McCartney") >>> beatles = Group.objects.create(name="The Beatles") >>> m1 = Membership(person=ringo, group=beatles, . date_joined=date(1962, 8, 16), . invite_reason="Needed a new drummer.") >>> m1.save() >>> beatles.members.all() ]> >>> ringo.group_set.all() ]> >>> m2 = Membership.objects.create(person=paul, group=beatles, . date_joined=date(1960, 8, 1), . invite_reason="Wanted to form a band.") >>> beatles.members.all() , ]>
Вы также можете использовать add() , create() или set() для создания отношений, если вы укажете through_defaults в любых обязательных полях:
>>> beatles.members.add(john, through_defaults='date_joined': date(1960, 8, 1)>) >>> beatles.members.create(name="George Harrison", through_defaults='date_joined': date(1960, 8, 1)>) >>> beatles.members.set([john, paul, ringo, george], through_defaults='date_joined': date(1960, 8, 1)>)
Вы можете предпочесть создавать экземпляры промежуточной модели напрямую.
Если настраиваемая сквозная таблица, определенная промежуточной моделью, не обеспечивает уникальность пары, позволяя использовать несколько значений, вызов удалит все экземпляры промежуточной модели: (model1, model2) remove()
>>> Membership.objects.create(person=ringo, group=beatles, . date_joined=date(1968, 9, 4), . invite_reason="You've been gone for a month and we miss you.") >>> beatles.members.all() , , ]> >>> # This deletes both of the intermediate model instances for Ringo Starr >>> beatles.members.remove(ringo) >>> beatles.members.all()
Этот clear() метод можно использовать для удаления всех отношений «многие ко многим» для экземпляра:
>>> # Beatles have broken up >>> beatles.members.clear() >>> # Note that this deletes the intermediate model instances >>> Membership.objects.all()
После того, как вы установили отношения «многие ко многим», вы можете отправлять запросы. Как и в случае с обычными отношениями «многие-ко-многим», вы можете запросить, используя атрибуты модели «многие-ко-многим»:
# Find all the groups with a member whose name starts with 'Paul' >>> Group.objects.filter(members__name__startswith='Paul') QuerySet [Group: The Beatles>]>
Поскольку вы используете промежуточную модель, вы также можете запросить ее атрибуты:
# Find all the members of the Beatles that joined after 1 Jan 1961 >>> Person.objects.filter( . group__name='The Beatles', . membership__date_joined__gt=date(1961,1,1)) QuerySet [Person: Ringo Starr]>
Если вам нужно получить доступ к информации о членстве, вы можете сделать это, напрямую запросив Membership модель:
>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo) >>> ringos_membership.date_joined datetime.date(1962, 8, 16) >>> ringos_membership.invite_reason 'Needed a new drummer.'
Другой способ получить доступ к той же информации — запросить обратную связь многие-ко-многим у Person объекта:
>>> ringos_membership = ringo.membership_set.get(group=beatles) >>> ringos_membership.date_joined datetime.date(1962, 8, 16) >>> ringos_membership.invite_reason 'Needed a new drummer.'
Индивидуальные отношения ¶
Чтобы определить взаимно однозначное отношение, используйте OneToOneField . Вы используете его так же, как и любой другой Field тип: включив его как атрибут класса вашей модели.
Это наиболее полезно для первичного ключа объекта, когда этот объект каким-то образом «расширяет» другой объект.
OneToOneField требуется позиционный аргумент: класс, к которому относится модель.
Например, если вы создавали базу данных «мест», вы бы создали в ней довольно стандартные вещи, такие как адрес, номер телефона и т. Д. Затем, если вы хотите создать базу данных ресторанов поверх мест, вместо того, чтобы повторять себя и реплицировать эти поля в Restaurant модели, вы могли бы сделать так Restaurant , OneToOneField чтобы Place (потому что ресторан «является» местом; фактически, для обработки это обычно используется наследование , которое включает неявное взаимно-однозначное отношение).
OneToOneField поля также принимают необязательный parent_link аргумент.
OneToOneField классы, используемые для автоматического превращения в первичный ключ модели. Это больше не так (хотя вы можете передать primary_key аргумент вручную, если хотите). Таким образом, теперь можно иметь несколько полей типа OneToOneField в одной модели.
Модели в файлах ¶
Совершенно нормально связать модель с одной из другого приложения. Для этого импортируйте связанную модель в верхнюю часть файла, в котором определена ваша модель. Затем при необходимости обратитесь к другому классу модели. Например:
from django.db import models from geography.models import ZipCode class Restaurant(models.Model): # . zip_code = models.ForeignKey( ZipCode, on_delete=models.SET_NULL, blank=True, null=True, )
Ограничения имени поля ¶
Django накладывает некоторые ограничения на имена полей модели:
-
Имя поля не может быть зарезервированным словом Python, поскольку это приведет к синтаксической ошибке Python. Например:
class Example(models.Model): pass = models.IntegerField() # 'pass' is a reserved word!
class Example(models.Model): foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
Однако эти ограничения можно обойти, поскольку имя вашего поля не обязательно должно совпадать с именем столбца базы данных. Смотрите db_column вариант.
SQL зарезервированных слов, например join , where или select , которые допускаются в качестве имен полой модели, так как Django ускользает все имена таблиц базы данных и имена столбцов в каждом основном запросе SQL. Он использует синтаксис кавычек вашего конкретного механизма базы данных.
Типы настраиваемых полей ¶
Если одно из существующих полей модели не может использоваться для ваших целей или если вы хотите воспользоваться преимуществами некоторых менее распространенных типов столбцов базы данных, вы можете создать свой собственный класс поля. Полное описание создания собственных полей приведено в разделе «Написание пользовательских полей модели» .
Meta варианты ¶
Предоставьте метаданные вашей модели с помощью внутреннего , например: class Meta
from django.db import models class Ox(models.Model): horn_length = models.IntegerField() class Meta: ordering = ["horn_length"] verbose_name_plural = "oxen"
Метаданные модели — это «все, что не является полем», например параметры упорядочения ( ordering ), имя таблицы базы данных ( db_table ) или удобочитаемые имена в единственном и множественном числе ( verbose_name и verbose_name_plural ). Ничего не требуется, и добавление к модели совершенно необязательно. class Meta
Полный список всех возможных Meta опций можно найти в справочнике опций модели .
Атрибуты модели ¶
objects Самый важный атрибут модели — это Manager . Это интерфейс, через который операции запросов к базе данных предоставляются моделям Django и используются для извлечения экземпляров из базы данных. Если пользовательские Manager настройки не определены, используется имя по умолчанию objects . Менеджеры доступны только через классы модели, но не через экземпляры модели.
Модельные методы ¶
Определите пользовательские методы в модели, чтобы добавить к своим объектам пользовательские функциональные возможности «на уровне строк». В то время как Manager методы предназначены для работы «в масштабе таблицы», методы модели должны работать с конкретным экземпляром модели.
Это ценный метод для хранения бизнес-логики в одном месте — модели.
Например, в этой модели есть несколько настраиваемых методов:
from django.db import models class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) birth_date = models.DateField() def baby_boomer_status(self): "Returns the person's baby-boomer status." import datetime if self.birth_date datetime.date(1945, 8, 1): return "Pre-boomer" elif self.birth_date datetime.date(1965, 1, 1): return "Baby boomer" else: return "Post-boomer" @property def full_name(self): "Returns the person's full name." return '%s %s' % (self.first_name, self.last_name)
Последний метод в этом примере — свойство .
Ссылка экземпляра модели имеет полный список методов автоматически предоставленные для каждой модели . Вы можете переопределить большинство из них — см. Переопределение предопределенных методов модели ниже — но есть пара, которую вы почти всегда захотите определить:
«Магический метод» Python, который возвращает строковое представление любого объекта. Это то, что Python и Django будут использовать всякий раз, когда необходимо привести экземпляр модели и отобразить его в виде простой строки. В частности, это происходит, когда вы отображаете объект в интерактивной консоли или в админке.
Вы всегда захотите определить этот метод; значение по умолчанию вообще не очень полезно.
Это сообщает Django, как вычислить URL-адрес объекта. Django использует это в своем интерфейсе администратора, и каждый раз, когда ему нужно выяснить URL-адрес объекта.
Любой объект, имеющий URL-адрес, который однозначно идентифицирует его, должен определять этот метод.
Переопределение предопределенных методов модели ¶
Есть еще один набор методов модели, которые инкапсулируют множество поведений базы данных, которые вы захотите настроить. В частности, вам часто захочется изменить образ save() и delete() работу.
Вы можете переопределить эти методы (и любой другой метод модели), чтобы изменить поведение.
Классический вариант использования для переопределения встроенных методов — это если вы хотите, чтобы что-то происходило при сохранении объекта. Например (см. save() Документацию по параметрам, которые он принимает):
from django.db import models class Blog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField() def save(self, *args, **kwargs): do_something() super().save(*args, **kwargs) # Call the "real" save() method. do_something_else()
Вы также можете предотвратить сохранение:
from django.db import models class Blog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField() def save(self, *args, **kwargs): if self.name == "Yoko Ono's blog": return # Yoko shall never have her own blog! else: super().save(*args, **kwargs) # Call the "real" save() method.
Важно не забыть вызвать метод суперкласса — это то дело, — чтобы гарантировать, что объект по-прежнему сохраняется в базе данных. Если вы забудете вызвать метод суперкласса, поведения по умолчанию не произойдет, и база данных не будет затронута. super().save(*args, **kwargs)
Также важно передать аргументы, которые можно передать методу модели — это то, что делает бит. Время от времени Django будет расширять возможности встроенных методов модели, добавляя новые аргументы. Если вы используете в определениях своих методов, вы гарантируете, что ваш код будет автоматически поддерживать эти аргументы при их добавлении. *args, **kwargs *args, **kwargs
Переопределенные методы модели не вызываются при массовых операциях.
Обратите внимание, что delete() метод для объекта не обязательно вызывается при массовом удалении объектов с помощью QuerySet или в результате . Чтобы обеспечить выполнение настраиваемой логики удаления, вы можете использовать сигналы и / или . cascading delete pre_delete post_delete
К сожалению, не существует обходной путь , когда creating или updating объекты в объеме, так как ни один из save() , pre_save и post_save называются.
Выполнение собственного SQL ¶
Другой распространенный шаблон — написание пользовательских операторов SQL в методах модели и методах уровня модуля. Дополнительные сведения об использовании необработанного SQL см. В документации по использованию необработанного SQL .
Наследование модели ¶
Наследование модели в Django работает почти так же, как обычное наследование классов в Python, но все же следует соблюдать основы, изложенные в начале страницы. Это означает, что базовый класс должен быть подклассом django.db.models.Model .
Единственное решение, которое вам нужно принять, — это то, хотите ли вы, чтобы родительские модели были моделями сами по себе (со своими собственными таблицами базы данных), или если родители являются просто держателями общей информации, которая будет видна только через дочерние модели.
В Django возможны три стиля наследования.
- Часто вы просто хотите использовать родительский класс для хранения информации, которую вы не хотите вводить для каждой дочерней модели. Этот класс никогда не будет использоваться изолированно, поэтому вам нужны абстрактные базовые классы .
- Если вы подклассифицируете существующую модель (возможно, что-то целиком из другого приложения) и хотите, чтобы каждая модель имела свою собственную таблицу базы данных, вам подойдет многотабличное наследование .
- Наконец, если вы хотите только изменить поведение модели на уровне Python, никоим образом не изменяя поля модели, вы можете использовать прокси-модели .
Абстрактные базовые классы ¶
Абстрактные базовые классы полезны, когда вы хотите поместить некоторую общую информацию в ряд других моделей. Вы пишете свой базовый класс и помещаете abstract=True в него мета- класс. Затем эта модель не будет использоваться для создания каких-либо таблиц базы данных. Вместо этого, когда он используется в качестве базового класса для других моделей, его поля будут добавлены к полям дочернего класса.
from django.db import models class CommonInfo(models.Model): name = models.CharField(max_length=100) age = models.PositiveIntegerField() class Meta: abstract = True class Student(CommonInfo): home_group = models.CharField(max_length=5)
Student Модель будет иметь три поля: name , age и home_group . CommonInfo Модель не может быть использована в качестве нормальной модели Django, поскольку он является абстрактным базовым классом. Он не создает таблицу базы данных, не имеет менеджера и не может быть создан или сохранен напрямую.
Поля, унаследованные от абстрактных базовых классов, могут быть переопределены другим полем или значением или удалены с помощью None .
Во многих случаях этот тип наследования модели будет именно тем, что вам нужно. Он предоставляет способ вычленить общую информацию на уровне Python, при этом создавая только одну таблицу базы данных для каждой дочерней модели на уровне базы данных.
Meta наследование ¶
Когда создается абстрактный базовый класс, Django делает любой внутренний класс Meta, который вы объявили в базовом классе, доступным в качестве атрибута. Если дочерний класс не объявляет свой собственный класс Meta , он унаследует родительский класс Meta . Если дочерний элемент хочет расширить родительский класс Meta , он может создать его подкласс. Например:
from django.db import models class CommonInfo(models.Model): # . class Meta: abstract = True ordering = ['name'] class Student(CommonInfo): # . class Meta(CommonInfo.Meta): db_table = 'student_info'
Django вносит одну корректировку в класс Meta абстрактного базового класса: перед установкой атрибута Meta он устанавливается abstract=False . Это означает, что потомки абстрактных базовых классов не становятся автоматически абстрактными классами. Чтобы создать абстрактный базовый класс, который наследуется от другого абстрактного базового класса, вам необходимо явно указать abstract=True дочерний класс .
Некоторые атрибуты не имеет смысла включать в класс Meta абстрактного базового класса. Например, включение db_table будет означать, что все дочерние классы (те, которые не определяют свою собственную Meta ) будут использовать одну и ту же таблицу базы данных, что почти наверняка не то, что вам нужно.
Из-за того, как работает наследование Python, если дочерний класс наследуется от нескольких абстрактных базовых классов, по умолчанию будут наследоваться только параметры Meta из первого перечисленного класса. Чтобы наследовать параметры Meta от нескольких абстрактных базовых классов, вы должны явно объявить наследование Meta . Например:
from django.db import models class CommonInfo(models.Model): name = models.CharField(max_length=100) age = models.PositiveIntegerField() class Meta: abstract = True ordering = ['name'] class Unmanaged(models.Model): class Meta: abstract = True managed = False class Student(CommonInfo, Unmanaged): home_group = models.CharField(max_length=5) class Meta(CommonInfo.Meta, Unmanaged.Meta): pass
Будьте осторожны с related_name и related_query_name ¶
Если вы используете related_name или related_query_name на ForeignKey или ManyToManyField , вы всегда должны указывать уникальное обратное имя и имя запроса для поля. Обычно это вызывает проблемы в абстрактных базовых классах, поскольку поля этого класса включаются в каждый из дочерних классов с точно такими же значениями атрибутов (включая related_name и related_query_name ) каждый раз.
Чтобы обойти эту проблему, когда вы используете related_name или related_query_name в абстрактном базовом классе (только), часть значения должна содержать ‘%(app_label)s’ и ‘%(class)s’ .
- ‘%(class)s’ заменяется на имя дочернего класса, в котором используется поле, в нижнем регистре.
- ‘%(app_label)s’ заменяется на имя приложения, в котором содержится дочерний класс, в нижнем регистре. Имя каждого установленного приложения должно быть уникальным, и имена классов модели в каждом приложении также должны быть уникальными, поэтому результирующее имя будет другим.
Например, для приложения common/models.py :
from django.db import models class Base(models.Model): m2m = models.ManyToManyField( OtherModel, related_name="%(app_label)s_%(class)s_related", related_query_name="%(app_label)s_%(class)ss", ) class Meta: abstract = True class ChildA(Base): pass class ChildB(Base): pass
Вместе с другим приложением rare/models.py :
from common.models import Base class ChildB(Base): pass
Обратное имя common.ChildA.m2m поля будет, common_childa_related а обратное имя запроса будет common_childas . Обратное имя common.ChildB.m2m поля будет, common_childb_related а обратное имя запроса будет common_childbs . Наконец, будет обратное имя rare.ChildB.m2m поля rare_childb_related и обратное имя запроса rare_childbs . Это до вас , как вы используете ‘%(class)s’ и ‘%(app_label)s’ часть , чтобы построить ваше родственное имя или родственное имя запроса , но если вы забыли его использовать, Django будет вызывать ошибки при выполнении проверки системы (или бега migrate ).
Если вы не укажете related_name атрибут для поля в абстрактном базовом классе, обратное имя по умолчанию будет именем дочернего класса, за которым следует ‘_set’ , как обычно, если бы вы объявили поле непосредственно в дочернем классе . Например, в приведенном выше коде, если related_name атрибут был опущен, обратное имя для m2m поля было бы childa_set в ChildA регистре и childb_set для ChildB поля.
Многотабличное наследование ¶
Второй тип наследования моделей, поддерживаемый Django, — это когда каждая модель в иерархии является моделью сама по себе. Каждая модель соответствует своей собственной таблице базы данных, и ее можно запрашивать и создавать индивидуально. Отношения наследования вводят связи между дочерней моделью и каждым из ее родителей (через автоматически созданную OneToOneField ). Например:
from django.db import models class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) class Restaurant(Place): serves_hot_dogs = models.BooleanField(default=False) serves_pizza = models.BooleanField(default=False)
Все поля Place также будут доступны в Restaurant , хотя данные будут находиться в другой таблице базы данных. Итак, оба варианта возможны:
>>> Place.objects.filter(name="Bob's Cafe") >>> Restaurant.objects.filter(name="Bob's Cafe")
Если у вас есть, Place который также является Restaurant , вы можете перейти от Place объекта к Restaurant объекту, используя строчную версию имени модели:
>>> p = Place.objects.get(id=12) # If p is a Restaurant object, this will give the child class: >>> p.restaurant
Однако, если p в приведенном выше примере не было Restaurant (он был создан непосредственно как Place объект или был родительским элементом какого-либо другого класса), ссылка на p.restaurant вызовет Restaurant.DoesNotExist исключение.
Автоматически созданный OneToOneField на Restaurant что связывает его Place выглядит следующим образом :
place_ptr = models.OneToOneField( Place, on_delete=models.CASCADE, parent_link=True, primary_key=True, )
Вы можете переопределить это поле, объявив свое собственное OneToOneField с помощью parent_link=True on Restaurant .
Meta и многотабличное наследование ¶
В ситуации наследования нескольких таблиц дочерний класс не имеет смысла наследовать от своего родительского класса Meta . Все параметры Meta уже были применены к родительскому классу, и их повторное применение обычно приводит только к противоречивому поведению (это контрастирует со случаем абстрактного базового класса, где базовый класс не существует сам по себе).
Таким образом, дочерняя модель не имеет доступа к родительскому мета- классу. Однако есть несколько ограниченных случаев, когда дочерний элемент наследует поведение от родителя: если дочерний ordering элемент не указывает атрибут или get_latest_by атрибут, он унаследует их от своего родителя.
Если у родителя есть порядок, и вы не хотите, чтобы у дочернего элемента был какой-либо естественный порядок, вы можете явно отключить его:
class ChildModel(ParentModel): # . class Meta: # Remove parent's ordering effect ordering = []
Наследование и обратные отношения ¶
Поскольку многотабличное наследование использует неявное OneToOneField связывание дочернего и родительского элементов, можно перейти от родительского элемента к дочернему, как в приведенном выше примере. Тем не менее, это израсходовал имя, которое по умолчанию related_name значения ForeignKey и ManyToManyField отношений. Если вы помещаете эти типы отношений в подкласс родительской модели, вы должны указать related_name атрибут в каждом таком поле. Если вы забудете, Django выдаст ошибку проверки.
Например, Place снова используя вышеуказанный класс, давайте создадим еще один подкласс с ManyToManyField :
class Supplier(Place): customers = models.ManyToManyField(Place)
Это приводит к ошибке:
Reverse query name for 'Supplier.customers' clashes with reverse query name for 'Supplier.place_ptr'. HINT: Add or change a related_name argument to the definition for 'Supplier.customers' or 'Supplier.place_ptr'.
Добавление related_name в customers поле ниже будет устранить ошибку: . models.ManyToManyField(Place, related_name=’provider’)
Указание поля родительской ссылки ¶
Как уже упоминалось, Django автоматически создаст OneToOneField связь вашего дочернего класса с любыми неабстрактными родительскими моделями. Если вы хотите контролировать имя атрибута, связывающегося с родительским классом , вы можете создать свое собственное OneToOneField и parent_link=True указать, что ваше поле является обратной ссылкой на родительский класс.
Прокси-модели ¶
При использовании наследования нескольких таблиц новая таблица базы данных создается для каждого подкласса модели. Обычно это желаемое поведение, поскольку подклассу нужно место для хранения любых дополнительных полей данных, которые отсутствуют в базовом классе. Однако иногда вам нужно только изменить поведение модели на языке Python — например, изменить диспетчер по умолчанию или добавить новый метод.
Для этого и используется наследование прокси-модели: создание прокси для исходной модели. Вы можете создавать, удалять и обновлять экземпляры прокси-модели, и все данные будут сохранены, как если бы вы использовали исходную (без прокси) модель. Разница в том, что вы можете изменить такие вещи, как порядок модели по умолчанию или менеджер по умолчанию в прокси-сервере, без изменения оригинала.
Прокси-модели объявлены как обычные модели. Вы сообщаете Django, что это прокси-модель, устанавливая для proxy атрибута Meta класса значение True .
Например, предположим, что вы хотите добавить метод к Person модели. Сделать это можно так:
from django.db import models class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) class MyPerson(Person): class Meta: proxy = True def do_something(self): # . pass
MyPerson Класс работает на одной и той же таблицы базы данных в качестве родительского Person класса. В частности, любые новые экземпляры Person также будут доступны через MyPerson и наоборот:
>>> p = Person.objects.create(first_name=«foobar») >>> MyPerson.objects.get(first_name=«foobar»)
Вы также можете использовать прокси-модель для определения другого порядка по умолчанию в модели. Возможно, вы не всегда хотите заказывать Person модель, но регулярно упорядочивайте ее по last_name атрибуту при использовании прокси:
class OrderedPerson(Person): class Meta: ordering = ["last_name"] proxy = True
Теперь обычные Person запросы будут неупорядоченными, а OrderedPerson запросы будут упорядочены по last_name .
Прокси-модели наследуют Meta атрибуты так же, как обычные модели .
QuerySet s по-прежнему возвращает запрошенную модель ¶
Невозможно заставить Django возвращать, скажем, MyPerson объект всякий раз, когда вы запрашиваете Person объекты. Набор запросов для Person объектов вернет эти типы объектов. Весь смысл прокси-объектов заключается в том, что код, основанный на оригинале, Person будет использовать их, а ваш собственный код может использовать включенные вами расширения (на которые в любом случае не полагается ни один другой код). Это не способ Person повсюду заменять (или любую другую) модель чем-то созданным вами.
Ограничения базового класса ¶
Прокси-модель должна наследовать ровно от одного неабстрактного класса модели. Вы не можете наследовать от нескольких неабстрактных моделей, поскольку прокси-модель не обеспечивает связи между строками в разных таблицах базы данных. Прокси-модель может наследовать от любого количества классов абстрактных моделей, если они не определяют никаких полей модели. Прокси-модель также может наследовать от любого количества прокси-моделей, которые имеют общий неабстрактный родительский класс.
Менеджеры прокси-моделей ¶
Если вы не укажете менеджеров модели в прокси-модели, она наследует менеджеров от своей родительской модели. Если вы определите менеджера в модели прокси, он станет менеджером по умолчанию, хотя все менеджеры, определенные в родительских классах, по-прежнему будут доступны.
Продолжая наш пример выше, вы можете изменить менеджер по умолчанию, используемый при запросе Person модели следующим образом:
from django.db import models class NewManager(models.Manager): # . pass class MyPerson(Person): objects = NewManager() class Meta: proxy = True
Если вы хотите добавить новый менеджер к прокси, не заменяя существующий по умолчанию, вы можете использовать методы, описанные в документации настраиваемого менеджера : создать базовый класс, содержащий новых менеджеров, и унаследовать его после основного базового класса:
# Create an abstract class for the new manager. class ExtraManagers(models.Model): secondary = NewManager() class Meta: abstract = True class MyPerson(Person, ExtraManagers): class Meta: proxy = True
Вероятно, вам не придется делать это очень часто, но когда вы это сделаете, это возможно.
Различия между наследованием прокси и неуправляемыми моделями ¶
Наследование прокси-модели может выглядеть довольно похоже на создание неуправляемой модели с использованием managed атрибута в Meta классе модели .
При тщательной настройке Meta.db_table вы можете создать неуправляемую модель, которая затеняет существующую модель и добавляет к ней методы Python. Однако это было бы очень повторяющимся и хрупким, поскольку вам необходимо синхронизировать обе копии, если вы внесете какие-либо изменения.
С другой стороны, прокси-модели предназначены для того, чтобы вести себя точно так же, как модель, для которой они проксируются. Они всегда синхронизированы с родительской моделью, поскольку напрямую наследуют ее поля и менеджеров.
Общие правила таковы:
- Если вы зеркалируете существующую модель или таблицу базы данных и не хотите, чтобы все столбцы исходной таблицы базы данных, используйте Meta.managed=False . Эта опция обычно полезна для моделирования представлений базы данных и таблиц, не находящихся под контролем Django.
- Если вы хотите изменить поведение модели только для Python, но сохранить все те же поля, что и в оригинале, используйте Meta.proxy=True . Это настраивает все так, что прокси-модель является точной копией структуры хранения исходной модели при сохранении данных.
Множественное наследование ¶
Как и в случае с подклассами Python, модель Django может наследовать от нескольких родительских моделей. Имейте в виду, что применяются обычные правила разрешения имен Python. Первый базовый класс, в котором появляется конкретное имя (например, Meta ), будет тем, который используется; например, это означает, что если несколько родителей содержат класс Meta , будет использоваться только первый, а все остальные будут проигнорированы.
Как правило, вам не нужно наследовать от нескольких родителей. Основной вариант использования, когда это полезно, — это «смешанные» классы: добавление определенного дополнительного поля или метода к каждому классу, наследующему смешивание. Старайтесь, чтобы иерархия наследования была как можно более простой и понятной, чтобы вам не приходилось бороться за определение того, откуда исходит конкретная часть информации.
Обратите внимание, что наследование от нескольких моделей, которые имеют общее id поле первичного ключа, вызовет ошибку. Чтобы правильно использовать множественное наследование, вы можете использовать явное значение AutoField в базовых моделях:
class Article(models.Model): article_id = models.AutoField(primary_key=True) . class Book(models.Model): book_id = models.AutoField(primary_key=True) . class BookReview(Book, Article): pass
Или используйте общего предка для хранения AutoField . Это требует использования явного указания OneToOneField от каждой родительской модели к общему предку, чтобы избежать конфликта между полями, которые автоматически генерируются и наследуются дочерним элементом:
class Piece(models.Model): pass class Article(Piece): article_piece = models.OneToOneField(Piece, on_delete=models.CASCADE, parent_link=True) . class Book(Piece): book_piece = models.OneToOneField(Piece, on_delete=models.CASCADE, parent_link=True) . class BookReview(Book, Article): pass
Имя поля «скрытие» недопустимо ¶
При обычном наследовании классов Python дочернему классу разрешается переопределять любой атрибут из родительского класса. В Django это обычно не разрешено для полей модели. Если неабстрактный базовый класс модели имеет поле с именем author , вы не можете создать другое поле модели или определить атрибут, вызываемый author в любом классе, который наследуется от этого базового класса.
Это ограничение не распространяется на поля модели, унаследованные от абстрактной модели. Такие поля могут быть заменены другим полем или значением или удалены путем установки . field_name = None
Менеджеры моделей унаследованы от абстрактных базовых классов. Переопределение унаследованного поля, на которое ссылается унаследованное, Manager может вызвать небольшие ошибки. См. Настраиваемые менеджеры и наследование модели .
Некоторые поля определяют дополнительные атрибуты на модели, например, ForeignKey определяет дополнительный атрибут с _id добавляемыми к имени поля, а также related_name и related_query_name на внешней модели.
Эти дополнительные атрибуты нельзя переопределить, если поле, которое их определяет, не будет изменено или удалено так, чтобы оно больше не определяло дополнительный атрибут.
Переопределение полей в родительской модели приводит к трудностям в таких областях, как инициализация новых экземпляров (указание, в каком поле инициализируется Model.__init__ ) и сериализация. Это функции, с которыми обычное наследование классов Python не должно иметь дело совершенно одинаково, поэтому разница между наследованием модели Django и наследованием классов Python не является произвольной.
Это ограничение применяется только к атрибутам, которые являются Field экземплярами. При желании обычные атрибуты Python можно переопределить. Это также относится только к имени атрибута, как его видит Python: если вы вручную указываете имя столбца базы данных, вы можете иметь одно и то же имя столбца, отображаемое как в дочерней, так и в родительской модели для многотабличного наследования (они являются столбцами в двух разных таблицах базы данных).
Django вызовет a, FieldError если вы переопределите любое поле модели в любой модели-предке.
Организация моделей в пакете ¶
Команда создает структуру приложения, включающую файл. Если у вас много моделей, может оказаться полезным их объединение в отдельные файлы. manage.py startapp models.py
Для этого создайте models пакет. Удалите models.py и создайте myapp/models/ каталог с __init__.py файлом и файлами для хранения ваших моделей. Вы должны импортировать модели в __init__.py файл.
Например, если у вас были organic.py и synthetic.py в models каталоге:
myapp / models / __ init__.py ¶
from .organic import Person from .synthetic import Robot
Явный импорт каждой модели вместо использования имеет преимущества в том, что он не загромождает пространство имен, делает код более читаемым и сохраняет полезными инструменты анализа кода. from .models import *
Справочник по моделям Охватывает все API, связанные с моделью, включая поля модели, связанные объекты и QuerySet .
СОДЕРЖАНИЕ
- Модели
- Быстрый пример
- Использование моделей
- Поля
- Типы полей
- Варианты полей
- Автоматические поля первичного ключа
- Подробные имена полей
- Отношения
- Отношения многие-к-одному
- Отношения многие-ко-многим
- Дополнительные поля для отношений «многие ко многим»
- Индивидуальные отношения
- Переопределение предопределенных методов модели
- Выполнение пользовательского SQL
- Абстрактные базовые классы
- Meta наследование
- Будьте осторожны с related_name и related_query_name
- Meta и многотабличное наследование
- Наследование и обратные отношения
- Указание поля родительской ссылки
- QuerySet s по-прежнему возвращает запрошенную модель
- Ограничения базового класса
- Менеджеры прокси-моделей
- Различия между наследованием прокси и неуправляемыми моделями
Просматривать
- Предыдущая: Модели и базы данных
- Далее: Выполнение запросов
- Оглавление
- Общий указатель
- Указатель модуля Python
Вы здесь:
- Документация Django 3.2
- Использование Django
- Модели и базы данных
- Модели
Python-сообщество
![[RSS Feed]](https://python.su/static/djangobb_forum/img/feed-icon-small.png)
- Начало
- » Django
- » related_name зачем оно?
#1 Сен. 7, 2008 02:00:08
romankrv От: Зарегистрирован: 2008-05-23 Сообщения: 513 Репутация: 0 Профиль Отправить e-mail
related_name зачем оно?
You can override the FOO_set name by setting the related_name parameter in the ForeignKey() definition. For example, if the Entry model was altered to blog = ForeignKey(Blog, related_name=’entries’), the above example code would look like this:
Понятно что этим мы перекрываем название менеджера FOO_set который возвращает все экземпляры молели по схеме бэкворд (foirgin key) на свой.
Вопрос для каких практических целей это используется так как это вроде вносит лишние имена при работе. Что в этом есть удобного ?
#2 Сен. 7, 2008 08:43:40
Lolka От: Зарегистрирован: 2007-09-29 Сообщения: 128 Репутация: 0 Профиль Отправить e-mail
related_name зачем оно?
Пример – модель трижды завешена на User по разным причинам :
model Labyrinth(models.Model):
designer = models.ForeignKey(User, null=False, related_name='designer') # дизайнер
curator = models.ForeignKey(User, null=True, related_name='curator') # куратор
manager = models.ForeignKey(User, null=True, related_name='manager') # менеджерЕсли не указать related_name, то получится такое:
Error: One or more models did not validate:
labyrinths.labyrinth: Accessor for field 'designer' clashes with related field 'User.labyrinth_set'. Add a related_name argument to the definition for 'designer'.
labyrinths.labyrinth: Accessor for field 'curator' clashes with related field 'User.labyrinth_set'. Add a related_name argument to the definition for 'curator'.
.
- Модели и базы данных
- Использование Django