Django 模型管理器

Posted

技术标签:

【中文标题】Django 模型管理器【英文标题】:Django Model Manager 【发布时间】:2021-04-05 08:16:42 【问题描述】:

我有一个 Django 模型,用户可以在其中创建对象并将它们保密一段时间。

class MyModel(models.Model):
    creator = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
    private_until = models.DateField(null=True, default=None, blank=True)
    objects = MyModelManager()

在我的 ListView 中,我希望访问者只显示“非私有”对象,而对于经过身份验证的用户,我希望显示“非私有”对象以及他们自己的私有对象。所以我的经理看起来像这样:

class MyModelManager(models.Manager):
    def include_his_private(self, user):
        if user.is_authenticated:
            include_his_private = super().get_queryset().filter(~Q(private_until__gt=datetime.date.today()))
            include_his_private |= super().get_queryset().filter(Q(creator=user))
            return include_his_private
        else:
            return super().get_queryset().filter(~Q(private_until__gt=datetime.date.today()))

    def get_queryset(self):
        return super().get_queryset().filter(~Q(private_until__gt=datetime.date.today()))

对于我的ListView,这很好用。但是当我单击一个对象以获取其DetailView 时,我在“URL-Level”上得到一个 404 错误。在我的网址中:

    path('<slug:slug>', MyModelDetailView.as_view(), name='mymodel_detail'),

...不知何故,在我有机会将用户传递之前,Django 已经提前检查了整个 slug 与每个 Manager 允许的 slug。有什么方法可以解决我的问题?任何帮助表示赞赏。提前致谢!

编辑: 我的 DetailView 如下所示:

class MyModelDetailView(DetailView):
     model = MyModel     
     template_name = 'mymodel_detail.html'      

     def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
         slug = self.kwargs['slug']
         if self.request.user.is_authenticated:
            obj = MyModel.objects.include_his_private(self.request.user).get(slug=slug)       
         else:
             obj = MyModel.objects.get(slug=slug)

【问题讨论】:

你的MyModelDetailView看起来怎么样? 请edit提问。 已编辑原帖,谢谢指点。 【参考方案1】:

DetailView [Django-doc] 将使用 .get_queryset(…) [Django-doc] 获取查询集,并且在 .get_object(…) method [Django-doc] 中,如果路径包含此类 URL 参数,它将自动过滤 pk 和/或 slug。因此,这是在您运行get_context_data 中的自定义逻辑之前完成的。

因此,您应该重写 get_queryset 方法:

class MyModelDetailView(DetailView):
     model = MyModel     
     template_name = 'mymodel_detail.html'

    def get_queryset(self, *args, **kwargs):
        return MyModel.objects.include_his_private(
            self.request.user
        )

【讨论】:

非常感谢!很简单,一旦你看到解决方案:-/我认为“get_QuerySet”只在 ListView 上有意义:-/ @zeus: 好吧,DetailView 基本上只是在此之上构建了额外的逻辑:因此它将使用get_queryset,然后使用 slug/pk 执行额外的过滤以检索单个对象。所以它永远不会将get_queryset 作为一个集合来处理。

以上是关于Django 模型管理器的主要内容,如果未能解决你的问题,请参考以下文章

Django 模型管理器

Django基础五之django模型层之关联管理器

django的模型类管理器-----------数据库操作的封装

python Django模型管理器

django 无法在指定中间模型的 ManyToManyField 上设置值。改用管理器

2 Model层-模型成员