7

ここではかなり基本的な使用シナリオです。オブジェクトを作成したユーザーと最後に変更したユーザーを保存したい。ただし、これはインライン モデルなので、もちろん使用する必要がありますsave_formset。Django ドキュメントには、次のサンプル コードがあります。

class ArticleAdmin(admin.ModelAdmin):
    def save_formset(self, request, form, formset, change):
        instances = formset.save(commit=False)
        for instance in instances:
            instance.user = request.user
            instance.save()
        formset.save_m2m()

気がつくと、superは決して呼び出されないため、これは行き止まりです。がModelAdminサブクラス化され、このメソッドが同じ方法でオーバーライドされると、親に固有の機能が失われます。これは非常に一般的な使用シナリオであるため、機能を除外したいため、次のように作成しました。

class TrackableInlineAdminMixin(admin.ModelAdmin):
    def save_formset(self, request, form, formset, change):
        instances = formset.save(commit=False)
        for instance in instances:
            if hasattr(instance, 'created_by') and hasattr(instance, 'modified_by'):
                if not instance.pk:
                    instance.created_by = request.user
                instance.modified_by = request.user
            instance.save()
        formset.save_m2m()
        super(TrackableInlineAdminMixin, self).save_formset(request, form, formset, change)

super実際にフォームセットが 2 回保存されるとは考えずに、何よりも習慣から の呼び出しに取り組みました。それにもかかわらず、1 つを除くすべてのシナリオで機能します: 削除します。管理画面でインラインを削除しようとすると、すぐにエラーが発生します。エラーはかなりあいまいで、ここでの私の質問にはあまり関係ありませんが、フォームセット内のインスタンスの 1 つを削除した後にフォームセットを再度保存しようとしたことに関連していると思います。への呼び出しsuperが削除されると、コードは正常に機能します。

長くても短くても、フォームセットの保存動作カスタマイズし、サブクラスが独自のオーバーライドを実行できるようにする方法はありますか?

4

2 に答える 2

6

これはドゥージーです。

私はいくつかの楽しい時間を過ごしました.すべてのアクションはここで行われるようdjango.forms.models.BaseModelFormSetです.

問題はModelFormSet.save()、フラグに関係なくインスタンスを削除commitし、削除された状態を反映するようにフォームを変更しないことです。

再度呼び出すsave()と、フォームが反復処理され、ModelChoiceFieldクリーニング中に参照された ID を取得しようとし、無効な選択エラーがスローされます。

def save_existing_objects(self, commit=True):
    self.changed_objects = []
    self.deleted_objects = []
    if not self.initial_forms:
        return []

    saved_instances = []
    for form in self.initial_forms:
        pk_name = self._pk_field.name
        raw_pk_value = form._raw_value(pk_name)

        # clean() for different types of PK fields can sometimes return
        # the model instance, and sometimes the PK. Handle either.
        pk_value = form.fields[pk_name].clean(raw_pk_value) 
        pk_value = getattr(pk_value, 'pk', pk_value)

        obj = self._existing_object(pk_value)
        if self.can_delete and self._should_delete_form(form):
            self.deleted_objects.append(obj)
            obj.delete()  
            # problem here causes `clean` 6 lines up to fail next round

            # patched line here for future save()
            # to not attempt a second delete
            self.forms.remove(form)

これを修正できた唯一の方法は、オブジェクトが削除された場合BaseModelFormset.save_existing_objectsにフォームを削除するようにパッチを当てることです。self.forms

いくつかのテストを行いましたが、悪影響はないようです。

于 2012-04-28T01:26:11.970 に答える
0

@クリス・プラットが助けました:

私は、実際にフォームセットが 2 回保存されるとは考えずに、何よりも習慣から super の呼び出しに取り組みました。

save_formset保存後の信号を送信するために、さらに a をオーバーライドしようとしていました。呼び出しsuper()がフォームセットを 2 回目に保存するだけであることを理解できませんでした。

この問題に対処するために、カスタム コードでメソッドsuper()を作成しました。このメソッドは、子を介してsave_formset_nowオーバーライドするときに呼び出します。save_formsetadmin.ModelAdmin

これは、2016年にDjango 1.10を使用して、削除の問題にも対処しているように見えるコードです。

class BaseMixinAdmin(object):
    def save_formset_now(self, request, form, formset, change):
        instances = formset.save(commit=False)
        for obj in formset.deleted_objects:
            obj.delete()
        for instance in instances:
            # *** Start Coding for Custom Needs ***
                ....
            # *** End Coding for Custom Needs ***
            instance.save()
        formset.save_m2m()

class BaseAdmin(BaseMixinAdmin, admin.ModelAdmin):
    def save_formset(self, request, form, formset, change):
        self.save_formset_now(request, form, formset, change)


class ChildAdmin(BaseAdmin):
    def save_formset(self, request, form, formset, change):
        self.save_formset_now(request, form, formset, change)
        my_signal.send(...)
于 2016-09-17T17:15:08.947 に答える