Situation
Using Django 1.5, I am using forms.ModelForm
s to let the user edit database contents. However I can't get the form to update the database upon form.save()
.
使用Django 1.5,我使用表单。ModelForms允许用户编辑数据库内容。但是,我不能让表单在form.save()上更新数据库。
Each of my models correspond to a setting form (the application is a the direct porting of a desktop software in which the user can store several settings). I needed to implement a Reset to default feature, so I thought of having a default object (imported with Django fixtures) which I would use only to reset a second one. The user would only interact with the second model.
我的每个模型都对应于一个设置表单(应用程序是一个桌面软件的直接移植,用户可以在其中存储多个设置)。我需要实现对默认特性的重置,所以我想要一个默认对象(用Django fixture导入),我只使用它来重置第二个对象。用户只与第二个模型交互。
-
pk=1
refers to the base object - pk=1表示基对象
-
pk=2
refers to the custom object - pk=2表示自定义对象
I have several forms on the same page (only foobar
here), so basically this what I planned to do:
我在同一页上有好几张表格(这里只有foobar),所以基本上这就是我计划做的:
- No POST data
- Building form from either
pk=1
orpk=2
, dependingpk=2
has been found or not - 从pk=1或pk=2构建形式,取决于pk=2是否被发现。
- Rendering the forms to the template
- 将表单呈现给模板
- Building form from either
- 从pk=1或pk=2中,没有任何POST数据构建形式,这取决于pk=2是否被发现或没有将表单呈现给模板。
- AJAX request, with POST datas
- Getting form content
- 获得表单内容
- Checking whether or not the user has permission to edit the model (checksum)
- 检查用户是否具有编辑模型的权限(校验和)
- Update the model form POST datas
- 更新模型表单后的数据
- Returning AJAX response
- 返回AJAX响应
- AJAX请求,POST数据获取表单内容,检查用户是否有权限编辑模型(checksum),更新模型表单POST数据,返回AJAX响应
Code
I have put two debug print
s to illustrate the issue I am facing. The form I fetch doesn't seem to be bound to my model.
我放了两个调试输出来说明我所面临的问题。我获取的表单似乎并不与我的模型绑定。
# Response codes to use in the template
RESPONSES = {
200: {'code':'0xB16B00B5', 'message':'Success'},
400: {'code':'0x8BADF00D', 'message':'Form is not valid'},
403: {'code':'0xBAADF00D', 'message':'No permission to edit the database'},
501: {'code':'0xDEADC0DE', 'message':'POST datas not found'},
}
# Those are the setting labels
TYPES = {
'foobar': {'model':FooBar, 'form':FooBarForm },
}
def index(request):
# Handling form datas
if request.method == 'POST':
response = HttpResponse(simplejson.dumps({'code':RESPONSES[501]['code']}), 'application/json')
for label in TYPES:
# Filtering the right form to handle
if label in request.POST:
model = _fetch_setting(label, mode='model')
form = _fetch_setting(label, mode='form', post=request.POST)
checksum = model.checksum # Somehow, 'form.is_valid()' is altering 'model', need to backup the checksum
if form.is_valid():
# The user has permission to edit the model
if form.cleaned_data['checksum'] == checksum:
if form.has_changed():
print form.cleaned_data['foo'] # Outputs the form data, as expected
form.save()
print model.foo # Outputs the old data
model.checksum = str(uuid4()).replace('-', '')
model.save()
response = HttpResponse(simplejson.dumps({'code':RESPONSES[200]['code']}), 'application/json')
# This one does not
else:
response = HttpResponse(simplejson.dumps({'code':RESPONSES[403]['code']}), 'application/json')
break # We are still inside the label loop
# The form is not valid
else:
response = HttpResponse(simplejson.dumps({'code':RESPONSES[400]['code']}), 'application/json')
# Form not submitted yet, building the HTML forms
else:
forms = {}
label = 'foobar'
for label in TYPES:
forms[label] = _fetch_setting(label, mode='form')
context = {'errors':RESPONSES, 'forms':forms}
response = render(request, 'home/index.html', context)
return response
# Return a setting object (model or form) corresponding to the given label
def _fetch_setting(label, mode='model', post=None):
try:
result = None
default = TYPES[label]['model'].objects.get(pk=1)
try:
model = TYPES[label]['model'].objects.get(pk=2)
except TYPES[label]['model'].DoesNotExist:
model = TYPES[label]['model'].objects.create(
checksum = default.checksum,
foo = default.foo,
bar = default.bar,
)
if mode == 'model':
result = model
if mode == 'form':
print model
result = TYPES[label]['form'](data=post, instance=model) # The 'instance' attribute doesn't seem to be applied
except KeyError:
result = None
finally:
return result
Update
07.10
It does work when I pass the instance to bound with to _fetch_setting
. So I guess this issue is coming from the form validation.
当我将实例传递给_fetch_setting时,它就会工作。我猜这个问题来自表单验证。
def _fetch_setting(label, mode='model', post=None, instance=None):
# ...
if mode == 'form':
if instance:
model = instance
result = TYPES[label]['form'](data=post, instance=model)
# ...
As I commented in my code, form.is_valid()
seems to alter the object.
正如我在代码中所注释的,form.is_valid()似乎改变了对象。
Will flag as answered if no one come with a clean solution.
如果没有人提出干净的解决方案,威尔就会像回答一样发出信号。
2 个解决方案
#1
1
The issue is, you are creating a new model object with each form.save()
问题是,您正在使用每个form.save()创建一个新的模型对象
You need to update the same model object with commit=False
您需要使用commit=False更新相同的模型对象
if form.cleaned_data['checksum'] == checksum:
if form.has_changed():
print form.cleaned_data['foo'] # Outputs the form data, as expected
model = form.save(commit=False)
model.checksum = str(uuid4()).replace('-', '')
model.save()
#2
0
From the fabulous manual:
的手册:
The first time you call
is_valid()
or access the errors attribute of aModelForm
triggers form validation as well as model validation. This has the side-effect of cleaning the model you pass to the ModelForm constructor. For instance, callingis_valid()
on your form will convert any date fields on your model to actual date objects. If form validation fails, only some of the updates may be applied. For this reason, you’ll probably want to avoid reusing the model instance passed to the form, especially if validation fails.第一次调用is_valid()或访问ModelForm的errors属性时,将触发表单验证和模型验证。这样做的副作用是清除传递给ModelForm构造函数的模型。例如,在窗体上调用is_valid()将把模型中的任何日期字段转换为实际的日期对象。如果表单验证失败,只能应用一些更新。由于这个原因,您可能希望避免重用传递给表单的模型实例,特别是在验证失败的情况下。
#1
1
The issue is, you are creating a new model object with each form.save()
问题是,您正在使用每个form.save()创建一个新的模型对象
You need to update the same model object with commit=False
您需要使用commit=False更新相同的模型对象
if form.cleaned_data['checksum'] == checksum:
if form.has_changed():
print form.cleaned_data['foo'] # Outputs the form data, as expected
model = form.save(commit=False)
model.checksum = str(uuid4()).replace('-', '')
model.save()
#2
0
From the fabulous manual:
的手册:
The first time you call
is_valid()
or access the errors attribute of aModelForm
triggers form validation as well as model validation. This has the side-effect of cleaning the model you pass to the ModelForm constructor. For instance, callingis_valid()
on your form will convert any date fields on your model to actual date objects. If form validation fails, only some of the updates may be applied. For this reason, you’ll probably want to avoid reusing the model instance passed to the form, especially if validation fails.第一次调用is_valid()或访问ModelForm的errors属性时,将触发表单验证和模型验证。这样做的副作用是清除传递给ModelForm构造函数的模型。例如,在窗体上调用is_valid()将把模型中的任何日期字段转换为实际的日期对象。如果表单验证失败,只能应用一些更新。由于这个原因,您可能希望避免重用传递给表单的模型实例,特别是在验证失败的情况下。