Как создать форму Django, которая отображает метку флажка справа от флажка? - PullRequest
32 голосов
/ 21 февраля 2009

Когда я определяю класс формы 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 неправильным ответом.

[Изменить]

Похоже, я отвечаю на свой вопрос ниже. Если у кого-то есть идея, как это сделать, не стесняйтесь.

Ответы [ 7 ]

32 голосов
/ 12 января 2010

Вот решение, которое я придумал (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)

Более подробная информация о пользовательских тегах шаблонов доступна здесь .

Редактировать : в духе собственного ответа Аскера:

Преимущества:

  1. Нет возни с CSS.
  2. Разметка в конечном итоге выглядит так, как должна.
  3. Я не взломал внутренности Джанго. (но пришлось посмотреть на кучу)
  4. Шаблон красивый, компактный и идиоматичный.
  5. Код фильтра проигрывается независимо от точных значений меток и имен полей ввода.

Недостатки:

  1. Возможно, где-то есть что-то, что делает это лучше и быстрее.
  2. Маловероятно, что клиент захочет платить за все время, потраченное на это, просто чтобы переместить ярлык вправо ...
15 голосов
/ 22 февраля 2010

Я взял ответ от Ромкинса и сделал его немного более общим

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 %}
11 голосов
/ 09 марта 2013

Все представленные решения включают модификации шаблона, которые в целом довольно неэффективны с точки зрения производительности. Вот пользовательский виджет, который делает эту работу:

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 в дополнительном файле, поэтому они могут быть импортированы при необходимости. Если вам не нужны переводы, вы можете удалить части ugettext. Этот код работает на Django 1.5 и не тестируется для более низких версий.

Преимущества:

  • Высокоэффективный, не требует изменений шаблона
  • Простой в использовании в качестве пользовательского виджета

Недостатки:

  • "as_table" отображает флажок + метку во втором столбце
  • {{field.label}} внутри шаблона пусто. Вместо этого метка привязана к {{field}}
  • Больше работы, чем я планировал делать в субботу; -)
3 голосов
/ 23 июля 2013

Я знаю, что пользователь исключил 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 не отражает презентацию (но достаточно хорош!)
  • Ваш фронтендист мог жаловаться
2 голосов
/ 22 февраля 2009

Вот что я в итоге сделал. Я написал собственный шаблонный строковый фильтр для переключения тегов. Теперь мой код шаблона выглядит так:

{% 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 сканирует строковый аргумент, находит пары тегов

Преимущества:

  1. Не возиться с CSS.
  2. Разметка в конечном итоге выглядит так, как и должна.
  3. Я не взломал внутренности Джанго.
  4. Шаблон красивый, компактный и идиоматичный.

Недостатки:

  1. Код фильтра необходимо проверить на захватывающие значения меток и имен полей ввода.
  2. Возможно, где-то есть что-то, что делает это лучше и быстрее.
  3. В субботу больше работы, чем я планировал.
1 голос
/ 15 февраля 2015

Изменение позиции флажка в админке 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

1 голос
/ 21 февраля 2009

Порядок ввода и меток предоставляется через параметр normal_row формы, и для флажков нет разных шаблонов строк. Таким образом, есть два способа сделать это (точно в версии 0.96):
1. переопределить _html_output формы
2. используйте CSS для изменения положения метки и флажка

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...