Django 在保存内联表单集之前修改它

Django 在保存内联表单集之前修改它,django,django-forms,Django,Django Forms,tldr;在验证和保存表单之前,我需要添加所需的“作者”字段 有两种模型:文档和修订版。每个文档可以有许多修订版,每个修订版作为作者(用户对象)。当然,我不希望用户能够自己设置作者ID,所以我需要自己设置 在编辑页面上,用户可以修改文档标题并创建新修订。有两种形式:DocumentForm和RevisionInlineFormset 在验证和保存所有内容之前,我似乎无法在post方法中将作者分配给RevisionInlineFormset forms.py class DocumentForm(

tldr;在验证和保存表单之前,我需要添加所需的“作者”字段

有两种模型:
文档
修订版
。每个
文档
可以有许多
修订版
,每个修订版作为作者(
用户
对象)。当然,我不希望用户能够自己设置作者ID,所以我需要自己设置

在编辑页面上,用户可以修改文档标题并创建新修订。有两种形式:
DocumentForm
RevisionInlineFormset

在验证和保存所有内容之前,我似乎无法在
post
方法中将作者分配给
RevisionInlineFormset

forms.py

class DocumentForm(forms.ModelForm):
    class Meta:
        fields = ['title', 'path']
        model = Document

RevisionInlineFormset = inlineformset_factory(
    Document,
    Revision,
    extra=1, max_num=1,
    fields=('content',),
    can_delete=False)
class DocUpdate(mixins.LoginRequiredMixin, generic.edit.UpdateView):

    """ Edit a document. """

    form_class = DocumentForm
    template_name = 'spaces/document/edit.html'

    def get_object(self):
        try:
            return Document.objects.get_by_path(self.kwargs["path"])
        except ObjectDoesNotExist:
            raise Http404

    def get(self, request, *args, **kwargs):
        """ Handles GET requests. """
        self.object = self.get_object()

        # Get latest revision
        rev_qs = self.object.revision_set.order_by('-created_on')
        if rev_qs.count():
            rev_qs = rev_qs.filter(pk=rev_qs[0].pk)

        form_class = self.get_form_class()
        form = self.get_form(form_class)
        revision_form = RevisionInlineFormset(
            instance=self.object,
            queryset=rev_qs)

        return self.render_to_response(
            self.get_context_data(form=form,
                                  revision_form=revision_form))

    def post(self, request, *args, **kwargs):
        """ Handles POST requests. """
        self.object = self.get_object()

        form_class = self.get_form_class()
        form = self.get_form(form_class)
        revision_form = RevisionInlineFormset(
            self.request.POST, instance=self.object)

        if (form.is_valid() and revision_form.is_valid()):
            return self.form_valid(form, revision_form)
        return self.form_invalid(form, revision_form)

    def form_valid(self, form, revision_form):
        """ All good. Finish up and save. """
        self.object = form.save()

        revision_form.instance = self.object
        revision_form.save()

        return HttpResponseRedirect(self.get_success_url())
from .models import Comment, Post
from django.forms.models import inlineformset_factory, ModelForm, BaseInlineFormSet


class PostCommentBaseFormset(BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user', None)
        super(PostCommentBaseFormset, self).__init__(*args, **kwargs)
        if user:
            for form in self.forms:
                if form.instance.pk is None:
                    form.fields['user'].initial = user #or form.initial['user'] = user

CommentFormset = inlineformset_factory(Post, Comment, extra=3, exclude=[], formset=PostCommentBaseFormset)
from .forms import CommentFormset

def post_create(request, *args, **kwargs):
    ...
    context['formset'] = CommentFormset(instance=post, user=request.user)
    ...
视图.py

class DocumentForm(forms.ModelForm):
    class Meta:
        fields = ['title', 'path']
        model = Document

RevisionInlineFormset = inlineformset_factory(
    Document,
    Revision,
    extra=1, max_num=1,
    fields=('content',),
    can_delete=False)
class DocUpdate(mixins.LoginRequiredMixin, generic.edit.UpdateView):

    """ Edit a document. """

    form_class = DocumentForm
    template_name = 'spaces/document/edit.html'

    def get_object(self):
        try:
            return Document.objects.get_by_path(self.kwargs["path"])
        except ObjectDoesNotExist:
            raise Http404

    def get(self, request, *args, **kwargs):
        """ Handles GET requests. """
        self.object = self.get_object()

        # Get latest revision
        rev_qs = self.object.revision_set.order_by('-created_on')
        if rev_qs.count():
            rev_qs = rev_qs.filter(pk=rev_qs[0].pk)

        form_class = self.get_form_class()
        form = self.get_form(form_class)
        revision_form = RevisionInlineFormset(
            instance=self.object,
            queryset=rev_qs)

        return self.render_to_response(
            self.get_context_data(form=form,
                                  revision_form=revision_form))

    def post(self, request, *args, **kwargs):
        """ Handles POST requests. """
        self.object = self.get_object()

        form_class = self.get_form_class()
        form = self.get_form(form_class)
        revision_form = RevisionInlineFormset(
            self.request.POST, instance=self.object)

        if (form.is_valid() and revision_form.is_valid()):
            return self.form_valid(form, revision_form)
        return self.form_invalid(form, revision_form)

    def form_valid(self, form, revision_form):
        """ All good. Finish up and save. """
        self.object = form.save()

        revision_form.instance = self.object
        revision_form.save()

        return HttpResponseRedirect(self.get_success_url())
from .models import Comment, Post
from django.forms.models import inlineformset_factory, ModelForm, BaseInlineFormSet


class PostCommentBaseFormset(BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user', None)
        super(PostCommentBaseFormset, self).__init__(*args, **kwargs)
        if user:
            for form in self.forms:
                if form.instance.pk is None:
                    form.fields['user'].initial = user #or form.initial['user'] = user

CommentFormset = inlineformset_factory(Post, Comment, extra=3, exclude=[], formset=PostCommentBaseFormset)
from .forms import CommentFormset

def post_create(request, *args, **kwargs):
    ...
    context['formset'] = CommentFormset(instance=post, user=request.user)
    ...

我建议采取如下方法:

class PostCommentBaseFormset(BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user', None)
        super(PostCommentBaseFormset, self).__init__(*args, **kwargs)
        for form in self.forms:
            if form.instance.pk is None:
                form.instance.user = user 
forms.py

class DocumentForm(forms.ModelForm):
    class Meta:
        fields = ['title', 'path']
        model = Document

RevisionInlineFormset = inlineformset_factory(
    Document,
    Revision,
    extra=1, max_num=1,
    fields=('content',),
    can_delete=False)
class DocUpdate(mixins.LoginRequiredMixin, generic.edit.UpdateView):

    """ Edit a document. """

    form_class = DocumentForm
    template_name = 'spaces/document/edit.html'

    def get_object(self):
        try:
            return Document.objects.get_by_path(self.kwargs["path"])
        except ObjectDoesNotExist:
            raise Http404

    def get(self, request, *args, **kwargs):
        """ Handles GET requests. """
        self.object = self.get_object()

        # Get latest revision
        rev_qs = self.object.revision_set.order_by('-created_on')
        if rev_qs.count():
            rev_qs = rev_qs.filter(pk=rev_qs[0].pk)

        form_class = self.get_form_class()
        form = self.get_form(form_class)
        revision_form = RevisionInlineFormset(
            instance=self.object,
            queryset=rev_qs)

        return self.render_to_response(
            self.get_context_data(form=form,
                                  revision_form=revision_form))

    def post(self, request, *args, **kwargs):
        """ Handles POST requests. """
        self.object = self.get_object()

        form_class = self.get_form_class()
        form = self.get_form(form_class)
        revision_form = RevisionInlineFormset(
            self.request.POST, instance=self.object)

        if (form.is_valid() and revision_form.is_valid()):
            return self.form_valid(form, revision_form)
        return self.form_invalid(form, revision_form)

    def form_valid(self, form, revision_form):
        """ All good. Finish up and save. """
        self.object = form.save()

        revision_form.instance = self.object
        revision_form.save()

        return HttpResponseRedirect(self.get_success_url())
from .models import Comment, Post
from django.forms.models import inlineformset_factory, ModelForm, BaseInlineFormSet


class PostCommentBaseFormset(BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user', None)
        super(PostCommentBaseFormset, self).__init__(*args, **kwargs)
        if user:
            for form in self.forms:
                if form.instance.pk is None:
                    form.fields['user'].initial = user #or form.initial['user'] = user

CommentFormset = inlineformset_factory(Post, Comment, extra=3, exclude=[], formset=PostCommentBaseFormset)
from .forms import CommentFormset

def post_create(request, *args, **kwargs):
    ...
    context['formset'] = CommentFormset(instance=post, user=request.user)
    ...
视图.py

class DocumentForm(forms.ModelForm):
    class Meta:
        fields = ['title', 'path']
        model = Document

RevisionInlineFormset = inlineformset_factory(
    Document,
    Revision,
    extra=1, max_num=1,
    fields=('content',),
    can_delete=False)
class DocUpdate(mixins.LoginRequiredMixin, generic.edit.UpdateView):

    """ Edit a document. """

    form_class = DocumentForm
    template_name = 'spaces/document/edit.html'

    def get_object(self):
        try:
            return Document.objects.get_by_path(self.kwargs["path"])
        except ObjectDoesNotExist:
            raise Http404

    def get(self, request, *args, **kwargs):
        """ Handles GET requests. """
        self.object = self.get_object()

        # Get latest revision
        rev_qs = self.object.revision_set.order_by('-created_on')
        if rev_qs.count():
            rev_qs = rev_qs.filter(pk=rev_qs[0].pk)

        form_class = self.get_form_class()
        form = self.get_form(form_class)
        revision_form = RevisionInlineFormset(
            instance=self.object,
            queryset=rev_qs)

        return self.render_to_response(
            self.get_context_data(form=form,
                                  revision_form=revision_form))

    def post(self, request, *args, **kwargs):
        """ Handles POST requests. """
        self.object = self.get_object()

        form_class = self.get_form_class()
        form = self.get_form(form_class)
        revision_form = RevisionInlineFormset(
            self.request.POST, instance=self.object)

        if (form.is_valid() and revision_form.is_valid()):
            return self.form_valid(form, revision_form)
        return self.form_invalid(form, revision_form)

    def form_valid(self, form, revision_form):
        """ All good. Finish up and save. """
        self.object = form.save()

        revision_form.instance = self.object
        revision_form.save()

        return HttpResponseRedirect(self.get_success_url())
from .models import Comment, Post
from django.forms.models import inlineformset_factory, ModelForm, BaseInlineFormSet


class PostCommentBaseFormset(BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user', None)
        super(PostCommentBaseFormset, self).__init__(*args, **kwargs)
        if user:
            for form in self.forms:
                if form.instance.pk is None:
                    form.fields['user'].initial = user #or form.initial['user'] = user

CommentFormset = inlineformset_factory(Post, Comment, extra=3, exclude=[], formset=PostCommentBaseFormset)
from .forms import CommentFormset

def post_create(request, *args, **kwargs):
    ...
    context['formset'] = CommentFormset(instance=post, user=request.user)
    ...
如果要避免用POST数据覆盖“用户”字段,请从表单集中排除“用户”字段:

CommentFormset = inlineformset_factory(Post, Comment, extra=3, exclude=['user'], formset=PostCommentBaseFormset)
并按如下方式更改PostCommentBaseFormset:

class PostCommentBaseFormset(BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user', None)
        super(PostCommentBaseFormset, self).__init__(*args, **kwargs)
        for form in self.forms:
            if form.instance.pk is None:
                form.instance.user = user 

还有其他方法可以实现您的目标(比如在保存formset后在view函数中迭代每个新实例,或者覆盖formset表单中的“clean”方法等等…)但在我看来,它们更复杂。

如果用户通过POST发送用户值,设置初始值不会阻止用户覆盖它,对吗?对。如果您需要避免覆盖,我将在明天到达我的PC时更新答案。这很有效。谢谢(仅供参考,您的
super()
行缺少
PostCommentBaseFormset,self
参数)谢谢。这是因为我经常在我的项目和超级作品中使用python3,对于这种基本用法,python3中没有参数。