管理者変更フォーム内のインライン フォームセット全体を必須にしたいと考えています。そのため、現在のシナリオでは、(管理者の) 請求書フォームで保存を押すと、インラインの注文フォームが空白になります。注文が関連付けられていない請求書を作成する人を止めたいです。
誰でもそれを行う簡単な方法を知っていますか?
モデル フィールドの( ) のような通常の検証required=True
は、このインスタンスでは機能しないようです。
管理者変更フォーム内のインライン フォームセット全体を必須にしたいと考えています。そのため、現在のシナリオでは、(管理者の) 請求書フォームで保存を押すと、インラインの注文フォームが空白になります。注文が関連付けられていない請求書を作成する人を止めたいです。
誰でもそれを行う簡単な方法を知っていますか?
モデル フィールドの( ) のような通常の検証required=True
は、このインスタンスでは機能しないようです。
これを行う最善の方法は、少なくとも 1 つの請求書注文が存在することを検証するクリーンなメソッドを使用して、カスタム フォームセットを定義することです。
class InvoiceOrderInlineFormset(forms.models.BaseInlineFormSet):
def clean(self):
# get forms that actually have valid data
count = 0
for form in self.forms:
try:
if form.cleaned_data:
count += 1
except AttributeError:
# annoyingly, if a subform is invalid Django explicity raises
# an AttributeError for cleaned_data
pass
if count < 1:
raise forms.ValidationError('You must have at least one order')
class InvoiceOrderInline(admin.StackedInline):
formset = InvoiceOrderInlineFormset
class InvoiceAdmin(admin.ModelAdmin):
inlines = [InvoiceOrderInline]
ダニエルの答えは素晴らしく、あるプロジェクトではうまくいきましたが、Django フォームの仕組みにより、can_delete を使用して保存中に削除ボックスをチェックすると、注文なしで検証できることに気付きました (この中で場合)。
私はそれが起こらないようにする方法を見つけようとしてしばらく時間を費やしました. 最初の状況は簡単でした。削除されるフォームをカウントに含めないでください。2 番目の状況はよりトリッキーでした...すべての削除ボックスがチェックされている場合clean
、呼び出されませんでした。
残念ながら、コードは単純ではありません。clean
メソッドは、プロパティがアクセスされたときに呼び出されるから呼び出されますfull_clean
。error
このプロパティは、サブフォームが削除されているときはアクセスされないため、full_clean
呼び出されることはありません。私は Django の専門家ではないので、これはひどい方法かもしれませんが、うまくいくようです。
変更されたクラスは次のとおりです。
class InvoiceOrderInlineFormset(forms.models.BaseInlineFormSet):
def is_valid(self):
return super(InvoiceOrderInlineFormset, self).is_valid() and \
not any([bool(e) for e in self.errors])
def clean(self):
# get forms that actually have valid data
count = 0
for form in self.forms:
try:
if form.cleaned_data and not form.cleaned_data.get('DELETE', False):
count += 1
except AttributeError:
# annoyingly, if a subform is invalid Django explicity raises
# an AttributeError for cleaned_data
pass
if count < 1:
raise forms.ValidationError('You must have at least one order')
class MandatoryInlineFormSet(BaseInlineFormSet):
def is_valid(self):
return super(MandatoryInlineFormSet, self).is_valid() and \
not any([bool(e) for e in self.errors])
def clean(self):
# get forms that actually have valid data
count = 0
for form in self.forms:
try:
if form.cleaned_data and not form.cleaned_data.get('DELETE', False):
count += 1
except AttributeError:
# annoyingly, if a subform is invalid Django explicity raises
# an AttributeError for cleaned_data
pass
if count < 1:
raise forms.ValidationError('You must have at least one of these.')
class MandatoryTabularInline(admin.TabularInline):
formset = MandatoryInlineFormSet
class MandatoryStackedInline(admin.StackedInline):
formset = MandatoryInlineFormSet
class CommentInlineFormSet( MandatoryInlineFormSet ):
def clean_rating(self,form):
"""
rating must be 0..5 by .5 increments
"""
rating = float( form.cleaned_data['rating'] )
if rating < 0 or rating > 5:
raise ValidationError("rating must be between 0-5")
if ( rating / 0.5 ) != int( rating / 0.5 ):
raise ValidationError("rating must have .0 or .5 decimal")
def clean( self ):
super(CommentInlineFormSet, self).clean()
for form in self.forms:
self.clean_rating(form)
class CommentInline( MandatoryTabularInline ):
formset = CommentInlineFormSet
model = Comment
extra = 1
@Daniel Rosemanのソリューションは問題ありませんが、これと同じことを行うために、コードを少し減らしていくつかの変更を加えています。
class RequiredFormSet(forms.models.BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super(RequiredFormSet, self).__init__(*args, **kwargs)
self.forms[0].empty_permitted = False
class InvoiceOrderInline(admin.StackedInline):
model = InvoiceOrder
formset = RequiredFormSet
class InvoiceAdmin(admin.ModelAdmin):
inlines = [InvoiceOrderInline]
これも機能します:)