Как создать форму Django, которая отображает метку флажка справа от флажка?
Когда я определяю класс формы Django, подобный этому:
def class MyForm(forms.Form):
check = forms.BooleanField(required=True, label="Check this")
Он расширяется до HTML, который выглядит следующим образом:
<form action="." id="form" method=POST>
<p><label for="check">Check this:</label> <input type="checkbox" name="check" id="check" /></p>
<p><input type=submit value="Submit"></p>
</form>
Я хотел бы, чтобы элемент ввода флажка имел метку, которая следует за флажком, а не наоборот. Есть ли способ убедить Джанго в этом?
[изменить]
Спасибо за ответ от Jonas - все же, хотя он исправляет проблему, о которой я спрашивал (метки флажков отображаются справа от флажка), она вводит новую проблему (все ярлыки виджетов отображаются справа от их виджетов...)
Я бы хотел избежать переопределения _html_output(), поскольку он явно не предназначен для него. Дизайн, который я придумал, - это реализовать метод вывода html поля в классах Field, переопределить значение для булевого поля и использовать этот метод в _html_output(). К сожалению, разработчики Django решили пойти по-другому, и я хотел бы как можно больше работать в рамках существующей структуры.
CSS похож на достойный подход, за исключением того, что я не знаю достаточно CSS, чтобы снять это или даже решить, нравится ли мне этот подход или нет. Кроме того, я предпочитаю разметку, которая по-прежнему напоминает окончательный вывод, по крайней мере, в порядке отображения.
Кроме того, поскольку для любой конкретной разметки может быть разумно иметь более одной таблицы стилей, сделать это в CSS может означать, что нужно сделать это несколько раз для нескольких стилей, что в значительной степени делает неправильный ответ CSS.
[изменить]
Кажется, я отвечаю на свой вопрос ниже. Если кто-то лучше поймет, как это сделать, не стесняйтесь.
Ответы
Ответ 1
Вот что я в итоге сделал. Я написал собственный шаблон stringfilter для переключения тегов. Теперь мой код шаблона выглядит так:
{% load pretty_forms %}
<form action="." method="POST">
{{ form.as_p|pretty_checkbox }}
<p><input type="submit" value="Submit"></p>
</form>
Единственным отличием от простого шаблона Django является добавление тега шаблона {% load%} и фильтра pretty_checkbox.
Здесь функциональная, но уродливая реализация pretty_checkbox - этот код не имеет обработки ошибок, он предполагает, что созданные Django атрибуты отформатированы очень определенным образом, и это будет плохо идея использовать что-либо подобное в вашем коде:
from django import template
from django.template.defaultfilters import stringfilter
import logging
register=template.Library()
@register.filter(name='pretty_checkbox')
@stringfilter
def pretty_checkbox(value):
# Iterate over the HTML fragment, extract <label> and <input> tags, and
# switch the order of the pairs where the input type is "checkbox".
scratch = value
output = ''
try:
while True:
ls = scratch.find('<label')
if ls > -1:
le = scratch.find('</label>')
ins = scratch.find('<input')
ine = scratch.find('/>', ins)
# Check whether we're dealing with a checkbox:
if scratch[ins:ine+2].find(' type="checkbox" ')>-1:
# Switch the tags
output += scratch[:ls]
output += scratch[ins:ine+2]
output += scratch[ls:le-1]+scratch[le:le+8]
else:
output += scratch[:ine+2]
scratch = scratch[ine+2:]
else:
output += scratch
break
except:
logging.error("pretty_checkbox caught an exception")
return output
pretty_checkbox сканирует свой строковый аргумент, находит пары <label> и <input> теги и переключает их, если <input> тип тега - "флажок". Он также разбивает последний символ метки, который является символом ':'.
Преимущества:
- Без использования CSS.
- Разметка заканчивается тем, как она должна быть.
- Я не нарушил внутренние элементы Django.
- Шаблон хороший, компактный и идиоматический.
Недостатки:
- Код фильтра должен быть проверен на предмет захватывающих значений меток и имен полей ввода.
- Там, наверное, что-то там, где все лучше и быстрее.
- Больше работы, чем планировалось в субботу.
Ответ 2
Вот решение, которое я придумал (Django v1.1):
{% load myfilters %}
[...]
{% for field in form %}
[...]
{% if field.field.widget|is_checkbox %}
{{ field }}{{ field.label_tag }}
{% else %}
{{ field.label_tag }}{{ field }}
{% endif %}
[...]
{% endfor %}
Вам нужно создать собственный тег шаблона (в этом примере в файле "myfilters.py" ), содержащем что-то вроде этого:
from django import template
from django.forms.fields import CheckboxInput
register = template.Library()
@register.filter(name='is_checkbox')
def is_checkbox(value):
return isinstance(value, CheckboxInput)
Дополнительная информация о пользовательских тегах шаблонов доступна здесь.
Изменить: в духе собственного ответа на вопрос:
Преимущества:
- Без использования CSS.
- Разметка заканчивается тем, как она должна быть.
- Я не разбирал внутренности Django. (но пришлось посмотреть на довольно связку)
- Шаблон хороший, компактный и идиоматический.
- Код фильтра отлично воспроизводится независимо от точных значений названий ярлыков и полей ввода.
Недостатки:
- Возможно, что-то там где-то там, где все лучше и быстрее.
- Вряд ли клиент будет готов заплатить за все время, потраченное на это, чтобы переместить ярлык вправо...
Ответ 3
Я взял ответ от romkyns и сделал его немного более общим
def field_type(field, ftype):
try:
t = field.field.widget.__class__.__name__
return t.lower() == ftype
except:
pass
return False
Таким образом вы можете напрямую проверить тип виджета со строкой
{% if field|field_type:'checkboxinput' %}
<label>{{ field }} {{ field.label }}</label>
{% else %}
<label> {{ field.label }} </label> {{ field }}
{% endif %}
Ответ 4
Все представленные решения включают модификации шаблонов, которые в целом довольно неэффективны в отношении производительности. Здесь пользовательский виджет, выполняющий задание:
from django import forms
from django.forms.fields import BooleanField
from django.forms.util import flatatt
from django.utils.encoding import force_text
from django.utils.html import format_html
from django.utils.translation import ugettext as _
class PrettyCheckboxWidget(forms.widgets.CheckboxInput):
def render(self, name, value, attrs=None):
final_attrs = self.build_attrs(attrs, type='checkbox', name=name)
if self.check_test(value):
final_attrs['checked'] = 'checked'
if not (value is True or value is False or value is None or value == ''):
final_attrs['value'] = force_text(value)
if 'prettycheckbox-label' in final_attrs:
label = _(final_attrs.pop('prettycheckbox-label'))
else:
label = ''
return format_html('<label for="{0}"><input{1} /> {2}</label>', attrs['id'], flatatt(final_attrs), label)
class PrettyCheckboxField(BooleanField):
widget = PrettyCheckboxWidget
def __init__(self, *args, **kwargs):
if kwargs['label']:
kwargs['widget'].attrs['prettycheckbox-label'] = kwargs['label']
kwargs['label'] = ''
super(PrettyCheckboxField, self).__init__(*args, **kwargs)
# usage in form
class MyForm(forms.Form):
my_boolean = PrettyCheckboxField(label=_('Some label'), widget=PrettyCheckboxWidget())
У меня есть PrettyCheckboxWidget и PrettyCheckboxField в дополнительном файле, поэтому они могут быть импортированы там, где это необходимо. Если вам не нужны переводы, вы можете удалить фрагменты. Этот код работает на Django 1.5 и не проверен для более низких версий.
<сильные > Преимущества:
- Высокопроизводительный, не нуждается в модификациях шаблонов.
- Простота использования в качестве настраиваемого виджета
Недостатки:
- "as_table" отображает флажок + ярлык во втором столбце
- {{field.label}} внутри шаблона пуст. Вместо этого метка привязана к {{field}}
- Больше работы, чем планировалось в субботу; -)
Ответ 5
Я знаю, что пользователь исключил CSS, но, учитывая, что лучшие ответы занимают около получаса работы, чтобы сделать такую маленькую вещь, но зная, что такие детали важны на веб-сайте, я бы согласился на решение CSS.
checkbox.css
input[type="checkbox"] {
float: left;
margin-right: 10px;
margin-top: 4px;
}
forms.py
class MyForm(forms.ModelForm):
# ...
class Media:
css = {
'all': 'checkbox.css',
}
template.html
{{ form.media }}
{{ form.as_p }}
Преимущества:
- быстро!
- без использования тэгов шаблонов (просто
form.as_p
)
- нет новых проклятых виджетов
- файл CSS автоматически включается в каждую форму
Недостатки:
- HTML не отражает представление (но достаточно хорошо!)
- Ваш сторонник может жаловаться
Ответ 6
Порядок входов и меток предоставляется через параметр normal_row формы, и для флажков с разными строками нет. Итак, есть два способа сделать это (в версии 0.96 точно):
1. переопределить _html_output формы
2. С помощью CSS измените положение метки и установите флажок
Ответ 7
Изменение позиции флажка в администраторе Django может быть довольно сложным, но, к счастью, есть простое решение с использованием настраиваемого виджета:
from django.forms.widgets import Widget, CheckboxInput, boolean_check
class RightCheckbox(Widget):
render = CheckboxInput().render
def __init__(self, attrs=None, check_test=None):
super(RightCheckbox, self).__init__(attrs)
self.check_test = boolean_check if check_test is None else check_test
Django использует левую позицию только тогда, когда виджет является подклассом CheckboxInput