2012-08-14 15 views
39

Jestem nowy w użyciu GenericForeignKey i nie mogłem go uruchomić w instrukcji zapytania. Tabele są mniej więcej tak:django: jak mogę wykonać zapytanie na podstawie pól 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) 

skan tworzy jeden problem, problem generuje jakieś bilety, i zrobiłem Issue jako klucz obcy do tabeli biletową. Teraz mam obiekt skanowania i chcę zapytać o wszystkie bilety związane z tym skanowaniem. Próbowałem tego najpierw:

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

co nie działa. Następnie próbowałem tego:

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) 

Nadal nie działa. Muszę wiedzieć, jak wykonać tego rodzaju zapytania w django? Dzięki.

Odpowiedz

57

Określone przez Ciebie pole Ticket.issue pomoże ci przejść z instancji Ticket do Issue, do której jest przyłączone, ale nie pozwoli ci wrócić do tyłu. Jesteś blisko ze swoim drugim przykładem, ale musisz użyć pola issue_id - nie możesz wykonać zapytania na GenericForeignKey (to tylko pomaga odzyskać obiekt, gdy masz instancję Ticket). Spróbuj tego:

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)) 
+3

Uratowałem mój dzień, przyjacielu, jestem po prostu bez włosów po tym, jak próbowałem to rozgryźć. Dzięki :) . –

+0

@girasquid issue_id jest bardzo mylące, ponieważ może odnosić się do pola issu_id w pytaniu lub atrybutu id pola problemowego w pytaniu, czy możemy je rozróżnić? – rohanagarwal

14

filtrowanie całej GenericForeignKey puszki, tworząc drugi model, który podziela db_table z Ticket. Najpierw podzielmy Bilet na abstrakcyjny model i konkretny 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(models.Model): 
    issue = generic.GenericForeignKey('issue_ct', 'issue_id') 

Następnie utwórz model, który również podklasuje TicketBase. Ta podklasa będzie miała wszystkie te same pola z wyjątkiem issue, która jest zdefiniowana jako ForeignKey. Dodanie niestandardowego Manager umożliwia jego filtrowanie do pojedynczego ContentType.

Ponieważ ta podklasa nie musi być synchronizowana ani migrowana, można ją utworzyć dynamicznie za pomocą 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) 
+0

To jest bardzo, bardzo sprytne .. –

Powiązane problemy