Разные поля для добавления и изменения страниц в admin

У меня есть приложение django со следующим классом в моей admin.py:

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',]

Я хочу иметь эти файлы для добавления страницы, но разные поля для страницы изменений. Как я могу это сделать?

Ответы

Ответ 1

Сначала рассмотрим источник методов ModelAdmin class 'get_form и get_formsets, расположенных в 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)

Ответ 2

Это старый вопрос, но я хотел добавить, что методы 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)

Ответ 3

Вот как это делается в Django 1.10. Просто переопределите get_form и верните add_form, когда объект None:

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

Я не мог получить эту работу в 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:

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

Ответ 5

Этот код не работал у меня. Я просто немного меняю его:

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)

Ответ 6

Я не думаю, что рекомендуется переопределять fields или exclude или form, потому что они являются атрибутами конфигурации, поэтому они не будут инициализироваться для каждого запроса.
Я думаю, что принятый ответ 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. Я не знаю, как они реализованы в других версиях.

Ответ 7

Полагаю, что решение

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 ( "name", в этом случае), установленный change_view() и, таким образом, защищать эти поля от заполнения.

Это можно решить, добавив в add_view() назначение 'roll back':

def add_view(self, request, extra_context=None):
    self.readonly_fields = []  # 'roll back' for changes made by change_view()
    return super().add_view(request)

Ответ 8

Использование форм в Django 1.6 В итоге я получил следующее:

def get_formsets(self, request, obj=None):
    if obj is None:
        # It 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)

EDIT: Возможно, более интуитивным способом является указание отдельного свойства add_fieldsets и выполните:

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

    return super().get_formsets(request, obj)