0

私はすでに拡張クラスを実装しており、このクラスの検証システムを実装する必要がありますが、カスタムメソッドを適用し、 LSP原則に違反している可能性があるため、クラスWidgetと互換性がないと思います(わからない)。次に例を示します。Fieldrender()

from django import forms
from django.utils.safestring import mark_safe
from django.utils.encoding import force_unicode
from django.utils import formats
from django_future import format_html, flatatt

class InputGeneric(forms.Widget):
"""
Base class for all <input> widgets
"""
input_type = None # Subclasses must define this.
_to_str = None

def __init__(self, attrs=None, single_attrs=None):
    super(InputGeneric, self).__init__(attrs)
    self.single_attrs = single_attrs or ''

def get_attrs(self):
    return self.attrs

def get_attr(self, key):
    return self.attrs.get(key, None)

def render(self, name=None, value=None, attrs=None, single_attrs=None):
    '''
    *The default arguments of this function are:
    (self, name, value, attrs=None)
    - - - -
    single_attrs: is a string of HTML5 single attributes like "required", disabled"
    Example:
    render(single_attrs='required disabled')
    '''
    name = name or self.attrs.get('name', None)

    if name:
        final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
    else:
        final_attrs = self.build_attrs(attrs, type=self.input_type)

    value = self.attrs.get('value', None)
    if value:
        # Only add the 'value' attribute if a value is non-empty.
        final_attrs['value'] = force_unicode(self._format_value(value))

    self._to_str = format_html('<input{0} {1} />', flatatt(final_attrs), single_attrs)
    return self._to_str

def get_rendered(self):
    return self.render(attrs=self.attrs, single_attrs=self.single_attrs)

def __str__(self):
    if self._to_str:
        return self._to_str
    self._to_str = self.render()
    return self._to_str

class InputText(InputGeneric):
input_type = 'text'

def __init__(self, attrs=None, single_attrs=None):
    if attrs.get('type', None) is not None:
        del attrs['type']
    super(InputText, self).__init__(attrs, single_attrs)

django_future.py:

注:six Django1.3 のライブラリは、 https ://github.com/django/django/blob/1.5/django/utils/six.pyから入手できます。

'''
This Lib contains functions from future implemantations of Django (After v1.3).
'''
from django.utils.safestring import mark_safe
from django.utils.html import conditional_escape
from django.utils import encoding
import datetime
from decimal import Decimal

    #The six lib is not included in Django 1.3
    #If you have 1.3 (as i have) you can search here in a future version of Django:
    #django.utils -> six
import six

def flatatt(attrs):
    """
    Convert a dictionary of attributes to a single string.
    The returned string will contain a leading space followed by key="value",
    XML-style pairs. It is assumed that the keys do not need to be XML-escaped.
    If the passed dictionary is empty, then return an empty string.

    The result is passed through 'mark_safe'.
    """
    return format_html_join('', ' {0}="{1}"', sorted(attrs.items()))

def format_html(format_string, *args, **kwargs):
    #django.utils.html
    """
    Similar to str.format, but passes all arguments through conditional_escape,
    and calls 'mark_safe' on the result. This function should be used instead
    of str.format or % interpolation to build up small HTML fragments.
    """
    args_safe = map(conditional_escape, args)
    kwargs_safe = dict([(k, conditional_escape(v)) for (k, v) in
                        six.iteritems(kwargs)])
    return mark_safe(format_string.format(*args_safe, **kwargs_safe))


def format_html_join(sep, format_string, args_generator):
    #django.utils.html
    """
    A wrapper of format_html, for the common case of a group of arguments that
    need to be formatted using the same format string, and then joined using
    'sep'. 'sep' is also passed through conditional_escape.

    'args_generator' should be an iterator that returns the sequence of 'args'
    that will be passed to format_html.

    Example:

    format_html_join('\n', "<li>{0} {1}</li>", ((u.first_name, u.last_name)
    for u in users))

    """
    return mark_safe(conditional_escape(sep).join(
            format_html(format_string, *tuple(args))
            for args in args_generator))


def is_protected_type(obj):
    return isinstance(obj, six.integer_types + (type(None), float, Decimal,
        datetime.datetime, datetime.date, datetime.time))

def force_text(s, encoding='utf-8', strings_only=False, errors='strict'):
    if isinstance(s, six.text_type):
        return s
    if strings_only and is_protected_type(s):
        return s
    try:
        if not isinstance(s, six.string_types):
            if hasattr(s, '__unicode__'):
                s = s.__unicode__()
            else:
                if six.PY3:
                    if isinstance(s, bytes):
                        s = six.text_type(s, encoding, errors)
                    else:
                        s = six.text_type(s)
                else:
                    s = six.text_type(bytes(s), encoding, errors)
        else:
            s = s.decode(encoding, errors)
    except UnicodeDecodeError as e:
        if not isinstance(s, Exception):
            raise encoding.DjangoUnicodeDecodeError(s, *e.args)
        else:
            s = ' '.join([force_text(arg, encoding, strings_only,
                    errors) for arg in s])
    return s

Fieldそこで、クラス(またはFormクラス)がから生の値(html)をどのように正確に取得Widgetし、検証フィルターをどのように適用して結果を返すかを尋ねたいと思います。プロセスを理解するために、説明付きの小さな例を提供してください。

* [Djangoコード][3]はすでに見たことがありますが、残念ながらプロセスを完全に理解することはできません

よろしくお願いします。

4

1 に答える 1

6

まず第一に、私のアプローチは正しくないと言わざるを得ません。確かにLSP原則に違反しているため、の引数nameを削除valueするのは間違った決定でした。また、入力フィールドの最も基本的なものは、POSTリクエストにも含まれています。render()namevalue

その後、forms.py内でのレンダリングのプロセスを発見しました。ウィジェットは、呼び出されたときに関数をBoundField()自動的に呼び出すクラス内にレンダリングされます。as_widget()したがって、 forms.pyはレンダリングのすべてのプロセスを実行します。

次に、サンプルアプリケーションを使用してプロセスを示します。

アプリの構造


1)まず、フォームクラスがあります。

my_form.py:

from django import forms

class ContactForm(forms.Form):
    subject = forms.CharField(min_length = 2, max_length = 100, widget = forms.TextInput(attrs = {'placeholder':'Type a subject...'}))
    email = forms.EmailField(required=True, widget = forms.TextInput(attrs={'class': 'span3', 'placeholder': 'Your email...'}))
    message = forms.CharField(widget=forms.Textarea(attrs={'rows': '5', 'placeholder': 'Type a message...'}))

    def clean_message(self):
        message = self.cleaned_data['message']
        num_words = len(message.split())
        if num_words < 4:
            raise forms.ValidationError("Not enough words!")
            return message

2)2番目の私たちの見解:

views.py:

def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
            try:
                send_mail(
                    cd['subject'],
                    cd['message'],
                    cd.get('email', 'noreply@anonymous.com'),
                    ['myemail@ahost.com'],
                )
            except BadHeaderError:
                return HttpResponse('Invalid header found.')

            return HttpResponseRedirect('thank-you/')
    else:
        form = ContactForm()

    return render_to_response('/contact.html', {'form': form})

3)テンプレート:

contact.html

{% if form.errors %}
    <p style="color: red;">
        Please correct the error{{ form.errors|pluralize }} below.
    </p>
{% endif %}

<form action="" method="post">
    <fieldset>
        <legend>Contact</legend>
        <div class="field">
            {{ form.subject.errors }}
        </div>
        {{ form.as_table }}
        <br />
        <button type="submit" class="btn">Submit</button>
    </fieldset>
</form>

処理手順


1)ユーザーは、フォームを呼び出してテンプレートとともに提供するviews.py 関数につながるリンクをクリックします。contact()

2)が呼び出されると、テンプレート内{{ form.as_table }}でアプリがforms.py内に入り、クラスを見つけBaseForm()てから、この関数(コピー貼り付け)を見つけます。

def as_table(self):
    "Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
    return self._html_output(
        normal_row = u'<tr%(html_class_attr)s><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>',
        error_row = u'<tr><td colspan="2">%s</td></tr>',
        row_ender = u'</td></tr>',
        help_text_html = u'<br /><span class="helptext">%s</span>',
        errors_on_separate_row = False)

3)上記の関数は、と呼ばれる別の関数を呼び出し、もちろんself._html_output(...)内部に存在します。BaseForm()この関数は、他の小さな関数またはクラスを呼び出すことによってレンダリングされた最終結果を出力する役割を果たします。

いくつかのメモ


  • views.pyform = ContactForm()内で実行されると、クラスと呼ばれることがわかります。ただし、このクラスは、 forms.pyに存在するサブクラスであり、クラスの子でもあります。つまり、はからすべてを継承します。ContactForm()FormForm()BaseForm()ContactForm()BaseForm()
  • Form()クラスは空です。からのみ継承しBaseForm()、継承の目的で使用されます。すべてのフォームの真の親はBaseForm()クラスです。
  • 初期化時に、ContactForm()と彼の両親は、子のすべてのフィールドを含むなど、発生するいくつかの非常に重要な変数と呼ばれForm()ます。この例では: 、、、。その他の重要な変数は次のとおりです。 BaseForm()self.fieldssubjectemailmessage
    • self.dataPOSTデータが含まれています
    • self._errors:clean()が呼び出された後のエラーを保存します。

の中に_html_output()


この関数は、次のように繰り返しself.fields(この場合はsubject、、 )を繰り返しemail、それぞれmessageのクラスを呼び出します。BoundField()

bf = BoundField(frm_clss, field, name)

最初の引数frm_clssBaseForm()クラスです。2番目はフィールドリストの現在のフィールド(例subject)で、3番目は内部に指定されたフィールドの実際の名前ですContactForm()。したがって、3つの名前は「subject」、「email」、「message」です。

最後に、この関数は、レンダリングされたすべてのウィジェットの「コンパイル済み」でマークされた安全な文字列を返します。

output.append(normal_row % {
    'errors': force_unicode(bf_errors),
    'label': force_unicode(label),
    'field': unicode(bf),
    'help_text': help_text,
    'html_class_attr': html_class_attr
    })

    .
    .
    .

return mark_safe(u'\n'.join(output))

BoundField()クラス


デフォルトではrender()、ウィジェットのメソッドを呼び出し、html文字列をBaseForm()クラス(_html_output())に返します。

于 2013-03-18T18:07:14.123 に答える