Django ORM QuerySet пересечение по полю - PullRequest
1 голос
/ 03 января 2011

Это (псевдо) модели, которые у меня есть.

Blog:
  name
  etc...

Article:
  name
  blog
  creator
  etc

User (as per django.contrib.auth)

Итак, моя проблема заключается в следующем: у меня есть два пользователя.Я хочу получить все статьи, опубликованные двумя пользователями в одном и том же блоге (независимо от того, какой блог).Я не могу просто отфильтровать модель Статьи обоими пользователями, потому что это даст набор Статей, созданных обоими пользователями.Очевидно, не то, что я хочу.но можно ли как-то отфильтровать, чтобы получить все статьи, в которых поле объекта совпадает между двумя наборами запросов?

Ответы [ 2 ]

2 голосов
/ 03 января 2011

Это отличный вопрос для применения ORM Джанго: -)

В зависимости от того, какие параметры известны заранее, есть несколько способов приблизиться к этому.

Сценарий 1: Вы знаете пользователей и конкретный блог

Если вы думаете об одном конкретном блоге и хотите просто найти все статьи, написанные одним из авторов, вы можете использовать объект Q.Я не думаю, что это ваш сценарий, но я поставлю его на всякий случай:

from django.db.models import Q
Article.objects.filter(Q(creator=user1) | Q(creator=user2), blog=some_blog_instance)

Сценарий 2: Вы знаете только пользователей

Если вы хотите найти все блоги, в которых публиковались оба пользователя, а затем хотите узнать, какие статьи они опубликовали в этих блогах, вам следует начать с модели Blog:

from django.db.models import Q

# Find all blogs where both user1 and user2 have written articles
blogs = Blog.objects.filter(article__creator=user1).\
        filter(article__creator=user2).distinct()

# Now find which articles those were
for blog in blogs:
    articles = blog.article_set.filter(Q=(creator=user1) | Q=(creator=user2))

Редактирование на основе комментариев Пауло:

Вот тестовый набор моделей, который, как мне кажется, соответствует псевдокоду OP и который демонстрирует работу вышеуказанного кода (по крайней мере, для sqlite3 и postgres):

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

class Blog(models.Model):
    name = models.CharField(max_length=128)

class Article(models.Model):
    name = models.CharField(max_length=128)
    blog = models.ForeignKey(Blog)
    creator = models.ForeignKey(User)

Затем некоторые данные, где и пользователь1, и пользователь2 пишут статьи в блоге2:

u1 = User.objects.create(username='u1')
u2 = User.objects.create(username='u2')

b1 = Blog.objects.create(name='b1')
b2 = Blog.objects.create(name='b2')
b3 = Blog.objects.create(name='b3')

b1_art1 = Article.objects.create(name='b1_art1', blog=b1, creator=u1)
b2_art1 = Article.objects.create(name='b2_art1', blog=b2, creator=u1)
b2_art2 = Article.objects.create(name='b2_art2', blog=b2, creator=u2)

Запрос:

[b.name for b in Blog.objects.filter(article__creator=user1).\
    filter(article__creator=user2).distinct()]

создает:

[b2]

И SQL-код (мое тестовое приложение django было названо foo):

SELECT "foo_blog"."id", "foo_blog"."name" 
FROM "foo_blog" 
INNER JOIN "foo_article" ON ("foo_blog"."id" = "foo_article"."blog_id") 
INNER JOIN "foo_article" T4 ON ("foo_blog"."id" = T4."blog_id") 
WHERE ("foo_article"."creator_id" = 1  AND T4."creator_id" = 2 )

Итак, в то время как предложение WHERE имеет (A AND B), A и B, ссылающиеся на разные внутренние объединения ("foo_article"."creator_id" против T4."creator_id"), а не та же таблица (что и генерирует фильтр (A, B), (т.е.: WHERE ("foo_article"."creator_id" = 1 and "foo_article"."creator_id" = 2))

(WitПо поводу предложения distinct(), если вы добавите больше статей для любого автора, вы получите несколько b2 записей в результатах набора запросов.)

Как я уже сказал, это отличное упражнение ORM!

1 голос
/ 03 января 2011

Я помню, что читал что-то вроде «Django ORM, вы получаете там 85% времени, остальные 15% - это raw SQL » в презентации кого-то из основной команды - но я могу » больше не могу найти источник.

Ваша проблема, кажется, соответствует , что 15% необработанного SQL .

...