如何检查Django表单是否正确绑定?

时间:2021-07-15 19:22:16

Situation


Using Django 1.5, I am using forms.ModelForms 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
    1. Building form from either pk=1 or pk=2, depending pk=2 has been found or not
    2. 从pk=1或pk=2构建形式,取决于pk=2是否被发现。
    3. Rendering the forms to the template
    4. 将表单呈现给模板
  • 从pk=1或pk=2中,没有任何POST数据构建形式,这取决于pk=2是否被发现或没有将表单呈现给模板。
  • AJAX request, with POST datas
    1. Getting form content
    2. 获得表单内容
    3. Checking whether or not the user has permission to edit the model (checksum)
    4. 检查用户是否具有编辑模型的权限(校验和)
    5. Update the model form POST datas
    6. 更新模型表单后的数据
    7. Returning AJAX response
    8. 返回AJAX响应
  • AJAX请求,POST数据获取表单内容,检查用户是否有权限编辑模型(checksum),更新模型表单POST数据,返回AJAX响应

Code


I have put two debug prints 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 a ModelForm 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, calling is_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 a ModelForm 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, calling is_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()将把模型中的任何日期字段转换为实际的日期对象。如果表单验证失败,只能应用一些更新。由于这个原因,您可能希望避免重用传递给表单的模型实例,特别是在验证失败的情况下。