2012-08-14 11 views
39

Soy nuevo en el uso de GenericForeignKey, y no pude hacerlo funcionar en una declaración de consulta. Las mesas son más o menos como la siguiente:django: ¿cómo hago una consulta basada en los campos de 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) 

Una exploración crea un problema, un problema genera algunas entradas, y me hizo Edición como una clave externa a la tabla de entradas. Ahora tengo un objeto de escaneo y quiero consultar todas las entradas relacionadas con este escaneo. Intenté esto primero:

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

que no funciona. Entonces probé esto:

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) 

Todavía no funciona. Necesito saber cómo hacer este tipo de consultas en django? Gracias.

Respuesta

57

El campo Ticket.issue que ha definido le ayudará a pasar de una instancia Ticket al Issue al que está conectado, pero no le permitirá retroceder. Estás cerca con tu segundo ejemplo, pero necesitas utilizar el campo issue_id; no puedes realizar consultas en el GenericForeignKey (solo te ayuda a recuperar el objeto cuando tienes una instancia de Ticket). Prueba esto:

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

Me salvó el día, amigo, estoy básicamente sin pelo después de tratar de resolver esto. Gracias :) . –

+0

@girasquid issue_id es muy confuso, ya que puede hacer referencia al campo issue_id en cuestión o al atributo id del campo de problema en la pregunta, ¿podemos hacer que se distingan? – rohanagarwal

14

Filtrado a través de una lata GenericForeignKey mediante la creación de un segundo modelo que comparte el db_table con Ticket. Primero divida el Boleto en un modelo abstracto y un modelo concreto.

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

continuación, crear un modelo que también subclases TicketBase. Esta subclase tendrá todos los mismos campos, excepto issue, que en su lugar se define como ForeignKey. Agregar un Manager personalizado permite filtrarlo a solo un ContentType.

Dado que no es necesario sincronizar o migrar esta subclase, se puede crear dinámicamente con 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

Esto es muy, muy inteligente ... –

Cuestiones relacionadas