用于在admin中添加和更改页面的不同字段

时间:2011-06-12 12:24:52

标签: 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",
      "current_version", "number_of_clients", "percent_of_clients"]
    list_display_links = ("version_number",)
    list_filter = ['product',]

我希望这些文件用于添加页面,但不同的字段用于更改页面。我怎么能这样做?

10 个答案:

答案 0 :(得分:27)

首先查看位于get_form的ModelAdmin类“get_formsetsdjango.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)

答案 1 :(得分:24)

这是一个老问题,但我想补充一点,为此可以修改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)

答案 2 :(得分:4)

使用上述解决方案,我无法在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')

admin.py

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)

现在我可以截取表单中的非持久性测试字段,并按照我的意愿执行,只需在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

答案 3 :(得分:4)

这是在Django 1.10中完成的。只需覆盖get_form并在对象为无时返回add_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)

答案 4 :(得分:2)

此特定代码对我不起作用。 我只是稍微改变一下:

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)

答案 5 :(得分:2)

我认为覆盖fieldsexcludeform是个好主意,因为它们是配置属性,因此它们不会为每个属性进行初始化请求。
我认为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_fieldsets。不幸的是,它不适合我的用例。

对于Django 1.7。我不知道它们是如何在其他版本中实现的。

答案 6 :(得分:1)

dpawlows 上面的解决方案是最清晰的,我认为。

但是,我在这种结构中遇到了另一个问题。

如果change_view()对模型进行了更改,例如指定readonly_fields中已填写的add_view()add_view()调用change_view()后,这些更改会在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_view()

在这种情况下,在任何实例上调用add_view()后,调用readonly_fields将显示由change_view()设置的add_view()(在本例中为“name”)从而保护这些领域免于填补。

这可以通过在def add_view(self, request, extra_context=None): self.readonly_fields = [] # 'roll back' for changes made by change_view() return super().add_view(request) 中添加“回滚”作业来解决:

.slide-down-banner-menu-panel

答案 7 :(得分:0)

在Django 1.6中使用formset我得到了以下内容:

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_fieldsets属性并执行:

def get_formsets(self, request, obj=None):
    if obj is None:
        self.fieldsets = self.add_fieldsets

    return super().get_formsets(request, obj)

答案 8 :(得分:0)

一种简单的方法是将fieldsets用于更改页面,将add_fieldsets用于添加页面。

答案 9 :(得分:0)

使用更现代的 Django 版本(在撰写本文时为 3.2),您可以覆盖 BaseModelAdmin 中的一些方法以实现在“更改”和“添加”模型管理页面上具有不同的字段:

class BaseModelAdmin(metaclass=forms.MediaDefiningClass):
    """Functionality common to both ModelAdmin and InlineAdmin."""
    
    # ...

    def get_exclude(self, request, obj=None):
        """
        Hook for specifying exclude.
        """
        return self.exclude

    def get_fields(self, request, obj=None):
        """
        Hook for specifying fields.
        """
        if self.fields:
            return self.fields
        # _get_form_for_get_fields() is implemented in subclasses.
        form = self._get_form_for_get_fields(request, obj)
        return [*form.base_fields, *self.get_readonly_fields(request, obj)]

    def get_fieldsets(self, request, obj=None):
        """
        Hook for specifying fieldsets.
        """
        if self.fieldsets:
            return self.fieldsets
        return [(None, {'fields': self.get_fields(request, obj)})]

    def get_readonly_fields(self, request, obj=None):
        """
        Hook for specifying custom readonly fields.
        """
        return self.readonly_fields

例如,在更改页面上添加一些只读字段(obj 存在),但不在添加页面上:

class MyModelAdmin(admin.ModelAdmin):
    
    # Readonly_fields only on change page
    def get_readonly_fields(self, request, obj=None):
        if obj:  # obj is not None, so this is a change page
            return 'field_1', 'field_2'
        return ()  # obj is None, so this is the add page