django管理表单的动态操作

发布于 2024-12-28 21:25:58 字数 1063 浏览 2 评论 0原文

我有一个修改后的管理表单,其中添加了一个字段,该字段将修改当前模型的父对象的值。现在,根据用户的不同,我需要

  • 更改该额外字段的查询集
  • ,将另一个字段设置为只读(或者更好,甚至完全隐藏它)

基本上我下面的代码按照我的预期工作。超级用户获取整个查询集,而另一个字段不是只读。所有其他用户都会获得有限的查询集,而其他字段是只读的。但是,一旦我以非超级用户的身份在不同的浏览器中打开该网站,即使是超级用户也会得到与非超级用户相同的结果。看起来 django 以某种方式缓存了结果?如果我将一些打印语句放入条件分支内,它们就会被正确打印。因此该方法仍然每次都会被调用,并且似乎仍然执行这些步骤。只有错误的结果。

这是缓存问题吗?我做错了什么吗?这可能是 django 测试服务器中的错误吗?

def get_form(self, request, obj=None, **kwargs):
    form = super(MultishopProductAdmin, self).get_form(request, obj, **kwargs)
    if obj is not None:
        form.declared_fields['categories'].initial = obj.product.category.all()
    if not request.user.is_superuser:
        user_site = request.user.get_profile().site
        form.declared_fields['categories'].queryset = Category.objects.filter(site__id=user_site.id)
        self.readonly_fields = ('virtual_sites', )
        if obj is not None:
            form.declared_fields['categories'].initial = obj.product.category.filter(site__id=user_site.id)
    return form

I have a modified admin form, where I added a field that shall modify the values of the current model's parent object. Now, depending on the user, I need to

  • alter the queryset of that extra field
  • set another field as readonly (or better, even hide it completely)

Basically my code below works as I'd expect it. A superuser gets the whole queryset and the other field is not readonly. All other users get a limited queryset and the other field is readonly. However, once I open that site in a different browser and as a non-superuser, even the superuser get the same result as the non-superusers. Seems like django somehow caches the result? If I put some print statements inside the conditional branches though, they get printed correctly. So the method still gets called each time and seems to still perform these steps. Only with a wrong outcome.

Is that a caching problem? Am I doing something entirely wrong? Can it be a bug in the django test server?

def get_form(self, request, obj=None, **kwargs):
    form = super(MultishopProductAdmin, self).get_form(request, obj, **kwargs)
    if obj is not None:
        form.declared_fields['categories'].initial = obj.product.category.all()
    if not request.user.is_superuser:
        user_site = request.user.get_profile().site
        form.declared_fields['categories'].queryset = Category.objects.filter(site__id=user_site.id)
        self.readonly_fields = ('virtual_sites', )
        if obj is not None:
            form.declared_fields['categories'].initial = obj.product.category.filter(site__id=user_site.id)
    return form

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

凉月流沐 2025-01-04 21:25:58

是的,你做错了。在 Django 1.2+ 中,您可以使用 get_readonly_fields

来自这个答案

对于收到的所有请求,ModelAdmin 仅实例化一次。因此,当您像这样定义只读字段时,您就将其永久设置。

关于更改 查询集。从文档中:

class MyModelAdmin(admin.ModelAdmin):
    def queryset(self, request):
        qs = super(MyModelAdmin, self).queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(author=request.user)

Yes you are doing it wrong. In Django 1.2+ you can use get_readonly_fields.

From this answer:

The ModelAdmin is only instantiated once for all requests that it receives. So when you define the readonly fields like that, you're setting it across the board permanently.

Regarding altering the queryset. From the documentation:

class MyModelAdmin(admin.ModelAdmin):
    def queryset(self, request):
        qs = super(MyModelAdmin, self).queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(author=request.user)
水中月 2025-01-04 21:25:58

扩展 dan-klasson 的答案:永远不要在任何 ModelAdmin 方法(如 self.readonly_fields)中设置实例属性,以防止出现您所描述的问题。仅使用 Django ModelAdmin 的选项(类级别)或方法来操作任何行为。对于只读字段,您可以使用具有以下签名的 ModelAdmin.get_readonly_fields 方法:get_readonly_fields(self, request, obj=None)

To expand on dan-klasson's anwer: Do not ever set instance attributes in any ModelAdmin method (like self.readonly_fields) to prevent issues like the one you described. Only use Django ModelAdmin's options (which are class-level) or methods to manipulate any behavior. Regarding readonly fields, you can use the ModelAdmin.get_readonly_fields method which has this signature: get_readonly_fields(self, request, obj=None).

浅忆 2025-01-04 21:25:58

所以我找不到一种真正聪明的方法来使用 django admin 的自定义方法来完成我想要的事情。我现在最终要做的是实现管理员的 change_view,手动设置我自己的表单并从那里执行所有自定义初始化。

然后,我通过设置 change_form_template 提供了一个自定义模板,它只是扩展 admin/change_form.html 但呈现我自己的表单而不是默认表单。我还设置了 extra_context['adminform'] = None 以便删除默认的管理表单。

这样我现在就可以按照我需要的方式自定义表单,但仍然可以使用所有其他管理便利。到目前为止,它似乎工作得很好。我认为这不是最优雅的解决方案,但却是我能想到的最好的解决方案。

So I couldn't find a really clever way to do what I wanted with django admin's custom methods. What I ended up doing now is implementing the admin's change_view, setting up my own form manually and performing all my custom initializations from there.

I then provided a custom template by setting change_form_template, which is simply extending admin/change_form.html but rendering my own form instead of the default one. I also set extra_context['adminform'] = None so the default admin form gets removed.

That way I can now customize my form the way I need it to be but still use all the other admin conveniences. So far it seems to work very nicely. Not the very most elegant solution either I think, but the best I could think of.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文