简体   繁体   English

Django 的默认模板表单渲染 ( {{ form }} ) 是如何工作的?

[英]How does Django's default template form rendering ( {{ form }} ) work?

The most basic way to render a form in a template in Django is to add an instance of the form to the context:在 Django 的模板中渲染表单的最基本方法是将表单的一个实例添加到上下文中:

context['form'] = MyForm()

And then, in your template, render the form by calling it as a template variable:然后,在您的模板中,通过将其作为模板变量调用来呈现表单:

<form method="POST">
    {% csrf_token %}
    {{ form }}
</form>

When this template is rendered, {{ form }} gets rendered as:当这个模板被渲染时, {{ form }}被渲染为:

<label for="my_field">my_field:</label>
<input type="default_widget_for_field_type" name="my_field" />

with one pair for each field in your form.表单中的每个字段一对。

My question is: what code path on the form and/or in the templating language actually does this rendering?我的问题是:表单和/或模板语言中的哪些代码路径实际上进行了这种渲染? Based on other conventions in the Django templating language, I had expected the Python underlying {{ form }} to be form() , much like {{ object.get_absolute_url }} ends up being object.get_absolute_url() , but when I instantiate an instance of my form in the shell and try to call it, I get a TypeError telling me it's not callable.基于 Django 模板语言中的其他约定,我曾期望 Python 底层{{ form }}form() ,很像{{ object.get_absolute_url }}最终是object.get_absolute_url() ,但是当我实例化一个我的表单在 shell 中的实例并尝试调用它,我收到一个TypeError告诉我它不可调用。 I had a look at the Django source code trying to figure out where this gets done, but I didn't have much luck.我查看了 Django 源代码,试图找出这是在哪里完成的,但我运气不佳。


Here's my use case.这是我的用例。 I want to create a ModelForm for a model representing a comment on a piece of content.我想为表示对一段内容的评论的模型创建一个 ModelForm。 It has a foreign key to that content model.它具有该内容模型的外键。 There are multiple pieces of content on each page, so I need to instantiate multiple, unbound copies of the ModelForm to create comments on each piece of content.每个页面上都有多条内容,因此我需要实例化多个未绑定的 ModelForm 副本,以对每条内容创建注释。 Each form will be attached to its content object on the page, so I don't want the foreign key to be a user-editable field.每个表单都将附加到页面上的内容对象,因此我希望外键成为用户可编辑的字段。

This means that I need to take care of populating the foreign key field, and it makes sense to me to do that from the template as a hidden form field, because the template will have access to the object that it needs to set the foreign key to.这意味着我需要注意填充外键字段,从模板作为隐藏表单字段执行此操作对我来说是有意义的,因为模板将可以访问它需要设置外键的对象到。 However, if I leave the foreign key field off of the ModelForm, then the model won't save correctly on form validation.但是,如果我离开 ModelForm 的外键字段,那么模型将无法在表单验证中正确保存。 I could override the form validation to add in the hidden field, but I feel like a more elegant way to do it would be to add the field to the ModelForm, then override the ModelForm's rendering method so it doesn't try to render that field.可以覆盖表单验证以添加到隐藏字段中,但我觉得更优雅的方法是将字段添加到 ModelForm,然后覆盖 ModelForm 的渲染方法,这样它就不会尝试渲染该字段. Then I'll add the field manually in my template.然后我将在我的模板中手动添加该字段。

{% for piece_of_content in page_contents %}
    {# the content #}
    <p>Add your comment!</p>
    <form method="POST" action="{% url 'comment-create' %}">
        {% csrf_token %}
        {# the overridden rendering method won't render the foreign key field... #}
        {{ form }}
        {# then I add it manually #}
        <input type="hidden" name="commented_on" value="{{ piece_of_content.id }}" />
    </form>

Then I wire the url for 'comment-create' to a CreateView , set its form_class to the ModelForm, and it will do its thing without any additional input from my end.然后我将 'comment-create' 的 url 连接到一个CreateView ,将它的form_class设置为 ModelForm,它会在没有任何额外输入的情况下完成它的工作。

If the form were just callable, like I thought, then I could override __call__ on the ModelForm, and tell it to render everything but the foreign key field.如果表单只是调用,就像我想的话,我可以覆盖__call__上的ModelForm,并告诉它使一切,但外键字段。 But the ModelForm doesn't have a __call__ , and I can't figure out where it is done!但的ModelForm不具有__call__ ,我想不通的地方完成了! The form has methods for rendering various other configurations ( as_p , as_ul , and as_table ), but none that I see for just the default rendering.该表单具有渲染各种其他配置( as_pas_ulas_table )的方法,但我没有看到仅用于默认渲染的方法。

Including {{ form }} is the same as {{ form.as_table }} .包括{{ form }}{{ form.as_table }} This is because the form's __str__ method calls its as_table() method.这是因为表单的__str__方法调用了它的as_table()方法。 See the docs on outputting forms as HTML for more info.有关更多信息,请参阅将表单输出为 HTML的文档。

The docs on template variables and lookups explain how variables are included in the template.关于模板变量和查找的文档解释了如何将变量包含在模板中。 If attributes are callable, the method is called.如果属性是可调用的,则调用该方法。 However in your case, form instances are not callable, so the unicode representation of the form is displayed.但是,在您的情况下,表单实例不可调用,因此会显示表单的 unicode 表示形式。

If you don't want users to edit a field, I suggest you exclude it from the form and set it in the view.如果您不希望用户编辑某个字段,我建议您将其从表单中排除并在视图中进行设置。 Users can tamper with the values of hidden fields.用户可以篡改隐藏字段的值。

The answer to your question is that this is just the string representation of the form: __unicode__() calls as_p() which renders the form.您的问题的答案是,这只是表单的字符串表示形式: __unicode__()调用呈现表单的as_p()

However, this really isn't the way to do it.然而,这确实不是这样做的方法。 Rather than trying to hack both output and validation, and then try to insert the value, you should just exclude the foreign key from the form altogether.与其尝试破解输出和验证,然后尝试插入值,您应该完全从表单中排除外键。 Then simply add it when you save: you can get the relevant value from the URL that the form posts to, and add it there:然后在保存时简单地添加它:您可以从表单发布到的 URL 中获取相关值,并将其添加到那里:

if form.is_valid():
    obj = form.save(commit=False)
    obj.content_id = value_from_url
    obj.save()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM