Django:仅更新UpdateView中已更改的字段
Posted
技术标签:
【中文标题】Django:仅更新UpdateView中已更改的字段【英文标题】:Django: Only update fields that have been changed in UpdateView 【发布时间】:2013-08-22 23:52:29 【问题描述】:我正在使用 UpdateView 来更新一系列字段。但是,我只希望将已修改的字段保存到数据库中。如果在更新过程中没有为字段提供值,我希望将以前的值用作默认值。如果为字段提供了新值,则只应更新该字段。我该怎么做呢?
#views.py
class AccountUpdate(UpdateView):
""" Updates an account; unchanged fields will not be updated."""
context_object_name = 'account'
form_class = UpdateForm
template_name = 'accounts/update_account.html'
success_url = '/accounts/home'
def get_object(self, queryset=None):
return self.request.user
def form_valid(self, form):
clean = form.cleaned_data
#encrypt plain password
form.instance.password = hash_password(clean['password'])
context =
self.object = context.update(clean, force_update=False)
return super(AccountUpdate, self).form_valid(form)
#templates
% extends 'base.html' %
<title>% block title %Update Account% endblock %</title>
% block content %
account.non_field_errors
<div class="errors" style="list-style-type: none;">
% if account.first_name.errors % account.first_name.errors % endif %
% if account.last_name.errors % account.last_name.errors % endif %
% if account.email.errors % account.email.errors % endif %
% if account.password.errors % account.password.errors % endif %
% if account.username.errors % account.username.errors % endif %
</div>
<form action="" name="edit" method="post">
% csrf_token %
<ul>
<li>Fullname</li>
<li> form.first_name form.last_name </li>
<li>Email</li>
<li> form.email </li>
<li>Password</li>
<li> form.password </li>
<li>Username</li>
<li> form.username </li>
</ul>
<ul>
<li><input type="submit" value="update account"></li>
</ul>
</form>
<ul>
<li class="nav"><a href="/accounts/">cancel changes</a></li>
</ul>
% endblock %
在models.py
中还根据需要声明了所有字段。目前该表单仅在我为每个字段提供值时才有效。
【问题讨论】:
【参考方案1】:我在更新过程中使用自定义哈希来加密密码。当我访问编辑页面并点击更新按钮时,当前加密形式的旧密码被重新加密,因此丢失旧密码
我会通过不在表单中包含 password
本身来处理这个问题。相反,我会添加一个新字段(类似于new_password
)以允许输入新密码。然后,在您的 is_valid
方法中,将密码设置为该字段的哈希值(如果有内容)。
您还应该使用sensitive value filtering 工具来防止用户密码出现在通过电子邮件发送的错误报告中。
class UpdateForm(forms.ModelForm):
class Meta:
model = user
fields = ('first_name', 'last_name', 'email', 'username')
new_password = forms.CharField(required=False, widget=forms.widgets.PasswordInput)
然后在你看来:
@sensitive_variables('new_password')
@sensitive_post_parameters('new_password')
def form_valid(self, form):
clean = form.cleaned_data
new_password = clean.get('new_password')
if new_password:
#encrypt plain password
form.instance.password = hash_password(new_password)
return super(AccountUpdate, self).form_valid(form)
【讨论】:
感谢您的解决方案。根据您对 Rob 回答的评论,我想我将能够解决个别字段更新问题。完成后我会发布我的解决方案。谢谢! 我不太确定您还有什么其他字段更新问题 - 如果没有更改,设置first_name = "Joe"
应该是无害的。
如果 update_form 预先填充了first_name = 'joe'
,但用户决定删除更新页面上该字段中的值并将其留空,我希望first_name
默认返回@987654330 @。很抱歉造成混乱。
我明白了。这是使用通用视图更难设置的东西,至少如果您仍然允许某些字段合法地为空白的可能性。我想你可以遍历表单的 changed_data
名称,看看它们是否为空,如果是,则复制 self.object
版本的值。
if you're still allowing for the possibility of some of the fields legitimately being blank.
所有四个字段都是必需的。我还能继续循环吗?【参考方案2】:
最简单的方法是使用已经存在的内容预先填充字段。
使用 account.name
或其他方式在模板上执行此操作。
【讨论】:
我已经用我的模板文件更新了这个问题。当我进入编辑页面时,表单已经预先填充了除密码字段之外的值(不知道为什么)但是当我尝试保存表单时,除非我填写所有空白字段 在password
上尝试render_value=True
。见:docs.djangoproject.com/en/dev/ref/forms/widgets/…
好的,render_value=True 工作...唯一的问题是,我在更新过程中使用自定义哈希来加密密码。当我访问编辑页面并点击更新按钮时,当前加密形式的旧密码被重新加密,因此丢失了旧密码。另一方面,由于表单已经被预先填充,我该如何解决我最初的问题,即只更新更改的字段?
我能想到的唯一方法是使用一系列 if-elif 语句将数据库中的每个字段与提交的值进行匹配,以查看它们是否不同,然后执行单个字段更新查询。 .这听起来乏味且不符合 Python 风格
另外,您为什么在更新表单上显示密码?把它放在一个单独的表单上,或者如果用户没有更改他/她的密码,则让它为空。以上是关于Django:仅更新UpdateView中已更改的字段的主要内容,如果未能解决你的问题,请参考以下文章
使用基于类的 UpdateView 在 Django 中更新用户模型
带有更新的 slug 的 Django 通用视图 UpdateView 重定向 URL
Django 使用 UpdateView 出现此错误:/user/2/edit 处的 NoReverseMatch