web-dev-qa-db-fra.com

django: comment interroger en fonction des champs GenericForeignKey?

Je suis nouveau dans l'utilisation de GenericForeignKey, et je n'ai pas pu le faire fonctionner dans une instruction de requête. Les tableaux sont à peu près comme suit:

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)

Une analyse crée un problème, un problème génère des tickets et j'ai créé Issue en tant que clé étrangère de la table Ticket. J'ai maintenant un objet Scan et je souhaite interroger tous les tickets liés à ce scan. J'ai essayé ceci en premier:

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

ce qui ne fonctionne pas. Ensuite, j'ai essayé ceci:

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)

Ça ne marche toujours pas. J'ai besoin de savoir comment faire ce genre de requêtes dans django? Merci.

47
Shang Wang

Le Ticket.issue le champ que vous avez défini vous aidera à passer d'une instance Ticket à la Issue à laquelle il est attaché, mais il ne vous laissera pas revenir en arrière. Vous êtes proche de votre deuxième exemple, mais vous devez utiliser le issue_id champ - vous ne pouvez pas interroger sur le GenericForeignKey (il vous aide simplement à récupérer l'objet lorsque vous avez une instance de Ticket). Essaye ça:

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))
65
girasquid

Filtrer sur un GenericForeignKey peut en créant un deuxième modèle qui partage le db_table Avec Ticket. Divisez d'abord Ticket en un modèle abstrait et un modèle concret.

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

Créez ensuite un modèle qui sous-classe également TicketBase. Cette sous-classe aura tous les mêmes champs sauf issue qui est plutôt défini comme ForeignKey. L'ajout d'un Manager personnalisé permet de le filtrer sur un seul ContentType.

Comme cette sous-classe n'a pas besoin d'être synchronisée ou migrée, elle peut être créée dynamiquement à l'aide de 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)
15
kreneskyp