在保存之前修改内联表单集
Modifying a inline formset before it is saved
tldr;我需要在验证和保存表单之前添加所需的 'author' 字段。
有两个型号:Documents
和 Revisions
。每个 Document
可以有很多 Revisions
和每个修订作为作者 (User
object)。当然我不希望用户自己设置作者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)
views.py
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())
我建议这样的方法:
forms.py
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)
views.py
from .forms import CommentFormset
def post_create(request, *args, **kwargs):
...
context['formset'] = CommentFormset(instance=post, user=request.user)
...
如果您想避免用 POST 数据覆盖 'user' 字段,请从表单集中排除 'user' 字段:
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
还有其他方法可以实现您的目标(例如在保存表单集后遍历视图函数中的每个新实例或覆盖表单集表单中的 "clean" 方法等等...)但它们更多在我看来很复杂。
tldr;我需要在验证和保存表单之前添加所需的 'author' 字段。
有两个型号:Documents
和 Revisions
。每个 Document
可以有很多 Revisions
和每个修订作为作者 (User
object)。当然我不希望用户自己设置作者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)
views.py
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())
我建议这样的方法:
forms.py
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)
views.py
from .forms import CommentFormset
def post_create(request, *args, **kwargs):
...
context['formset'] = CommentFormset(instance=post, user=request.user)
...
如果您想避免用 POST 数据覆盖 'user' 字段,请从表单集中排除 'user' 字段:
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
还有其他方法可以实现您的目标(例如在保存表单集后遍历视图函数中的每个新实例或覆盖表单集表单中的 "clean" 方法等等...)但它们更多在我看来很复杂。