Django Crispy Forms、Twitter Bootstrap 和 Formsets with form-inline
Posted
技术标签:
【中文标题】Django Crispy Forms、Twitter Bootstrap 和 Formsets with form-inline【英文标题】:Django Crispy Forms, Twitter Bootstrap, and Formsets with form-inline 【发布时间】:2013-08-01 06:49:49 【问题描述】:我正在试验django-crispy-forms 和 Twitter Bootstrap,这是 django-crispy-forms 用作其默认模板包的。我正在使用 Django 1.4.5、django-crispy-forms 1.2.3 和 Twitter Bootstrap 2.3.2。
相关代码
urls.py
from django.conf.urls import patterns, url
from core import views
urlpatterns = patterns('',
url(r'^$', views.Survey.as_view(), name='survey'),
)
views.py
from django.views.generic.edit import FormView
from .forms import SurveyFormset
class Survey(FormView):
form_class = SurveyFormset
template_name = 'survey.html'
forms.py
from django import forms
from django.forms.formsets import formset_factory
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
class SurveyForm(forms.Form):
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_action = '.'
self.helper.form_class = 'form-inline'
self.helper.add_input(Submit('submit', 'Submit'))
super(SurveyForm, self).__init__(*args, **kwargs)
name = forms.CharField(
widget=forms.TextInput(attrs='placeholder': 'Name'),
label='',
max_length=50
)
favorite_food = forms.CharField(
widget=forms.TextInput(attrs='placeholder': 'Favorite food'),
label='',
max_length=50
)
favorite_game = forms.CharField(
widget=forms.TextInput(attrs='placeholder': 'Favorite game'),
label='',
max_length=50
)
SurveyFormset = formset_factory(SurveyForm, extra=2)
survey.html
% load crispy_forms_tags %
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Survey</title>
<link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet">
</head>
<body>
% crispy form form.form.helper %
</body>
</html>
实际结果
尽管我将表单的类设置为form-inline
,但表单字段都是堆叠的。这也使您无法分辨出有两种不同的形式;它看起来就像一个带有重复字段的长表单。
这是正在生成的 HTML:
<form action="." class="form-inline" method="post">
<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='foo' /></div>
<div>
<input type="hidden" name="form-TOTAL_FORMS" value="2" id="id_form-TOTAL_FORMS" />
<input type="hidden" name="form-INITIAL_FORMS" value="0" id="id_form-INITIAL_FORMS" />
<input type="hidden" name="form-MAX_NUM_FORMS" value="1000" id="id_form-MAX_NUM_FORMS" />
</div>
<div id="div_id_form-0-name" class="control-group">
<div class="controls">
<input name="form-0-name" maxlength="50" placeholder="Name" type="text" class="textinput textInput" id="id_form-0-name" />
</div>
</div>
<div id="div_id_form-0-favorite_food" class="control-group">
<div class="controls">
<input name="form-0-favorite_food" maxlength="50" placeholder="Favorite food" type="text" class="textinput textInput" id="id_form-0-favorite_food" />
</div>
</div>
<div id="div_id_form-0-favorite_game" class="control-group">
<div class="controls">
<input name="form-0-favorite_game" maxlength="50" placeholder="Favorite game" type="text" class="textinput textInput" id="id_form-0-favorite_game" />
</div>
</div>
<div id="div_id_form-1-name" class="control-group">
<div class="controls">
<input name="form-1-name" maxlength="50" placeholder="Name" type="text" class="textinput textInput" id="id_form-1-name" />
</div>
</div>
<div id="div_id_form-1-favorite_food" class="control-group">
<div class="controls">
<input name="form-1-favorite_food" maxlength="50" placeholder="Favorite food" type="text" class="textinput textInput" id="id_form-1-favorite_food" />
</div>
</div>
<div id="div_id_form-1-favorite_game" class="control-group">
<div class="controls">
<input name="form-1-favorite_game" maxlength="50" placeholder="Favorite game" type="text" class="textinput textInput" id="id_form-1-favorite_game" />
</div>
</div>
<div class="form-actions">
<input type="submit" name="submit" value="Submit" class="btn btn-primary" id="submit-id-submit" />
</div>
</form>
预期结果
由于 Twitter Bootstrap 是 django-crispy-forms 使用的默认模板包,我希望它默认能够很好地处理这个问题。这些字段应该并排,而不是堆叠,并且应该很明显有两种不同的形式。
我希望 HTML 看起来像这样:
<form action="." class="form-inline" method="post">
<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='foo' /></div>
<div>
<input type="hidden" name="form-TOTAL_FORMS" value="2" id="id_form-TOTAL_FORMS" />
<input type="hidden" name="form-INITIAL_FORMS" value="0" id="id_form-INITIAL_FORMS" />
<input type="hidden" name="form-MAX_NUM_FORMS" value="1000" id="id_form-MAX_NUM_FORMS" />
</div>
<div id="id_form-0" class="control-group">
<input type="text" id="id_form-0-name" class="textinput textInput" placeholder="Name" maxlength="50" name="form-0-name">
<input type="text" id="id_form-0-favorite_food" class="textinput textInput" placeholder="Favorite food" maxlength="50" name="form-0-favorite_food">
<input type="text" id="id_form-0-favorite_game" class="textinput textInput" placeholder="Favorite game" maxlength="50" name="form-0-favorite_game">
</div>
<div id="id_form-1" class="control-group">
<input type="text" id="id_form-1-name" class="textinput textInput" placeholder="Name" maxlength="50" name="form-1-name">
<input type="text" id="id_form-1-favorite_food" class="textinput textInput" placeholder="Favorite food" maxlength="50" name="form-1-favorite_food">
<input type="text" id="id_form-1-favorite_game" class="textinput textInput" placeholder="Favorite game" maxlength="50" name="form-1-favorite_game">
</div>
<div class="form-actions">
<input type="submit" name="submit" value="Submit" class="btn btn-primary" id="submit-id-submit" />
</div>
</form>
我做错了吗?我应该怎么做才能获得预期的结果?
【问题讨论】:
【参考方案1】:我也能够复制这个问题。虽然我不能 100% 确定这种行为,但我能够在 django-crispy-forms documentation 中找到以下内容:
#forms.py
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_action = '.'
self.helper.form_class = 'form-inline'
self.helper.template = 'bootstrap/table_inline_formset.html'
self.helper.add_input(Submit('submit', 'Submit'))
super(SurveyForm, self).__init__(*args, **kwargs)
# views.py
from django.views.generic.edit import FormView
from .forms import SurveyFormset, SurveyForm
class Survey(FormView):
form_class = SurveyForm
template_name = 'survey.html'
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = SurveyFormset()
return self.render_to_response(
self.get_context_data(
form=form,
formset=formset,
)
)
def post(self, request, *args, **kwargs):
"""
Handles POST requests, instantiating a form instance and its inline
formsets with the passed POST variables and then checking them for
validity.
"""
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = SurveyFormset(self.request.POST)
if (form.is_valid() and formset.is_valid()):
return self.form_valid(form, formset)
else:
return self.form_invalid(form, formset)
def form_valid(self, form, formset):
"""
Called if all forms are valid. Creates a Recipe instance along with
associated Ingredients and Instructions and then redirects to a
success page.
"""
self.object = form.save()
formset.instance = self.object
formset.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, formset):
"""
Called if a form is invalid. Re-renders the context data with the
data-filled forms and errors.
"""
return self.render_to_response(
self.get_context_data(
form=form,
formset=formset,
)
)
# survey.html
% load crispy_forms_tags %
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Survey</title>
<link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row">
<div class="span12">
% crispy formset formset.form.helper 'bootstrap' %
</div>
</div>
</div>
</body>
</html>
虽然我对使用 div 的解决方案感到好奇,因为 django-crispy-forms 文档指出它应该可以工作,但我可以确认该解决方案在我的机器上有效。上面的部分代码归功于Kevin Dias。
【讨论】:
以上是关于Django Crispy Forms、Twitter Bootstrap 和 Formsets with form-inline的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Ajax 渲染 django-crispy-forms 的验证错误?
Django-crispy-forms:为 AppendedText 父 div 设置 CSS 类
python测试开发django-41.crispy-forms设计标签式导航菜单(TabHolder)
使用内联表单在 django-crispy-forms 中呈现字段错误