У меня есть приложение django со следующим классом в моем admin.py:
Я хочу, чтобы эти поля были для страницы добавления, но другие поля для страницы изменения. Как я могу это сделать?
Сначала взглянем на источник методов класса get_form
и get_formsets
класса ModelAdmin, расположенных в 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)
Я не мог заставить это работать в 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 из импорта моделей Project
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)
Теперь я могу перехватить непостоянное тестовое поле в чистых формах и делать с ним все, что захочу, просто перезаписав clean в 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
Этот конкретный код не работал для меня. Я просто немного меняю это:
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)
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()
:
def add_view(self, request, extra_context=None):
self.readonly_fields = [] # 'roll back' for changes made by change_view()
return super().add_view(request)