django - 使用 FormView 和 ModelForm 更新模型

Posted

技术标签:

【中文标题】django - 使用 FormView 和 ModelForm 更新模型【英文标题】:django - update model with FormView and ModelForm 【发布时间】:2014-03-03 14:59:37 【问题描述】:

我不知道如何在FormView 中使用ModelForm 来更新已经存在的实例??

此 URL 上的 POST 表单:r'/object/(?P<pk>)/'

我使用ModelForm(而不是直接使用UpdateView),因为其中一个字段是必需的,我对其进行了清理。

我基本上想在FormView(在POST)中初始化表单时提供kwarg instance=...,以便它绑定到在url中给出pk的对象。但我不知道在哪里做...

class SaveForm(ModelForm):
    somedata = forms.CharField(required=False)
    class Meta:
        model = SomeModel  # with attr somedata
        fields = ('somedata', 'someotherdata')
    def clean_somedata(self):
        return sometransformation(self.cleaned_data['somedata'])

class SaveView(FormView):
    form_class = SaveForm
    def form_valid(self, form):
        # form.instance here would be == SomeModel.objects.get(pk=pk_from_kwargs)
        form.instance.save()
        return ...

【问题讨论】:

我不太明白你为什么不能使用UpdateView。你能发布你的视图代码吗? @jproffitt 清楚为什么我现在不能使用 UpadteView 了吗? 似乎答案在SingleObjectMixin 中(或停止尝试使用django 基于类的通用视图) 不清楚。 UpdateView 继承自 SingleObjectMixin。看来这正是您所需要的。如果您在场地上进行清洁并不重要。您不想用现有数据填充表单吗? 对于可能遇到此问题的其他人:如果您发现必须添加 blank=True, null=True 以使事情得到足够的验证以进行后处理,您最好将模型解耦并表单,只使用表单,然后手动创建模型实例。在许多用例中对我来说效果更好,例如当您需要访问 request.user 时。 【参考方案1】:

您可以使用 FormView 的 post 方法获取发布的数据并使用 form.save() 保存到模型。希望这会有所帮助。

试试这个

    class SaveForm(ModelForm):
    somedata = forms.CharField(required=False)

    class Meta:
        model = SomeModel  # with attr somedata
        fields = ('somedata', 'someotherdata')

    def __init__(self, *args, **kwargs):
        super(SaveForm, self).__init__(*args, **kwargs)

    def save(self, id):
        print id   #this id will be sent from the view
        instance = super(SaveForm, self).save(commit=False)
        instance.save()
        return instance


class SaveView(FormView):
    template_name = 'sometemplate.html'
    form_class = SaveForm

    def post(self, request, *args, **kwargs):

        form = self.form_class(request.POST)

        if form.is_valid():
            form.save(kwargs.get('pk'))
        else:
            return self.form_invalid(form)

【讨论】:

不使用 URL 中的“pk”,这对我没有帮助 检查我已经添加了从视图中获取 pk 到表单中的逻辑。希望这会有所帮助。【参考方案2】:

在与您讨论后,我仍然不明白您为什么不能使用UpdateView。如果我理解正确,这似乎是一个非常简单的用例。您有一个要更新的模型。而且您有一个自定义表单可以在将其保存到该模型之前进行清理。似乎UpdateView 可以正常工作。像这样:

class SaveForm(ModelForm):
    somedata = forms.CharField(required=False)

    class Meta:
        model = SomeModel  # with attr somedata
        fields = ('somedata', 'someotherdata')

    def clean_somedata(self):
        return sometransformation(self.cleaned_data['somedata'])


class SaveView(UpdateView):
    template_name = 'sometemplate.html'
    form_class = SaveForm
    model = SomeModel

    # That should be all you need. If you need to do any more custom stuff 
    # before saving the form, override the `form_valid` method, like this:

    def form_valid(self, form):
        self.object = form.save(commit=False)

        # Do any custom stuff here

        self.object.save()

        return render_to_response(self.template_name, self.get_context_data())

当然,如果我误解了你,请告诉我。不过你应该可以让它工作。

【讨论】:

很好,我只是没明白我们可以在 UpdateView 中使用 form_class 属性 再想一想,你真的需要self.object = form.save(commit=False)吗? 不,如果您不打算进行任何其他更改,则不这样做。你可以只做self.object = form.save() 并取出self.object.save()。更好的是,您可以删除整个 form_valid 方法。我只是把它放在那里,以防您在将对象保存到数据库之前需要对对象进行任何其他处理。 对,但我的意思是:self.object 在调用form_valid 之前不是已经定义了吗? 是的。但是form_valid 是应该从表单中保存对象的位置。如果您查看ModelFormMixinform_valid 方法(UpdateView 继承自),它就是这样做的:self.object = form.save()。所以要么打电话给super().form_valid,要么自己保存表格。就像我展示的那样。【参考方案3】:

对于此线程的任何其他访问者,是的,您可以创建一个 FormView,其作用类似于 CreateViewUpdateView。尽管有其他用户的一些意见,但如果您希望有一个 单个 表单/URL/页面用于 Web 表单以保存一些用户数据,这些数据可以是可选的,但需要得救一次且只有一次。您不希望为此有 2 个 URL/视图,而只有一个显示表单的页面/URL,如果用户已保存模型,则填充要更新的先前数据。

考虑一种像这样的“接触”模型:

from django.conf import settings
from django.db import models


class Contact(models.Model):
    """
    Contact details for a customer user.
    """
    user = models.OneToOneField(settings.AUTH_USER_MODEL)
    street = models.CharField(max_length=100, blank=True)
    number = models.CharField(max_length=5, blank=True)
    postal_code = models.CharField(max_length=7, blank=True)
    city = models.CharField(max_length=50, blank=True)
    phone = models.CharField(max_length=15)
    alternative_email = models.CharField(max_length=254)

所以,你给它写一个ModelForm,像这样:

from django import forms

from .models import Contact


class ContactForm(forms.ModelForm):
    class Meta:
        model = Contact
        exclude = ('user',)  # We'll set the user later.

您的FormView 同时具有“创建”和“更新”功能将如下所示:

from django.core.urlresolvers import reverse
from django.views.generic.edit import FormView

from .forms import ContactForm
from .models import Contact


class ContactView(FormView):
    template_name = 'contact.html'
    form_class = ContactForm
    success_url = reverse('MY_URL_TO_REDIRECT')

    def get_form(self, form_class):
        """
        Check if the user already saved contact details. If so, then show
        the form populated with those details, to let user change them.
        """
        try:
            contact = Contact.objects.get(user=self.request.user)
            return form_class(instance=contact, **self.get_form_kwargs())
        except Contact.DoesNotExist:
            return form_class(**self.get_form_kwargs())

    def form_valid(self, form):
        form.instance.user = self.request.user
        form.save()
        return super(ContactView, self).form_valid(form)

您甚至不需要在此示例的 URL 中使用 pk,因为该对象是通过 user 一对一字段从数据库中检索的。如果您有类似的情况,其中要创建/更新的模型与用户具有唯一的关系,那就很容易了。

希望这对某人有所帮助...

干杯。

【讨论】:

调用 return super(ContactView, self).form_valid(form) 将涉及 save() 已保存的表单对象。 完美,这很有用,所以我可以避免将用户 ID 放在 url 中! 如果省略 **self.get_form_kwargs() 会发生什么?对我来说,这条线 form_class(instance=contact, **self.get_form_kwargs()) 产生了一些错误,说 instance kwarg got multiple values。

以上是关于django - 使用 FormView 和 ModelForm 更新模型的主要内容,如果未能解决你的问题,请参考以下文章

Django:如何使用动态(非模型)数据预填充 FormView?

Django:如何使用FormView和ajax将对象保存到数据库?

重定向到 django FormView 中的下一个 URL

使用jquery post时未调用django formview中的post函数

python 基于FormView类的django视图示例

使用 FormView django 在 form_valid 中渲染模板而不是 success_url