django管理表单的动态操作
我有一个修改后的管理表单,其中添加了一个字段,该字段将修改当前模型的父对象的值。现在,根据用户的不同,我需要
- 更改该额外字段的查询集
- ,将另一个字段设置为只读(或者更好,甚至完全隐藏它)
基本上我下面的代码按照我的预期工作。超级用户获取整个查询集,而另一个字段不是只读。所有其他用户都会获得有限的查询集,而其他字段是只读的。但是,一旦我以非超级用户的身份在不同的浏览器中打开该网站,即使是超级用户也会得到与非超级用户相同的结果。看起来 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
是的,你做错了。在 Django 1.2+ 中,您可以使用 get_readonly_fields。
来自这个答案:
关于更改 查询集。从文档中:
Yes you are doing it wrong. In Django 1.2+ you can use get_readonly_fields.
From this answer:
Regarding altering the queryset. From the documentation:
扩展 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 theModelAdmin.get_readonly_fields
method which has this signature:get_readonly_fields(self, request, obj=None)
.所以我找不到一种真正聪明的方法来使用 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 extendingadmin/change_form.html
but rendering my own form instead of the default one. I also setextra_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.