8 votes

Inclure les tables en double à l'aide de l'ORM Extra() de Django

J'essaie d'implémenter un triplestore simple en utilisant l'ORM de Django. J'aimerais être en mesure de rechercher des modèles de triplés arbitrairement complexes (par exemple, comme vous le feriez avec SparQL).

Pour ce faire, j'essaie d'utiliser la fonction .extra() méthode. Cependant, même si la documentation mentionne qu'elle peut, en théorie, gérer les références doubles à la même table en créant automatiquement un alias pour les références de table doubles, j'ai constaté qu'elle ne le fait pas en pratique.

Par exemple, disons que j'ai le modèle suivant dans mon application "triple" :

class Triple(models.Model):
    subject = models.CharField(max_length=100)
    predicate = models.CharField(max_length=100)
    object = models.CharField(max_length=100)

et j'ai les triples suivants stockés dans ma base de données :

subject predicate object
bob has-a hat .
bob knows sue .
sue has-a house .
bob knows tom .

Maintenant, disons que je veux demander les noms de toutes les personnes que Bob connaît qui ont une maison. En SQL, je ferais simplement :

SELECT t2.subject AS name
FROM triple_triple t1
INNER JOIN triple_triple t2 ON
    t1.subject = 'bob'
AND t1.predicate = 'knows'
AND t1.object = t2.subject
AND t2.predicate = 'has-a'
AND t2.object = 'house'

Je ne suis pas tout à fait sûr de ce à quoi cela ressemblerait avec l'ORM de Django, même si je pense que ce serait dans le genre :

q = Triple.objects.filter(subject='bob', predicate='knows')
q = q.extra(tables=['triple_triple'], where=["triple_triple.object=t1.subject AND t1.predicate = 'has-a' AND t1.object = 'house'"])
q.values('t1.subject')

Malheureusement, cette opération échoue avec l'erreur "DatabaseError : no such column : t1.subject".

L'exécution de print q.query montre :

SELECT "triple_triple"."subject" FROM "triple_triple" WHERE ("triple_triple"."subject" = 'bob' AND "triple_triple"."predicate" = 'knows'
AND triple_triple.object = t1.subject AND t1.predicate = 'has-a' AND t1.object = 'house')

ce qui semble montrer que le paramètre tables de mon appel à .extra() est ignoré, car il n'y a pas de deuxième référence à triple_triple insérée nulle part.

Pourquoi cela se produit-il ? Quelle est la manière appropriée de faire référence à des relations complexes entre des enregistrements d'une même table en utilisant l'ORM de Django ?

EDIT : J'ai trouvé ceci extrait utile pour inclure du SQL personnalisé via .extra() afin qu'il soit utilisable dans un gestionnaire de modèles.

15voto

Tommaso Barbugli Points 3826

Je pense que ce qui vous manque est le paramètre select (pour la méthode supplémentaire).

Cela semble fonctionner :

qs = Triple.objects.filter(subject="bob", predicate="knows").extra(
          select={'known': "t1.subject"},
          tables=['"triple_triple" AS "t1"'],
          where=['''triple_triple.object=t1.subject 
                    AND t1.predicate="has-a" AND t1.object="'''])

qs.values("known")

0voto

David Stockwell Points 21

J'ai eu le même problème : Django échappe (ajoute des points arrière) aux noms de mes tables, ce qui signifie que je ne peux pas ajouter un alias manuellement ; la clause FROM qui en résulte ressemble à ceci :

"mytable" AS T100

Mais en même temps, Django ne créera pas automatiquement des alias pour vous si la table est déjà mentionnée ; au lieu de cela, il ignore les tables et ajoute simplement les clauses WHERE comme si elles faisaient référence aux tables originales.

La documentation de Django 1.8 suggère que .extra() crée des alias pour vous :

https://docs.djangoproject.com/en/1.8/ref/models/querysets/#django.db.models.query.QuerySet.extra

Mais cela ne semble pas être le cas pour ma requête, peut-être parce que la table d'origine fait partie d'un LEFT OUTER JOIN plutôt que d'une simple clause FROM x,y,z.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X