У меня есть длинный список ссылок, которые я выкладываю, используя приведенный ниже код, общее количество голосов, поданных обычными вещами, но я не на 100% уверен в том, как определить, проголосовал ли пользователь, вошедший в данный момент, по ссылке или не. Я знаю, как это сделать из своего представления, но нужно ли мне изменить код, представленный ниже, или я могу использовать способ работы шаблонов для его определения?
Я прочитал Метод голосования Django Up / Down , но я не совсем понимаю, что происходит (и мне не нужна javascriptery).
Модели (фрагмент):
class Link(models.Model):
category = models.ForeignKey(Category, blank=False, default=1)
user = models.ForeignKey(User)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
url = models.URLField(max_length=1024, unique=True, verify_exists=True)
name = models.CharField(max_length=512)
def __unicode__(self):
return u'%s (%s)' % (self.name, self.url)
class Vote(models.Model):
link = models.ForeignKey(Link)
user = models.ForeignKey(User)
created = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return u'%s vote for %s' % (self.user, self.link)
Просмотры (фрагмент):
def hot(request):
links = Link.objects.select_related().annotate(votes=Count('vote')).order_by('-created')
for link in links:
delta_in_hours = (int(datetime.now().strftime("%s")) - int(link.created.strftime("%s"))) / 3600
link.popularity = ((link.votes - 1) / (delta_in_hours + 2)**1.5)
if request.user.is_authenticated():
try:
link.voted = Vote.objects.get(link=link, user=request.user)
except Vote.DoesNotExist:
link.voted = None
links = sorted(links, key=lambda x: x.popularity, reverse=True)
links = paginate(request, links, 15)
return direct_to_template(
request,
template = 'links/link_list.html',
extra_context = {
'links': links,
})
Приведенный выше взгляд на самом деле выполняет то, что мне нужно, но в том, что я считаю ужасно неэффективным способом. Это вызывает страшные n + 1 запросы, так как на данный момент это 33 запроса для страницы, содержащей всего 29 ссылок, в то время как изначально мне это удалось, всего 4 запроса. Я действительно предпочел бы сделать это, используя ORM Джанго или, по крайней мере, .extra ().
Любой совет?
EDIT
@ Габриэль Херли
Я пытаюсь воссоздать ваш ответ, и у меня смешанные результаты, позвольте мне показать вам, что я получил.
views.py
links = Link.objects.select_related().extra(
select={
'votes': 'COUNT(links_vote.id)',
'voted': 'SELECT COUNT(links_vote.id) FROM links_vote WHERE links_vote.user_id = 1 AND links_vote.link_id = links_link.id',
},
tables = ['links_vote']
)
models.py
class Vote(models.Model):
link = models.ForeignKey(Link)
user = models.ForeignKey(User)
created = models.DateTimeField(auto_now_add=True)
class Meta:
unique_together = ('link', 'user')
def __unicode__(self):
return u'%s vote for %s' % (self.user, self.link)
Но возвращается ошибка:
subquery uses ungrouped column "links_link.id" from outer query
LINE 1: ... E links_vote.user_id = 1 И links_vote.link_id = links_link ...
Сгенерированный запрос выглядит примерно так:
SELECT (SELECT COUNT(links_vote.id) FROM links_vote WHERE links_vote.user_id = 1 AND links_vote.link_id = links_link.id) AS "voted", "links_link"."id", "links_link"."category_id", "links_link"."user_id", "links_link"."created", "links_link"."modified", "links_link"."url", "links_link"."name", "links_category"."id", "links_category"."name", "auth_user"."id", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."password", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."is_superuser", "auth_user"."last_login", "auth_user"."date_joined" FROM "links_link" INNER JOIN "links_category" ON ("links_link"."category_id" = "links_category"."id") INNER JOIN "auth_user" ON ("links_link"."user_id" = "auth_user"."id") , "links_vote"
Я использую PostgreSQL, который, как я знаю, любит GROUP BY, но я не на 100% уверен, как это исправить.
РЕДАКТИРОВАТЬ 2 (Большой прогресс)
links = Link.objects.select_related (). Annotate (голоса = Количество ('голосование')). Экстра (
выберите = {
# 'voted': 'SELECT COUNT () ОТ links_vote WHERE links_vote.user_id =% s AND links_vote.link_id = links_link.id'% (request.user.id),
# 'voted': ''% (request.user.id),
# 'voted': 'SELECT CASE WHEN links_vote.user_id =% s THEN 1 ELSE 0 END'% (request.user.id),
# 'voted': 'SELECT COUNT () ОТ links_vote WHERE links_vote.link_id = links_link.id AND links_vote.user_id =% s'% (request.user.id),
},
где = ['links_link.id = links_vote.link_id'],
) .Order_by ( '- создано')
* Это работает только после применения патча для ошибки здесь (http://code.djangoproject.com/ticket/11916)
Я так близок к тому, чтобы найти последний кусок, который мне нужен, чтобы определить, проголосовал ли пользователь ...