Как мне потребовать встроенный в Django Admin? - PullRequest
25 голосов
/ 30 июля 2009

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

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)

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

Ответы [ 5 ]

30 голосов
/ 05 августа 2009

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

Из моих форм.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)

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

21 голосов
/ 17 мая 2015

Теперь с 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)
9 голосов
/ 31 июля 2009

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

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

Ваша основная проблема заключается в том, что 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

Поиграй и посмотри, на что ты способен!

7 голосов
/ 04 февраля 2010

Самый простой и естественный способ сделать это - через 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

(Вдохновленный этим фрагментом Мэтью Фланагана и комментарием Митара ниже, проверенным для работы в Django 1.11 и 2.0).

0 голосов
/ 20 декабря 2018

Вам необходимо установить min_num во встроенном режиме и validate_min в форме.

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
...