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)
문제는 사용자를 추가 할 때 프로필 인라인 형식의 두 필드가 필요하다는 것입니다. 입력이 입력되지 않으면 인라인 양식이 유효성이 없습니다. 어쨌든 인라인을 필요로하여 비워 둘 수 없습니까?
해결책
나는 Carl의 조언을 받아 그의 답변에서 언급 한 Hack-ish를 훨씬 더 나은 구현을 만들었습니다. 내 해결책은 다음과 같습니다.
내 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
그리고 관리자
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)
이것은 내가 원하는 것을 정확하게 수행하므로 프로파일 인라인 FormSet이 검증됩니다. 따라서 프로파일 양식에 필요한 필드가 있으므로 필요한 정보가 인라인 형식으로 입력되지 않으면 유효성을 검사하고 실패합니다.
다른 팁
이제 django 1.7을 사용하면 매개 변수를 사용할 수 있습니다 min_num
. 수업이 필요하지 않습니다 RequiredInlineFormSet
더 이상.
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)
아마도 이것을 할 수 있지만 Formset/Inline 코드에서 손을 더럽 힐 것입니다.
우선, 나는 당신 이이 경우 FormSet에 항상 하나의 형태가 있고 둘 이상이지 않기를 원한다고 생각하므로 설정하고 싶을 것입니다. max_num= 1과 추가의profileInline에서 = 1.
당신의 핵심 문제는 그 것입니다 baseformset._construct_form은 empty_permitted = true를 통과합니다 FormSet의 각 "extra"(즉, 빈) 형태로. 이 매개 변수는 양식이 변경되지 않은 경우 검증을 우회하도록 지시합니다. 양식에 대해 empty_permitted = false를 설정하는 방법을 찾아야합니다.
당신은 할 수 있습니다 자신의 BaseInlineformset 서브 클래스를 사용하십시오 인라인으로 도움이 될 수 있습니다. _construct_form이 ** kwargs를 사용하고 전달 된 Kwargs를 개별 양식 인스턴스로 무시할 수 있음을 알게되면 Formset 서브 클래스에서 _construct_forms를 무시하고 _construct_form으로 모든 호출에서 emply_permitted = false를 통과 할 수 있습니다. 단점은 내부 API에 의존하고 있다는 것입니다 (_construct_forms를 다시 작성해야합니다).
또는 ProfileInline에서 get_formset 메소드를 재정의하고 부모의 get_formset을 호출 한 후 반환 된 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
(에서 영감을 받다 이 Matthew Flanagan의 스 니펫 아래의 Mitar의 의견은 Django 1.11 및 2.0에서 작업하도록 테스트되었습니다.
설정해야합니다 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