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
是应该从表单中保存对象的位置。如果您查看ModelFormMixin
的form_valid
方法(UpdateView
继承自),它就是这样做的:self.object = form.save()
。所以要么打电话给super().form_valid
,要么自己保存表格。就像我展示的那样。【参考方案3】:
对于此线程的任何其他访问者,是的,您可以创建一个 FormView
,其作用类似于 CreateView
和 UpdateView
。尽管有其他用户的一些意见,但如果您希望有一个 单个 表单/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