查看用户是否在列表中添加了书签对象

时间:2014-10-30 08:51:33

标签: django templates django-models django-templates django-views

我在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的书签对象。

编辑2

我不想过滤任何东西。我想要模型中的所有对象,但是当我在模板中循环查询集时,我想确定经过身份验证的用户是否已为特定对象添加了书签。所以我想我需要在一个额外的字段中填充查询集中的每个对象,告诉用户是否已经为特定对象添加了书签。我只是不知道该怎么做。

3 个答案:

答案 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_postbookmarks_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