web-dev-qa-db-ja.com

チェックボックスの右側にチェックボックスのラベルを表示する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>

チェックボックスのinput要素に、チェックボックスに続くラベルではなく、逆にしたいです。 Djangoを説得する方法はありますか?

[編集]

ジョナスからの回答をありがとう-それでも、私が尋ねた問題は修正されますが(チェックボックスのラベルがチェックボックスの右側に表示されます)、新しい問題が発生します(すべてのウィジェットのラベルがウィジェットの右側に表示されます...)

_html_output()は明らかに設計されていないため、オーバーライドしないようにしたいと思います。私が思いつく設計は、Fieldクラスにフィールドhtml出力メソッドを実装し、ブールフィールド用のメソッドをオーバーライドして、そのメソッドを_html_output()で使用することです。悲しいことに、Django開発者は別の方法を選択しました。可能な限り既存のフレームワーク内で作業したいと考えています。

CSSはまともなアプローチのように聞こえますが、これを引き出すのに十分なCSSがわからない、またはこのアプローチが好きかどうかを決定することさえできません。さらに、少なくともレンダリングの順序では、最終出力に似たマークアップを好みます。

さらに、特定のマークアップに対して複数のスタイルシートを設定することは妥当な場合があるため、CSSでこれを実行すると、複数のスタイルに対して複数回実行する必要があり、CSSが間違った答えになることがあります。

[編集]

以下の自分の質問に答えているようです。誰かがこれを行う方法についてより良いアイデアを持っているなら、恥ずかしがらないでください。

34
Ori Pessach

これが私がやったことです。タグを切り替えるためのカスタムテンプレート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>タグのタイプが「チェックボックス」の場合はそれらを切り替えます。また、ラベルの最後の文字(たまたま ':'文字)を取り除きます。

利点:

  1. CSSをごちゃごちゃにする必要はありません。
  2. マークアップは、本来あるべき姿になってしまいます。
  3. 私はDjango内部をハックしませんでした。
  4. テンプレートはナイスでコンパクト、慣用的です。

短所:

  1. フィルターコードは、ラベルと入力フィールド名のエキサイティングな値をテストする必要があります。
  2. おそらく、どこかでそれをより速くより速くする何かがあるでしょう。
  3. 土曜日に予定していた以上の仕事。
2
Ori Pessach

ここに私が思いついた解決策があります(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. 私はDjango内部をハックしませんでした(しかし、かなりの束を見る必要がありました))
  4. テンプレートはナイスでコンパクト、慣用的です。
  5. フィルターコードは、ラベルと入力フィールド名の正確な値に関係なく、Niceを再生します。

短所:

  1. おそらく、どこかでそれをより速くより速くする何かがあるでしょう。
  2. ラベルを右に移動するためだけに、クライアントがこれに費やしたすべての時間を喜んで支払うことはほとんどありません...
32
Roman Starkov

私はロムキンスから答えを受け取り、もう少し一般的にしました

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 %}
15
Marco

提示されたすべてのソリューションには、テンプレートの変更が含まれますが、これは一般にパフォーマンスに関してかなり非効率的です。これがその仕事をするカスタムウィジェットです:

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())

追加ファイルにPrettyCheckboxWidgetPrettyCheckboxFieldがあるので、必要に応じてインポートされます。翻訳が必要ない場合は、ugettextの部分を削除できます。このコードはDjango 1.5で動作し、下位バージョンではテストされていません。

利点:

  • 高いパフォーマンス、テンプレートの変更は不要
  • カスタムウィジェットとして使いやすい

欠点:

  • 「as_table」は、2番目の列内のチェックボックス+ラベルをレンダリングします
  • テンプレート内の{{field.label}}が空です。代わりに、ラベルは{{フィールド}}にバインドされます
  • 土曜日に予定していた以上の仕事;-)
11

ユーザーがCSSを除外したことは知っていますが、上位の回答を考えると、このような小さなことを行うのに約30分かかりますが、Webサイトではこれらの詳細が重要であることを知っているので、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はプレゼンテーションを反映していません(ただし十分です)。
  • あなたのフロントエンドは文句を言うかもしれません
3
caesarsol

Django adminでチェックボックスの位置を変更するのはかなり難しいかもしれませんが、カスタムウィジェットを使用した簡単な解決策があります:

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のサブクラスである場合にのみ、左位置を使用します

2
Andrei

入力とラベルの順序はフォームのnormal_rowパラメータを介して提供され、チェックボックスに異なる行パターンはありません。したがって、これを行うには2つの方法があります(0.96バージョンでは正確に)。
1。フォームの_html_outputを上書きする
2。 CSSを使用してラベルとチェックボックスの位置を変更する

1
zihotki