我在Django中有一个ListView
,我列出了所有对象。一些对象由经过身份验证的用户添加书签。
每个对象都有bookmarks = GenericRelation(Bookmark)
的关系。
如何查看看到ListView
的用户是否为每个特定对象添加了书签?
我认为如果我在模板中检查它会花费很多sql查询。
我的Bookmark
型号:
class Bookmark(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey()
user = models.ForeignKey(settings.AUTH_USER_MODEL)
我的Post
型号:
class Post(models.Model):
title = models.CharField(max_length=100)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
bookmarks = generic.GenericRelation(Bookmark)
我的列表视图:
class PostListView(ListView):
model = Post
template_name = 'resource/post_list.html'
我的猜测是我需要创建一个方法get_queryset()
,其中我为每个对象检查是否存在user=self.request.user
的书签对象。
我不想过滤任何东西。我想要模型中的所有对象,但是当我在模板中循环查询集时,我想确定经过身份验证的用户是否已为特定对象添加了书签。所以我想我需要在一个额外的字段中填充查询集中的每个对象,告诉用户是否已经为特定对象添加了书签。我只是不知道该怎么做。
答案 0 :(得分:3)
1)使用django extra https://docs.djangoproject.com/en/dev/ref/models/querysets/#extra:
def get_queryset(self):
qs = super(PostListView, self).get_queryset()
annotated_qs = qs.extra(select=OrderedDict([('bookmarked', 'SELECT CASE WHEN user_id = %s THEN "true" ELSE NULL END FROM resource_bookmark WHERE object_id = resource_post.id AND content_type_id = %s'), ('has_voted', 'provide_some_query_that_returns_one_column_and_one_row'), ]), select_params=[self.request.user.id, ContentType.objects.get_for_model(self.model).id])
return annotated_qs
主要的想法是,您可以使用额外的select语句访问每行中的some_app_post
。bookmarks_id
,使用该ID选择bookmarks
对象并添加到此行的额外列{{ 1}}值取决于条件bookmarked
答案 1 :(得分:2)
我有一个更简单的设置,其中对象可以将粉丝(用户)作为ManyToMany关系。我标记当前用户“喜欢”哪些项目的解决方案是使用模型的ID向上下文添加列表,然后在模板中检查此列表。它可能不是最有效的方式,但它对我来说足够快。
您可能需要调整它以使其适应您的通用关系,但代码如下:
class PostListView(ListView):
model = Post
template_name = 'resource/post_list.html'
def get_context_data(self, **kwargs):
context = super(PostListView, self).get_context_data(**kwargs)
if self.request.user.is_authenticated():
context['user_favs'] = Post.objects.filter(
bookmarks__username=self.request.user.username).values_list(
'id', flat=True)
return context
模板:
{% if item.id in user_favs %}
... Your special code here ...
{% endif %}
答案 2 :(得分:0)
看一下the documentation for Django's ListView class,我发现这个类继承自django.views.generic.list.MultipleObjectMixin
类,等等。这意味着你的直觉是正确的。您需要覆盖get_queryset()
类继承自ListView
类的MultipleObjectMixin
方法。
From the documentation about get_queryset()
:
获取此视图的项目列表。这必须是可迭代的,并且可以是查询集(其中将启用查询集特定的行为)。
这只是意味着您应该覆盖此方法以返回包含您希望ListView显示给用户的对象的查询集。您希望的行为是过滤ListView中显示的Post对象,以仅显示用户具有现有书签的Post对象。
还有一个关于如何获取用户对象进行过滤的问题。 get_queryset()
方法似乎没有收到request
对象,因此我们无法直接访问此方法中的request.user
。在Django ListView的文档中,有一个简短的流程图,说明了为该视图调用的方法,以及它的顺序。
根据此流程图,首先运行dispatch()
方法。更重要的是,它在get_queryset()
方法之前运行,因此我们可以存储来自调度方法的数据,以便以后在get_queryset方法中使用。您可以在视图的dispatch()
方法(documentation here)中获取该用户的副本。
首先,覆盖dispatch()
方法,以便您可以保留request.user的副本,以便以后在get_queryset()
中使用。像这样:
def dispatch(self, request, *args, **kwargs):
self.user = request.user
return super(PostListView, self).dispatch(request, *args, **kwargs)
以下是如何覆盖get_queryset()
以仅过滤具有与当前用户对象关联的书签的Post对象的示例:
def get_queryset(self):
queryset_with_all_posts = super(PostListView, self).get_queryset()
queryset_for_only_this_user = queryset_with_all_posts.filter(bookmarks__user=self.user)
return queryset_for_only_this_user