【问题标题】:See if user has bookmarked object in list查看用户是否在列表中添加了书签
【发布时间】:2014-12-26 05:36:26
【问题描述】:

我在 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

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

【问题讨论】:

  • 您能分享您的书签模型和列出的对象模型及其字段吗?
  • 我已将模型和列表视图添加到我的问题中。
  • 我已经尽力帮助您并进行了一些研究 - Django 不提供“条件注释”,所以我认为您可以通过三种方式解决您的问题:1) 额外使用 django,但我对 stackoverflow.com/questions/26716843/… 2) 有问题,编写像 gist.github.com/vinilios/225997 这样的自定义聚合函数并将其与 django annotate 3) 一起使用定义两个查询集并将它们简单地组合到您的模板中
  • 已解决问题,请尽快发布解决方案

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


【解决方案1】:

查看the documentation for Django's ListView class,我发现这个类继承自django.views.generic.list.MultipleObjectMixin 等。这意味着你的直觉是正确的。您将需要覆盖 ListView 类从 MultipleObjectMixin 类继承的 get_queryset() 方法。

From the documentation about get_queryset():

获取此视图的项目列表。这必须是一个可迭代的并且可能是一个查询集(其中将启用特定于查询集的行为)。

这只是意味着您应该重写此方法以返回包含您希望 ListView 向用户显示的对象的查询集。您的预期行为是过滤 ListView 中显示的 Post 对象,以仅显示用户具有现有书签的 Post 对象。

还有一个额外的问题是如何获取用户对象进行过滤。 get_queryset() 方法似乎没有接收到request 对象,因此我们无法在该方法中直接访问request.user。在 Django ListView 的文档中,有一个简短的流程图,解释了为此视图调用了哪些方法,以及它的顺序。

根据此流程图,首先运行dispatch() 方法。更重要的是,它在get_queryset() 方法之前运行,因此我们可以存储来自dispatch 方法的数据,以供以后在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

【讨论】:

  • 感谢您的回答。我想要模型中的所有对象,但是当我在模板中循环我的查询集时,我想确定经过身份验证的用户是否为对象添加了书签。所以我想我需要用一个额外的字段来填充查询集中的每个对象,告诉用户是否为特定对象添加了书签。我只是不知道该怎么做。
  • 如果您使用我上面提供的查询过滤器来查找给定用户添加书签的帖子,它将起作用。它在 Post 模型的书签字段上使用辅助字段查找。 filter(bookmarks__user=self.user) 将只返回至少有一个书签包含对所提供用户的引用的帖子。
  • 如果您希望创建一个新列来指示用户是否为帖子添加了书签,您可以尝试looking at using annotation to accomplish this task
  • 我不想过滤任何东西。我只需要将额外信息添加到我的查询集中。似乎注释仅适用于摘要/汇总。
  • 您可能希望在此处使用您的 cmets 中的信息更新您的问题。这之前不清楚。
【解决方案2】:

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

主要思想是,您可以在额外的选择语句中访问每行中的 some_app_post.bookmarks_id,使用该 ID 选择 bookmarks 对象并根据条件向该行添加额外的列 bookmarkedbookmarks__user=self.request.user

【讨论】:

  • 这似乎与GenericRelation 不同:-( 我得到no such column: some_app_post.bookmarks_id
  • 将 some_app_bookmark 替换为 Bookmark 表名(在您的数据库中),将 some_app_post 替换为 Post 表名。
  • 例如你有应用程序bookmark和模型Bookmark,所以django默认为这个模型表创建名称bookmark_bookmark和模型Post在应用程序bookmark它应该是bookmark_post ...但无论如何你应该在你的数据库中查看这些名字
  • 感谢您的回答。我确实按照你说的替换了名字。我只是没有更改评论中的名称 :-) 我认为将 bookmarks_id 用于 GenericRelation 是不正确的,但我不知道它使用什么 sql 语法
  • 周末我会尝试实现第二个解决方案 - 注释的自定义类,主要目标是这样的:qs.annotate(bookmarked=IfEqual('bookmarks__user', self.request.user, 'success_value'))
【解决方案3】:

我有一个更简单的设置,其中对象可以拥有粉丝(用户)作为多对多关系。我标记当前用户“喜欢”哪些项目的解决方案是在上下文中添加一个包含模型 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 %}

【讨论】:

  • 是的,这是我对问题的评论中提到的第三种解决方案 :)
  • 你说的是两个查询集,这要简单得多。
  • 好的,您已经升级了第二个查询集 - 通过从模型中仅选择 ids 并从中列出列表,但想法相同 - 有两个查询:1) SELECT * FROM resource_bookmark 2) SELECT books_authors_post.id FROM books_authors_post INNER JOIN books_authors_bookmark ON (books_authors_post.id = books_authors_bookmark.object_id AND ( books_authors_bookmark.content_type_id = 11)) INNER JOIN auth_user ON (books_authors_bookmark.user_id = auth_user.id) WHERE auth_user.username=一些书签用户名)
猜你喜欢
  • 2014-08-23
  • 2010-10-05
  • 1970-01-01
  • 2017-01-28
  • 2021-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-10
相关资源
最近更新 更多