различные поля для добавления и изменения страниц в админке - PullRequest
21 голосов
/ 12 июня 2011

У меня есть приложение 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',]

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

Ответы [ 8 ]

27 голосов
/ 12 июня 2011

Сначала взглянем на источник методов классаAdAdmin '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)
25 голосов
/ 03 августа 2015

Это старый вопрос, но я хотел добавить, что для этого можно изменить методы 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)
4 голосов
/ 21 ноября 2016

Вот как это делается в 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 голосов
/ 29 июля 2014

Я не мог заставить это работать в 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 и делать с ним все, что захочу, просто перезаписать 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
2 голосов
/ 24 марта 2016

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

Или мы можем использовать метод из 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.Я не знаю, как они реализованы в других версиях.

2 голосов
/ 12 марта 2012

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

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)
1 голос
/ 31 октября 2016

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)
0 голосов
/ 28 апреля 2014

Используя формы в 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)

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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...