2017-04-11 20 views
1

私は次のような関係持って考える:1つのクエリで関連モデルの選択:左結合、prefetch_relatedまたはselect_related?

class House(Model): 
    name = ... 

class User(Model): 
    """The standard auth model""" 
    pass 

class Alert(Model): 
    user = ForeignKey(User) 
    house = ForeignKey(House) 
    somevalue = IntegerField() 

    Meta: 
    unique_together = (('user', 'property'),) 

を、私は家のリストを取得したいと思い、現在のユーザーはそれらのいずれかのための任意のアラートを持っているかどうか。

私はこのようにそれを行うだろうSQLで

SELECT * 
    FROM house h 
LEFT JOIN alert a 
     ON h.id = a.house_id 
    WHERE a.user_id = ? 
     OR a.user_id IS NULL 

そして私は、私はこのような何かを達成するためにprefetch_relatedを使用することができることを発見しました:

p = Prefetch('alert_set', queryset=Alert.objects.filter(user=self.request.user), to_attr='user_alert') 
houses = House.objects.order_by('name').prefetch_related(p) 

上記の例は動作しますが、しかしhouses.user_alertリストであり、Alertオブジェクトではありません。私は家ごとにユーザーごとに1つのアラートしか持っていないので、私はこの情報を得るための最良の方法は何ですか?

select_relatedは動作していないようです。ああ、確かに私は複数のクエリでこれを管理することができますが、私は本当に1つの "Djangoの方法"でそれをやりたいと思います。

ありがとうございます!

答えて

0

複数のクエリのアプローチから始めて最適化を行うと、ソリューションがより明確になります。すべての家のためのuser_alertsを取得するには、次の操作を行うことができます:

houses = House.objects.order_by('name') 
for house in houses: 
    user_alerts = house.alert_set.filter(user=self.request.user) 

user_alertsクエリセットは、クエリセット内のすべての家のための余分なクエリが発生します。これはprefetch_relatedで回避できます。

alerts_queryset = Alert.objects.filter(user=self.request.user) 
houses = House.objects.order_by('name').prefetch_related(
    Prefetch('alert_set', queryset=alerts_queryset, to_attrs='user_alerts'), 
) 
for house in houses: 
    user_alerts = house.user_alerts 

これは、住宅用とアラート用の2つのクエリを使用します。 self.request.userで既にユーザーにアクセスしているため、ここで選択したユーザーを取得する必要はありません。あなたはalerts_querysetselect_relatedを追加することができますしたい場合:

alerts_queryset = Alert.objects.filter(user=self.request.user).select_related('user') 

をお使いの場合には、user_alertsがあるため、あなたのunique_together制約のため、空のリストまたは1つのアイテムを持つリストになります。あなたがリストを処理できない場合は、クエリセットをループに一度、およびhouse.user_alertを設定できます

for house in houses: 
    house.user_alert = house.user_alerts[0] if house.user_alerts else None 
+0

おかげで、私はあなたのコードのいくつかのタイプミスがあると思います...私はそれが働いて得ることができたのに。それは私がリストを得る問題を解決するものではなく、一つのアイテムではない。 'user_alerts'を' Alert'オブジェクトにできると思いますか? – FMCorz

+0

私はコードを実行していないので、私はどんな[編集](http://stackoverflow.com/posts/43342718/edit)でも入力ミスを受け入れることができます。いいえ、Djangoに 'user_alerts'をリストではなくオブジェクトにするよう指示する方法はわかりません。オブジェクトが本当に必要な場合は、クエリーセットを1回ループし、答えの最後に提案したように 'house.user_alert'を設定します。 – Alasdair

+0

私は 'user_alerts.first()'もやっていると思っていましたが、それはそれがないときれいになります。 – FMCorz

関連する問題