Я последовал совету Карла и реализовал его гораздо лучше, чем хакерский, который я упомянул в своем комментарии к его ответу. Вот мое решение:
Из моих forms.py:[1252 providedAnd 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)
Это делает именно то, что я хочу, то есть проверяет встроенный набор форм Profile. Таким образом, поскольку в форме профиля есть обязательные поля, она будет проверена и завершится ошибкой, если требуемая информация не будет введена во встроенную форму.
Возможно, вы сможете это сделать, но вам придется запачкать руки в коде набора форм / встроенном коде.
Прежде всего, я думаю, вы хотите, чтобы в этом случае всегда была одна форма в наборе форм, а не более одной, поэтому вам нужно установить 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
Поиграйте и посмотрите, что вы можете заставить работать!
Заметив, что _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
Поиграйте и посмотрите, что вы можете заставить работать!
Заметив, что _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
Поиграйте и посмотрите, что вы можете заставить работать!
Самый простой и естественный способ сделать это - использовать 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
(На основе этого Мэтью Фрагмент Flanagan и комментарий Митара ниже, протестированы для работы в Django 1.11 и 2.0).