Ограничение типов контента в модели Django

В этой статье мы рассмотрим, как ограничить типы контента при работе с общими отношениями в Django:

  1. Как ограничить типы контента в модели Django?
  2. Как ограничить общий ForeignKey Django списком моделей?

Содержимое

Введение

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

Эти экземпляры ContentType хранят информацию о вашей модели и предоставляют методы для получения экземпляров соответствующих моделей - например, get_object_for_this_type().

Кроме того, фреймворк позволяет вам реализовывать "общие" отношения, которые представляют собой отношения между экземпляром одной из ваших моделей и любой другой моделью. Например, используя общие связи, вы можете создать единую систему тегов или комментариев, которая будет работать во всех ваших моделях.

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

Ограничить типы контента

Предположим, мы работаем над веб-приложением для автосалона. Дилерский центр продает три типа транспортных средств: легковые автомобили, электромобили и мотоциклы. В результате у нас есть три модели Django для транспортных средств и еще одна для отслеживания продаж автомобилей.

Модели выглядят примерно так:

class Car(models.Model):
    # ...


class ElectricCar(models.Model):
    # ...


class Motorcycle(models.Model):
    # ...


class Sale(models.Model):
    content_type = models.ForeignKey(
        ContentType,
        on_delete=models.CASCADE,
    )
    object_id = models.PositiveIntegerField()
    # ...

Хотя это позволяет нам связать продажу с экземпляром транспортного средства через content_type и object_id, это не идеально. Не ограничивая типы контента, пользователь технически может также связать продажу с моделью, не относящейся к автомобилю, например, auth.user.

Давайте исправим это.

Во-первых, определите список вариантов выбора типа контента следующим образом:

from django.db.models import Q

from dealership.apps import DealershipConfig


CONTENT_TYPE_CHOICES = (
    Q(app_label=DealershipConfig.name, model=Car.__name__.lower())
    | Q(app_label=DealershipConfig.name, model=ElectricCar.__name__.lower())
    | Q(app_label=DealershipConfig.name, model=Motorcycle.__name__.lower())
)

Кроме того, вы можете жестко задать ярлыки приложений и названия моделей. Это полезно при работе со сторонними приложениями, где у вас нет прямого доступа к конфигурации приложения. Например:

Q(app_label="dealership", model="car")

Имейте в виду, что название приложения и модели всегда должно быть в нижнем регистре. Например, если ваша модель называется FooBarFaz,, название модели должно быть foobarfaz.

Если вы не уверены в названиях приложений или моделей, вы можете перечислить все зарегистрированные типы контента, используя оболочку Django:

from django.contrib.contenttypes.models import ContentType

for content_type in ContentType.objects.all():
    print(content_type.app_label, content_type.model)

Затем установите параметр Foreign keys limit_choices_to в только что определенный список:

class Sale(models.Model):
    content_type = models.ForeignKey(
        ContentType,
        on_delete=models.CASCADE,
        limit_choices_to=CONTENT_TYPE_CHOICES,
    )
    # ...

Вносите изменения, мигрируйте и тестируйте.

Типы контента теперь ограничены значениями Car, ElectricCar, и Motorcycle. Более того, на панели администратора будет отображаться только ограниченное количество типов контента.

Заключение

Итак, мы рассмотрели, как ограничить типы контента в модели Django.

Самый простой способ ограничить типы контента - это использовать параметр ForeignKey из limit_choices_to. При указании подмножества типов контента вы можете использовать -

  1. Конфигурация приложения
  2. Конфигурация модели

Или вы можете жестко задать значения. Если вы решите использовать жесткий код, укажите название приложения и модели в нижнем регистре.

Полный пример доступен на GitHub..

Вернуться на верх