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)
Сканирование создает одну проблему, проблема генерирует некоторые билеты, и я сделал Issue в качестве внешнего ключа в таблице Ticket. Теперь у меня есть объект 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? Спасибо.
Ответы
Ответ 1
Поле 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))
Ответ 2
Фильтрация по GenericForeignKey
может путем создания второй модели, которая разделяет db_table
с 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(models.Model):
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)