Получение отношения "один ко многим" из определенной модели в 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

Вернуться на верх