django: hoe voer ik een query uit op basis van de velden van GenericForeignKey?

Ik ben nieuw in het gebruik van GenericForeignKey, en ik kon het niet laten werken in een query-instructie. De tabellen zien er ongeveer als volgt uit:

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)

Een scan creëert één probleem, een probleem genereert enkele tickets en ik heb een probleem gemaakt als een externe sleutel voor de tickettabel. Nu heb ik een Scan-object en ik wil alle tickets opvragen die betrekking hebben op deze scan. Ik heb dit eerst geprobeerd:

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

wat niet werkt. Toen probeerde ik dit:

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)

Werkt nog steeds niet. Ik moet weten hoe ik dit soort vragen in django moet doen? Bedankt.


Antwoord 1, autoriteit 100%

Het veld Ticket.issuedat je hebt gedefinieerd, helpt je om van een Ticket-instantie naar het issuete gaan waaraan het is gekoppeld, maar het laat je niet achteruit gaan. U bent in de buurt met uw tweede voorbeeld, maar u moet het veld issue_idgebruiken – u kunt geen query uitvoeren op de GenericForeignKey(het helpt u alleen het object op te halen wanneer u een Ticketinstantie hebben). Probeer dit:

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
    )

Antwoord 2, autoriteit 21%

Filteren op een GenericForeignKeykan door een tweede model te maken dat de db_tabledeelt met Ticket. Splits Ticket eerst op in een abstract model en een concreet model.

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')

Maak vervolgens een model dat ook TicketBasesubklassen. Deze subklasse heeft allemaal dezelfde velden behalve issuedie in plaats daarvan is gedefinieerd als een ForeignKey. Door een aangepaste Managertoe te voegen, kan deze worden gefilterd op slechts één ContentType.

Aangezien deze subklasse niet hoeft te worden gesynchroniseerd of gemigreerd, kan deze dynamisch worden gemaakt met 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)

Other episodes