Получение отношения "один ко многим" из определенной модели в django
У меня есть две модели User и Sale, у каждой продажи есть покупатель и продавец. Я хотел бы получить всех покупателей (также пользователей) от определенного пользователя my_user.
class User(models.Model):
name = models.CharField(max_length=250, default='', blank=True)
# more fields....
class Sale (models.Model):
buyer = models.ForeignKey(
User,
blank=False,
null=False,
related_name='purchases',
on_delete=models.CASCADE,
)
seller = models.ForeignKey(
User,
blank=False,
null=False,
related_name='sales',
on_delete=models.CASCADE,
)
# more fields....
Это работает нормально:
User.objects.filter(purchases__seller = my_user)
Но я хотел бы знать, есть ли способ сделать это, используя объект my_user напрямую. Что-то вроде этого
my_user.sales.buyer
Заранее спасибо, ребята
Вы можете получить доступ к продажам и покупкам конкретного пользователя следующим образом:
q = my_user.sale_set.all()
или
q = my_user.sale_set.filter(your fileter)
тогда вы можете получить доступ к покупателю или продавцу, используя :
buyer = q.buyer
seller = q.seller
но помните об использовании q.last(), потому что вы не сможете получить доступ к покупателю или продавцу в списке наборов запросов .
Надеюсь, это помогло достаточно.
вы можете использовать django value_list
https://docs.djangoproject.com/en/dev/ref/models/querysets/#values-list
Итак, вы можете сделать это, используя следующий код
buyer_list = my_user.sales.values_list('buyer', flat=True)
Дополнительно, если вы хотите удалить дублирующееся значение, используйте опцию Find distinct.
https://docs.djangoproject.com/en/4.0/ref/models/querysets/#distinct
Между вашей фразой и решением seokmin есть большая разница:
my_user.sales.values_list('buyer', flat=True) # returns a list of ids
User.objects.filter(purchases__seller=my_user) # returns a QuerySet
Вы можете сделать имхо более уродливое понимание (select_related гарантирует, что у вас есть все данные о покупателях и не будет посылать один запрос на каждую продажу, больше здесь) :
buyers = {sale.buyer for sale in my_user.sales.select_related('buyer')}
Получение от нескольких пользователей
Гораздо хуже будет, если у вас будет не один пользователь, а несколько, что, как я думаю, в конечном итоге и произойдет. Вам придется применить немного больше магии, если вы хотите, чтобы пользователи выходили, чтобы предотвратить обращение к БД слишком много раз.
По мотивам вашего оригинального запроса можно сделать очень простой сниппет из 2 строк:
user_ids = my_user_qs.values_list('id', flat=True)
buyers = User.objects.filter(purchases__seller__id__in=user_ids)
Легко.
Теперь представим, что вы хотите повторно использовать buyers = {sale.buyer... как метод для пользователя, назовем его user.get_buyers():
buyers = [user.get_buyers() for user in my_users]
Это будет выполнять 1 запрос к БД для каждого пользователя, и, вероятно, будет катастрофически медленным даже при сотнях пользователей.
Как вы это оптимизируете?
Сначала вы должны определить Prefetch - это указывает Django загрузить дополнительные данные, когда он первоначально выполняет ваш запрос для пользователей.
from django.db.models import Prefetch
sales_prefetch = Prefetch('sales', Sale.objects.select_related('buyer', 'seller')
# no query runs yet
В префетче говорится "при оценке набора запросов, к которому применяется этот префетч, одновременно выполните запрос Sale и присоедините его к подполю 'sales'. Более того, когда вы будете это делать, соедините таблицу продаж с таблицей пользователей для покупателя и продавца".
Затем вы должны убедиться, что при выполнении запроса, который дает вам my_users, вы используете эту предварительную выборку.
my_users = Users.objects.prefetch_related(sales_prefetch)
Это позволяет вам сделать следующее:
buyers = [user.get_buyers() for user in my_users]
# sends a single query and returns a list of lists of User objects
# you still have to flatten the list of lists
Заключение
В целом, вам будет лучше, если вы выполните свой первоначальный запрос через модель User. С предварительной выборкой вы все еще рискуете использовать связанное поле для пользователя, для которого вы не выполнили предварительную выборку/select_related, и вы будете выполнять запрос к БД для каждого элемента в конечном списке.
Я настоятельно рекомендую прочитать и понять оба select_related и prefetch_related, поскольку они являются способом Django правильно делать JOINs.
https://docs.djangoproject.com/en/4.0/ref/models/querysets/#select-related https://docs.djangoproject.com/en/4.0/ref/models/querysets/#prefetch-related