Django Jointure Externe Gauche

J'ai un site web où les utilisateurs peuvent voir une liste de films, et de créer des revues pour eux.

L'utilisateur doit être en mesure de voir la liste de tous les films. En outre, SI ils ont revu le film, ils devraient être en mesure de voir le score qu'ils ont donné. Si non, le film est simplement affiché sans le score.

Ils ne se soucient pas du tout sur les scores fournis par d'autres utilisateurs.

De considérer les éléments suivants models.py

from django.contrib.auth.models import User
from django.db import models


class Topic(models.Model):
    name = models.TextField()

    def __str__(self):
        return self.name


class Record(models.Model):
    user = models.ForeignKey(User)
    topic = models.ForeignKey(Topic)
    value = models.TextField()

    class Meta:
        unique_together = ("user", "topic")

Ce que j'ai essentiellement voulez, c'est ce

select * from bar_topic
left join (select topic_id as tid, value from bar_record where user_id = 1)
on tid = bar_topic.id

De considérer les éléments suivants test.py pour le contexte:

from django.test import TestCase

from bar.models import *


from django.db.models import Q

class TestSuite(TestCase):

    def setUp(self):
        t1 = Topic.objects.create(name="A")
        t2 = Topic.objects.create(name="B")
        t3 = Topic.objects.create(name="C")
        # 2 for Johnny
        johnny = User.objects.create(username="Johnny")
        johnny.record_set.create(topic=t1, value=1)
        johnny.record_set.create(topic=t3, value=3)
        # 3 for Mary
        mary = User.objects.create(username="Mary")
        mary.record_set.create(topic=t1, value=4)
        mary.record_set.create(topic=t2, value=5)
        mary.record_set.create(topic=t3, value=6)

    def test_raw(self):
        print('\nraw\n---')
        with self.assertNumQueries(1):
            topics = Topic.objects.raw('''
                select * from bar_topic
                left join (select topic_id as tid, value from bar_record where user_id = 1)
                on tid = bar_topic.id
                ''')
            for topic in topics:
                print(topic, topic.value)

    def test_orm(self):
        print('\norm\n---')
        with self.assertNumQueries(1):
            topics = Topic.objects.filter(Q(record__user_id=1)).values_list('name', 'record__value')
            for topic in topics:
                print(*topic)

Les DEUX tests doivent imprimer exactement la même sortie, cependant, que la première version crache le bon tableau des résultats:

raw 
--- 
1 
B Aucun 
C 3

l'orm au lieu de cela renvoie cette

orm 
--- 
1 
C 3

Toute tentative de joindre à dos le reste des sujets, ceux qui n'ont pas de commentaires de l'utilisateur "johnny", le résultat suivant:

orm
---
A 1
A 4
B 5
C 3
C 6

Comment puis-je accomplir le simple comportement de la matière première requête avec l'ORM de Django?

edit: Ce genre de travaux, mais semble très pauvres:

sujets = Sujet.objets.filtre(record__utilisateur_id=1).values_list('nom', 'enregistrer__valeur') 
noned = Sujet.objets.exclure(record__utilisateur_id=1).values_list('nom') 
pour le sujet en chaîne(sujets, noned): 
...

edit: Cela fonctionne un peu mieux, mais toujours mauvais:

 sujets = Sujet.objets.filtre(record__utilisateur_id=1).annoter(valeur=F (record__valeur')) 
sujets |= Sujet.objets.exclure(pk__dans=les sujets)
orm 
--- 
1 
B 5 
C 3
  • Cette question semble très relative: stackoverflow.com/questions/6500066/...
  • Je suis maintenant à la recherche à RawSQL: docs.djangoproject.com/en/1.9/ref/models/expressions/...
  • quel est le problème avec Record.objects.get(user=mary, topic=A)?
  • il se complique pas faire ce que je veux. il ne me le les dossiers où Marie a écrit quelque chose, et le sujet est A. je veux que tous les sujets, et pas seulement Un, et pas seulement ceux pour lesquels Marie a écrit un examen.
  • plus de ressources: djangosnippets.org/snippets/236
  • Pour clarifier, basé sur l'un de vos commentaires, est-ce juste pour SQLite?
  • SQLite serait bien que c'est la valeur par défaut pour le test, mais je veux juste savoir la meilleure façon possible de rapprocher la coquette requête SQL, j'ai près du début de la question.
  • L'unicité de l'Enregistrement peuvent être plus explicites déclaré (que l'Utilisateur peut avoir qu'un seul Enregistrement pour le Sujet), par class Meta: unique_together = ("user", "topic"). C'est une condition fondamentale pour la question et pour l'ensemble des quatre solutions actuelles.
  • Dans le futur, vous serez en mesure d'annotation d'une sous-requête sur le queryset: github.com/django/django/pull/6478
  • hâte de

InformationsquelleAutor RodericDay | 2016-06-27