Shshzik
@Shshzik
Начинающий

Как задать в ContentType только 2 модели?

Есть вот такая модель:
class Slider(models.Model):
    name = models.CharField(max_length=200, verbose_name='Название')
    image = models.ImageField(upload_to='slider', verbose_name='Изображение')
    content = models.TextField(verbose_name='Текст на слайдере')
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = 'Слайдер'
        verbose_name_plural = 'Слайдеры'

Соответственно в поле - content_type = models.ForeignKey(ContentType) выводятся все модели. А мне нужно только 2.
Соответственно у меня 2 вопроса.
1) Как сделать так как я описал.
2) Есть ли возможность что бы в поле object_id можно было выбрать уже существующие элементы из выбранной Модели?
  • Вопрос задан
  • 534 просмотра
Решения вопроса 2
@DmitryBurn
Бекенд разработчик Python/Django
Сам по себе content_type в качестве внешнего ключа на любую таблицу не подразумевает каких-либо ограничений ... так как ты указываешь по сути айдишник типа на который ссылаешься (ссылка на табл. content_type) и id собственно объекта....
Но можно сделать ограничение на уровне отображения формы где ты выбираешь ту или иную модель для ссылки...
Есть готовое решение (для админки): https://github.com/arthanson/django-genericadmin ... где ты можешь задать white_list и указать модели с которыми ты хочешь работать...
Если же используется не админка, а кастомные формы (или кастомная форма для админки) - советую просто наложить ограничение на queryset который выдается в поле content_type-а
Ответ написан
@marazmiki
Укротитель питонов
Используйте limit_choices_to. Принципиально решение будет выглядеть примерно так:

from django.db.models import Q
class Slider(models.Model):
   # ...
    content_type = models.ForeignKey(
        ContentType,
        limit_choices_to=Q(app_label='app_1', model='modelclass1') | Q(app_label='app_2', model='modelclass2')
    )
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')    
   
    # ...


Нового тут особо ничего не придумаешь, но есть есть возможность украсить код, например, написав функцию (limit_choices_to может принимать callable-объект, возвращающий dict или Q):

from operator import or_ as OR
from functools import reduce
from django.apps import apps
from django.db.models import Q

def limit(*models):
    """
    Динамически построит выражение Q для списка заданных в аргументе моделей.
    """
    return reduce(OR, [Q(
        app_label=apps.get_model(m)._meta.app_label,
        model=apps.get_model(m)._meta.model_name) for m in models
    ])


class Slider(models.Model):
    # ...
    content_type = models.ForeignKey(
        ContentType,
        limit_choices_to=limit('app_1.ModelClass1', 'app_2.ModelClass2'),
    )
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')    
    # ...


Можно пойти ещё дальше и написать собственное поле, заменяющее три поля generic одним. На мой взгляд, код будет лаконичнее:

class Slider(models.Model):
    content_object = LimitedGenericForeignKey(
        allowed_models=['app_1.ModelClass1', 'app_2.ModelClass2']
    )


Но это оставлю на домашнее задание :)
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы