Django ManyToMany filter()


131

私はモデルを持っています:

class Zone(models.Model):
    name = models.CharField(max_length=128)
    users = models.ManyToManyField(User, related_name='zones', null=True, blank=True)

そして、私は次のようにフィルターを構築する必要があります:

u = User.objects.filter(...zones contains a particular zone...)

これはユーザーのフィルターである必要があり、単一のフィルターパラメーターである必要があります。これは、管理ユーザーの変更リストをフィルターするためにURLクエリ文字列を作成しているためです。http://myserver/admin/auth/user/?zones=3

シンプルなようですが、頭がおかしいです!


8
私は右のあなたを取得する場合、私はよく分からない-ではないUser.objects.filter(zones__id=<id>)か、User.objects.filter(zones__in=<id(s)>)このために良いですか?
TomaszZieliński、2010

それは大丈夫です:) BTW User.objects.filter(zones__in=<id(s)>)はおそらくあるはずですUser.objects.filter(zones__id__in=<id(s)>)
TomaszZieliński

21
これをグーグルする人に指摘したいのは、related_nameが設定されている場合にのみ機能することです。たとえば、zone_setは機能しません。その上で30分を無駄にしました:-)

回答:


155

トマシュが言ったことを言い直してください。

対多および多対1のテストにFOO__in=...は、スタイルフィルターの多くの例があります。特定の問題の構文は次のとおりです。

users_in_1zone = User.objects.filter(zones__id=<id1>)
# same thing but using in
users_in_1zone = User.objects.filter(zones__in=[<id1>])

# filtering on a few zones, by id
users_in_zones = User.objects.filter(zones__in=[<id1>, <id2>, <id3>])
# and by zone object (object gets converted to pk under the covers)
users_in_zones = User.objects.filter(zones__in=[zone1, zone2, zone3])

二重下線(__)構文は、querysetsを操作するときに至る所で使用されます。


@maxmに感謝します。いくつかの例へのより最新のリンクで更新されました。
istruble

9
ダブルアンダースコア(
argh。3

ゾーンのセットにいるユーザーを、それらの1つだけではなくどうしたいのですか?ゾーン1、ゾーン3、..およびゾーン10にいるユーザーを検索するとします
FRR

...__in後の例を見てください# filtering on a few zones, by id。これらは、複数のID /オブジェクト(この場合)のフィルタリングを示しています。気になるゾーン1、ゾーン3、ゾーン10のID /オブジェクトを渡すだけです。または、必要に応じて4を追加します。
istruble 2018年

どうも。単一の値を含む配列ではなく、単一の値に対してのみフィルタリングしていました。
zypro

36

ユーザーがクエリで使用される複数のゾーンにいる可能性がある場合は、おそらく.distinct()を追加する必要があります。それ以外の場合は、1人のユーザーを複数回取得します。

users_in_zones = User.objects.filter(zones__in=[zone1, zone2, zone3]).distinct()

1

これを行う別の方法は、中間テーブルを通過することです。これをDjango ORM内で次のように表現します。

UserZone = User.zones.through

# for a single zone
users_in_zone = User.objects.filter(
  id__in=UserZone.objects.filter(zone=zone1).values('user'))

# for multiple zones
users_in_zones = User.objects.filter(
  id__in=UserZone.objects.filter(zone__in=[zone1, zone2, zone3]).values('user'))

.values('user')指定する必要がなければいいのですが、Django(バージョン3.0.7)では必要なようです。

上記のコードは、最終的に次のようなSQLを生成します。

SELECT * FROM users WHERE id IN (SELECT user_id FROM userzones WHERE zone_id IN (1,2,3))

重複したユーザーが返される可能性のある中間結合がないため、これは便利です


ヒヤ。これ自体は答えではありません。部分的な回答を追加するのではなく、コメントを追加するか、QBの回答を編集する必要があります。
アンディベイカー

ええ-答えを編集して完全に完全なものにしたい場合(QBの答えを編集するのに十分なカルマがない場合)、それが最善の策です。StackOverflowには、「正しい答えが1つ」あるのが理想的です。通常はそれほどうまく機能しませんが、狙う価値はあります。
アンディベイカー

@AndyBakerは同意しました!振り返ってみると、QBの答えはおそらくistrubleの答えに関するコメントであるはずですが、私は別の答えを正当化するのに十分なほどはっきりしていると思いますが、そうですね
Sam Mason
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.