#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, да, мы заметили это, когда работали над формами, но к тому времени мы просто оставили все как есть. Тем не менее, я попытаюсь это исправить.