2011-04-13 4 views
0

Supposons que le modèle X comporte un champ n (un entier). Maintenant, j'ai aussi un modèle Y qui a des champs n et m (entiers). Est-il possible d'utiliser Django ORM pour sélectionner x (modèle X) de telle sorte qu'il existe y (modèle Y) tel que x.n = y.n et y.m = m pour une valeur donnée de m?Jointures arbitraires dans les modèles Django

S'il vous plaît ne me conseille pas d'introduire une relation ForeignKey entre les deux modèles ou quelque chose comme ça. J'aimerais savoir précisément s'il est possible d'y parvenir sans modifier le modèle. Dans le cas précis sur lequel je travaille, les relations données sont génériques. Et le côté opposé de la relation générique peut être n'importe quoi, donc selon les docs je ne peux pas vraiment introduire GenericRelation plusieurs fois dans différents modèles.

+0

Est-ce seulement moi ou est-ce une grave lacune dans la fonctionnalité? Comment est-ce possible que ce ne soit pas trivial? Je commence à penser que le fait qu'ils gardent leur code dans svn est un mauvais signe. – julkiewicz

+0

Dans le monde Django, moins contraignant que vous ne le pensez. Si vous voulez faire des jointures arbitraires, le SQL brut (retournant les objets du modèle réel) est là pour vous. Et ... vous savez, j'aime git et Mercurial aussi, mais si la plus grande plainte à propos d'un projet open source est son goût pour les outils de contrôle de code source, on a atteint le paradis. – Christophe

+0

@Christophe Le problème avec SQL brut est que vous avez besoin de générer des noms de table à la main, les noms de colonnes à la main. Et je pense que cela rend la transaction actuelle aussi sale. Je sais que c'est possible, mais si chaque fois que j'ai besoin de faire quelque chose d'un peu inhabituel, j'ai besoin d'écrire SQL à la main, je préfère ne pas utiliser ORM du tout. Après un bon moment passé avec Django ORM, je pense que c'est sérieusement cassé par le design. – julkiewicz

Répondre

4

je suppose que cela pourrait être atteint usi ng extraQuerySet méthode.

(je ne l'ai pas testé)

X.objects.extra(where=["x.n in (select y.n from y where y.m = '%s')"], params=['m_value']) 
+0

Eh bien, cela fonctionne après quelques changements mineurs dans la dénomination. D'après ce que j'ai lu, il fonctionne aussi mieux qu'un JOIN régulier. Merci. – julkiewicz

+0

queryset.query.get_initial_alias() pour obtenir l'alias affecté à la table primaire dans la requête. aussi SomeModel._meta.db_table a le nom de la table sous-jacente pour n'importe quel modèle. –

1

Vous pouvez le faire en utilisant sql brut:

def my_custom_sql(m): 
    from django.db import connection, transaction 
    cursor = connection.cursor() 

    # Data retrieval operation - no commit required 

    command = """SELECT * 
     FROM tX 
INNER JOIN tY 
     ON (tX.n=tY.n AND tY.m=%s)""" 

    cursor.execute(command % str(m)) 
    rows = cursor.fetchall() 

    return rows 

utilisant le ORM, je pense que vous pouvez le faire en utilisant values_list et le filtre in:

class X(models.Model): 
    n = models.IntegerField() 

class Y(models.Model): 
    n = models.IntegerField() 
    m = models.IntegerField() 

xs = X.objects.filter(n__in=Y.objects.filter(m=m).values_list('n')).distinct() 

modifier: Comme indiqué dans les commentaires cette méthode va frapper le db beaucoup

+1

Juste une note: le faire de la deuxième façon que @kriegar a fait là provoquera O (n + 1) hits à la DB pour chaque objet X, super mauvais !! et serait fortement déconseillé. Vous devriez utiliser la première méthode qu'il a posté. –

+0

Eh bien, le problème avec la deuxième façon est que c'est très inefficace, car cela transporte probablement une très longue liste de valeurs à la base de données ... La première façon, eh bien je n'ai pas le choix. Peut-être que je vais essayer de l'empaqueter dans un gestionnaire. Cependant je pense que je commence à développer une haine pour ce cadre. Argh !!! – julkiewicz

Questions connexes