Более эффективный запрос Django (SQL)

#python #sql #django

#python #sql #django

Вопрос:

Работая над проектом Django, у меня есть база данных пользователей и произведений искусства, последнее из которых может понравиться. Я хотел бы запросить, чтобы узнать, сколько лайков на всех своих фотографиях у отдельного пользователя есть вместе. Я могу написать это в двух циклах for в views.py , но это медленно. Я также написал его как отдельный (ужасный) SQL-запрос, но я не уверен, как его правильно использовать, поскольку это больше не набор запросов (я думаю, мне пришлось бы запрашивать таким образом, чтобы я получал все необходимые данные?).

Конечная идея состоит в том, чтобы просто иметь таблицу, состоящую из пользователей, их электронных писем, количества лайков, полученных их фотографиями, и количества опубликованных изображений. Вот соответствующие модели (я использую таблицу Django auth_user по умолчанию для пользователей) и SQL-запрос.

 class Arts(models.Model):
user_id = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, unique=False)
title = models.CharField(max_length=100)
description = models.TextField(unique=False, null=False, blank=True)
timestamp = models.DateTimeField(default=timezone.now)
url = models.URLField()
likes = models.IntegerField(default=0)
  

Иллюстрация понравилась пользователю:

 class Like(models.Model):                                  
artwork = models.ForeignKey(Arts, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
  

мой views.py для цикла:

 def all_users(request):
...
liked = defaultdict()
for user in users_all:
    liked[user.id] = 0
    for artwork in Arts.objects.filter(user_id=user.id):
        liked[user.id]  = artwork.likes
...
  

SQL-запрос

     with connection.cursor() as cursor:
    sql = """
WITH lajki AS 
(SELECT user_id_id, likes FROM artists_arts ORDER BY likes) 
SELECT user_id_id, SUM(likes) AS suma 
FROM lajki 
GROUP BY user_id_id 
ORDER BY suma DESC
"""
    cursor.execute(sql)
    def namedtuplefetchall(cursor):
        desc = cursor.description
        nt_result = namedtuple('Result', [col[0] for col in desc])
        return [nt_result(*row) for row in cursor.fetchall()]
    liked = namedtuplefetchall(cursor)
  

Есть ли способ сделать это более эффективно?

Ответ №1:

Нет необходимости использовать необработанные запросы вообще. Вы можете запросить с:

из django.contrib.auth импортируйте get_user_model

 def all_users(request):
    users = get_user_model().objects.annotate(
        suma=Sum('arts__likes')
    ).order_by('-suma')
    # …  

Пользовательские объекты, возникающие из этого набора запросов, будут иметь дополнительный атрибут, .suma который будет суммировать likes связанные Arts объекты этого пользователя. Обычно это позволяет избежать временной таблицы и выполнить только один запрос.

Однако я не уверен, стоит ли хранить likes в Arts объекте. По сути, это дублирование данных (вы храните агрегированную форму данных в объекте). Оказывается, что синхронизировать данные даже в одной базе данных сложнее. Таким образом, было бы лучше подсчитывать лайки для Arts объектов по Like объекту.

Комментарии:

1. .аннотировать — это именно то, что я искал. Я подумал, что есть хороший способ сгруппировать две модели, но просто не мог понять это. Большое вам спасибо. Что касается лайков в arts, да, мы заметили это, когда работали над формами, но к тому времени мы просто оставили все как есть. Тем не менее, я попытаюсь это исправить.