Подтвердить что ты не робот

Как мне потребовать встроенный администратор Django?

У меня есть следующая настройка администратора, так что я могу добавлять/редактировать пользователя и их профиль одновременно.

class ProfileInline(admin.StackedInline):
    """
    Allows profile to be added when creating user
    """
    model = Profile


class UserProfileAdmin(admin.ModelAdmin):
    """
    Options for the admin interface
    """
    inlines = [ProfileInline]
    list_display = ['edit_obj', 'name', 'username', 'email', 'is_active',
        'last_login', 'delete_obj']
    list_display_links = ['username']
    list_filter = ['is_active']
    fieldsets = (
        (None, {
            'fields': ('first_name', 'last_name', 'email', 'username',
                'is_active', 'is_superuser')}),
        )
    ordering = ['last_name', 'first_name']
    search_fields = ['first_name', 'last_name']

admin.site.register(User, UserProfileAdmin)

Проблема в том, что при добавлении пользователя мне нужно два поля в встроенной форме профиля. Встроенная форма не проверяется, если вход не введен. В любом случае, чтобы сделать встроенную строку, чтобы она не оставалась пустой?

4b9b3361

Ответ 1

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

Из моих forms.py:

from django.forms.models import BaseInlineFormSet


class RequiredInlineFormSet(BaseInlineFormSet):
    """
    Generates an inline formset that is required
    """

    def _construct_form(self, i, **kwargs):
        """
        Override the method to change the form attribute empty_permitted
        """
        form = super(RequiredInlineFormSet, self)._construct_form(i, **kwargs)
        form.empty_permitted = False
        return form

И admin.py

class ProfileInline(admin.StackedInline):
    """
    Allows profile to be added when creating user
    """
    model = Profile
    extra = 1
    max_num = 1
    formset = RequiredInlineFormSet


class UserProfileAdmin(admin.ModelAdmin):
    """
    Options for the admin interface
    """
    inlines = [ProfileInline]
    list_display = ['edit_obj', 'name', 'username', 'email', 'is_active',
        'last_login', 'delete_obj']
    list_display_links = ['username']
    list_filter = ['is_active']
    fieldsets = (
        (None, {
            'fields': ('first_name', 'last_name', 'email', 'username',
                'is_active', 'is_superuser')}),
        (('Groups'), {'fields': ('groups', )}),
    )
    ordering = ['last_name', 'first_name']
    search_fields = ['first_name', 'last_name']


admin.site.register(User, UserProfileAdmin)

Это делает именно то, что я хочу, это делает проверку встроенного formet профиля. Так как в форме профиля есть обязательные поля, он будет проверять и терпеть неудачу, если требуемая информация не будет введена во встроенную форму.

Ответ 2

Теперь с Django 1.7 вы можете использовать параметр min_num. Вам больше не нужен класс RequiredInlineFormSet.

См. https://docs.djangoproject.com/en/1.8/ref/contrib/admin/#django.contrib.admin.InlineModelAdmin.min_num

class ProfileInline(admin.StackedInline):
    """
    Allows profile to be added when creating user
    """
    model = Profile
    extra = 1
    max_num = 1
    min_num = 1 # new in Django 1.7


class UserProfileAdmin(admin.ModelAdmin):
    """
    Options for the admin interface
    """
    inlines = [ProfileInline]
    ...


admin.site.register(User, UserProfileAdmin)

Ответ 3

Возможно, вы можете это сделать, но вам придется замарать руки в код формы/встроенного кода.

Прежде всего, я думаю, вы хотите, чтобы в этом случае всегда была одна форма в наборе форм и не более одного, поэтому вам нужно установить max_num= 1 и extra= 1 в вашей профильной строке.

Основная проблема заключается в том, что BaseFormSet._construct_form передает empty_permitted = True в каждую "лишнюю" (т.е. пустую) форму в наборе форм. Этот параметр сообщает форме обходить проверку, если она не изменилась. Вам просто нужно найти способ установить для формы empty_permitted = False.

Вы можете использовать свой собственный подкласс BaseInlineFormset в своем встроенном режиме, чтобы это могло помочь. Заметив, что _construct_form принимает ** kwargs и позволяет переопределить kwargs, переданные отдельным экземплярам Form, вы можете переопределить _construct_forms в вашем подклассе Formset и передать ему значение empty_permitted = False в каждом вызове _construct_form. Недостатком является то, что вы полагаетесь на внутренние API (и вам придется переписать _construct_forms).

В качестве альтернативы вы можете попробовать переопределить метод get_formset в вашей ProfileInline и после вызова родительского get_formset вручную выставить форму в возвращаемом наборе форм:

def get_formset(self, request, obj=None, **kwargs):
    formset = super(ProfileInline, self).get_formset(request, obj, **kwargs)
    formset.forms[0].empty_permitted = False
    return formset

Поиграйте и посмотрите, что вы можете сделать!

Ответ 4

Самый простой и естественный способ сделать это через fomset clean():

class RequireOneFormSet(forms.models.BaseInlineFormSet):
    def clean(self):
        super().clean()
        if not self.is_valid():
            return
        if not self.forms or not self.forms[0].cleaned_data:
            raise ValidationError('At least one {} required'
                                  .format(self.model._meta.verbose_name))

class ProfileInline(admin.StackedInline):
    model = Profile
    formset =  RequireOneFormSet

(Вдохновленный этот фрагмент Matthew Flanagan и комментарий Mitar ниже, протестированный для работы в Django 1.11 и 2.0).

Ответ 5

Вам нужно установить min_num во встроенном и validate_min в formset.

https://docs.djangoproject.com/en/1.8/topics/forms/formsets/#validate-min

class SomeInline(admin.TabularInline):
    ...
    min_num = 1

    def get_formset(self, request, obj=None, **kwargs):
        formset = super().get_formset(request, obj=None, **kwargs)
        formset.validate_min = True
        return formset