django: как сделать запрос на основе полей GenericForeignKey?

Я новичок в использовании GenericForeignKey, и мне не удалось заставить его работать в операторе запроса. Таблицы примерно такие:

class Ticket(models.Model):
    issue_ct = models.ForeignKey(ContentType, related_name='issue_content_type')
    issue_id = models.PositiveIntegerField(null=True, blank=True)
    issue = generic.GenericForeignKey('issue_ct', 'issue_id')

class Issue(models.Model):
    scan = models.ForeignKey(Scan)

Сканирование создает одну проблему, проблема генерирует несколько билетов, и я сделал задачу как внешний ключ для таблицы билетов. Теперь у меня есть объект сканирования, и я хочу запросить все билеты, связанные с этим сканированием. Я попробовал это первым:

tickets = Tickets.objects.filter(issue__scan=scan_obj)

что не работает. Затем я попробовал это:

issue = Issue.objects.get(scan=scan_obj)
content_type = ContentType.objects.get_for_model(Issue)
tickets = Tickets.objects.filter(content_type=content_type, issue=issue)

Все еще не работает. Мне нужно знать, как делать такие запросы в django? Спасибо.


person Shang Wang    schedule 14.08.2012    source источник


Ответы (2)


Определенное вами поле Ticket.issue поможет вам перейти от экземпляра Ticket к экземпляру Issue, к которому он присоединен, но не позволит вернуться назад. Вы близки со своим вторым примером, но вам нужно использовать поле issue_id — вы не можете запрашивать GenericForeignKey (это просто помогает вам получить объект, когда у вас есть экземпляр Ticket). Попробуй это:

from django.contrib.contenttypes.models import ContentType

issue = Issue.objects.get(scan=scan_obj)
tickets = Ticket.objects.filter(
    issue_id=issue.id,
    issue_ct=ContentType.objects.get_for_model(issue).id
    )
person girasquid    schedule 14.08.2012
comment
@girasquid issue_id очень сбивает с толку, так как может относиться к рассматриваемому полю issue_id или атрибуту id поля задачи в вопросе, можем ли мы сделать их различимыми? - person rohanagarwal; 07.04.2017

Фильтрация по банку GenericForeignKey путем создания второй модели, которая разделяет db_table с Ticket. Сначала разделите Ticket на абстрактную модель и конкретную модель.

class TicketBase(models.Model):
    issue_ct = models.ForeignKey(ContentType, related_name='issue_content_type')
    issue_id = models.PositiveIntegerField(null=True, blank=True)

    class Meta:
        abstract = True

class Ticket(TicketBase):
    issue = generic.GenericForeignKey('issue_ct', 'issue_id')

Затем создайте модель, которая также является подклассом TicketBase. Этот подкласс будет иметь все те же поля, кроме issue, который вместо этого определен как ForeignKey. Добавление пользовательского Manager позволяет отфильтровать его до одного ContentType.

Поскольку этот подкласс не нужно синхронизировать или переносить, его можно создать динамически с помощью type().

def subclass_for_content_type(content_type):
    class Meta:
        db_table = Ticket._meta.db_table

    class Manager(models.Manager):
        """ constrain queries to a single content type """
        def get_query_set(self):
            return super(Manager, self).get_query_set().filter(issue_ct=content_type)

    attrs = {
        'related_to': models.ForeignKey(content_type.model_class()),
        '__module__': 'myapp.models',
        'Meta': Meta,
        'objects': Manager()
    }
   return type("Ticket_%s" % content_type.name, (TicketBase,), attrs)
person kreneskyp    schedule 10.01.2014