Python 管理中添加和更改页面的不同字段
我的admin.py中有一个django应用程序,具有以下类:Python 管理中添加和更改页面的不同字段,python,django,django-admin,Python,Django,Django Admin,我的admin.py中有一个django应用程序,具有以下类: class SoftwareVersionAdmin(ModelAdmin): fields = ("product", "version_number", "description", "media", "relative_url", "current_version") list_display = ["product", "version_number", "size", "curren
class SoftwareVersionAdmin(ModelAdmin):
fields = ("product", "version_number", "description",
"media", "relative_url", "current_version")
list_display = ["product", "version_number", "size",
"current_version", "number_of_clients", "percent_of_clients"]
list_display_links = ("version_number",)
list_filter = ['product',]
我想为添加页面设置这些文件,但为更改页面设置不同的字段。如何实现这一点?首先查看ModelAdmin类的源代码“
get\u form
和get\u formset
方法,这些方法位于django.contrib.admin.options.py
中。您可以重写这些方法并使用kwargs来获得所需的行为。例如:
class SoftwareVersionAdmin(ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
# Proper kwargs are form, fields, exclude, formfield_callback
if obj: # obj is not None, so this is a change page
kwargs['exclude'] = ['foo', 'bar',]
else: # obj is None, so this is an add page
kwargs['fields'] = ['foo',]
return super(SoftwareVersionAdmin, self).get_form(request, obj, **kwargs)
def add_view(self, request, extra_context=None):
return super().add_view(request)
def change_view(self, request, object_id, extra_context=None):
self.readonly_fields = ['name'] # this change persists in add_view()
return super().change_view(self, request, object_id)
这个特定的代码对我不起作用。 我只是稍微改变一下:
if obj: # obj is not None, so this is a change page
#kwargs['exclude'] = ['owner']
self.fields = ['id', 'family_name', 'status', 'owner']
else: # obj is None, so this is an add page
#kwargs['fields'] = ['id', 'family_name', 'status']
self.fields = ['id', 'family_name', 'status']
return super(YourAdmin, self).get_form(request, obj, **kwargs)
在Django 1.6中使用表单集,我得到了以下结果:
def get_formsets(self, request, obj=None):
if obj is None:
# It's a new object
for field, fieldset in {'hide_me_from_the_first_fieldset': 0,
'me_from_the_second': 1,
'and_me_too': 1}.items():
self.fieldsets[fieldset][1]['fields'].remove(field)
return super().get_formsets(request, obj)
编辑:
也许更直观的方法是指定一个单独的add_fieldset
属性并执行以下操作:
def get_formsets(self, request, obj=None):
if obj is None:
self.fieldsets = self.add_fieldsets
return super().get_formsets(request, obj)
使用上述解决方案,我无法在django 1.6.5中实现这一点。因此,我尝试创建表单,并让get_form根据对象是否存在为这些预定义表单提供服务: models.py:
from django.db import models
class Project(models.Model):
name = models.CharField('Project Name', max_length=255)
slug = models.SlugField('Project Slug', max_length=255, unique=True)
forms.py:
来自django导入表单
从模型导入项目
class ProjectAddForm(forms.ModelForm):
test = forms.Field()
class Meta:
model = Project
class ProjectEditForm(forms.ModelForm):
class Meta:
model = Project
fields = ("name", 'slug')
管理员
from django.contrib import admin
from models import Project
from forms import ProjectAddForm, ProjectEditForm
class ProjectAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
# Proper kwargs are form, fields, exclude, formfield_callback
if obj:
self.form = ProjectEditForm
else:
self.form = ProjectAddForm
return super(ProjectAdmin, self).get_form(request, obj, **kwargs)
admin.site.register(Project, ProjectAdmin)
现在我可以截取forms clean中的非持久性测试字段,并按照我的意愿使用它,只需在ProjectAddForm中覆盖clean:
def clean(self):
cleaned_data = super(ProjectAddForm, self).clean()
test = cleaned_data.get("test")
# Do logic here
#raise forms.ValidationError("Passwords don't match.")
return cleaned_data
这是一个老问题,但我想补充的是,可以为此修改add_view和change_view方法:
class SoftwareVersionAdmin(ModelAdmin):
...
def add_view(self,request,extra_content=None):
self.exclude = ('product','version_number',)
return super(SoftwareVersionAdmin,self).add_view(request)
def change_view(self,request,object_id,extra_content=None):
self.exclude = ('product','description',)
return super(SoftwareVersionAdmin,self).change_view(request,object_id)
我认为覆盖
字段
或排除
或表单
不是一个好主意,因为它们是配置属性,所以不会对每个请求进行初始化。我认为shanyu接受的答案是一个很好的解决方案 或者我们可以使用UserAdmin的方法:
def get_fieldsets(self, request, obj=None):
if not obj:
return self.add_fieldsets
return super(UserAdmin, self).get_fieldsets(request, obj)
记得自己分配add_字段集
。不幸的是,它不适合我的用例
对于Django 1.7。我不知道它们在其他版本中是如何实现的。我认为上面的dpawlows解决方案是最清晰的
然而,我在这种结构中遇到了另一个问题
如果change\u view()
对模型进行更改,例如指定在add\u view()
中填写的readonly\u字段,则调用change\u view()
后,这些更改将在add\u view()
中保留。例如:
class SoftwareVersionAdmin(ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
# Proper kwargs are form, fields, exclude, formfield_callback
if obj: # obj is not None, so this is a change page
kwargs['exclude'] = ['foo', 'bar',]
else: # obj is None, so this is an add page
kwargs['fields'] = ['foo',]
return super(SoftwareVersionAdmin, self).get_form(request, obj, **kwargs)
def add_view(self, request, extra_context=None):
return super().add_view(request)
def change_view(self, request, object_id, extra_context=None):
self.readonly_fields = ['name'] # this change persists in add_view()
return super().change_view(self, request, object_id)
在这种情况下,在任何实例上调用了change\u view()
之后,调用add\u view()
将显示由change\u view()
设置的readonly\u字段
(“名称”),从而保护这些字段不被填充
这可以通过在add\u view()
中添加“回滚”分配来解决:
Django 1.10就是这样做的。当对象为None时,只需覆盖get\u form
并返回add\u form
:
class FoobarAddForm(forms.ModelForm):
class Meta:
model = Foobar
fields = ['some_field',]
@register(Foobar)
class AdminFoobar(admin.ModelAdmin):
add_form = FoobarAddForm
def get_form(self, request, obj=None, **kwargs):
defaults = {}
if obj is None:
defaults['form'] = self.add_form
defaults.update(kwargs)
return super(AdminFoobar, self).get_form(request, obj, **defaults)
一种简单的方法是使用fieldset
进行更改页面,使用add\u fieldset
进行添加页面 我得到了不可预测的结果(它在不同的时间出现和消失)。我认为更改自身属性不是线程安全的——请参见此处的评论:这比当前公认的答案在意图上更好、更清晰。