8

django-adminのフィールドにカスタムの「セレクター」が必要な場合は、カスタムウィジェットを作成する必要があることを知っています。しかし、ウィジェットが2つの値、たとえばX座標とY座標を生成する必要がある場合、モデルの2つの異なるフィールドにそれらを入力するにはどうすればよいですか?

4

4 に答える 4

7

管理者で2つのフィールドとして表示される日時フィールドの実装を確認できます。

トップダウンで、

管理者が使用する

class AdminSplitDateTime(forms.SplitDateTimeWidget):
    """
    A SplitDateTime Widget that has some admin-specific styling.
    """
    def __init__(self, attrs=None):
        widgets = [AdminDateWidget, AdminTimeWidget]
        # Note that we're calling MultiWidget, not SplitDateTimeWidget, because
        # we want to define widgets.
        forms.MultiWidget.__init__(self, widgets, attrs)

    def format_output(self, rendered_widgets):
        return mark_safe(u'<p class="datetime">%s %s<br />%s %s</p>' % \
            (_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1]))

順番に使用しますSplitDateTimeWidget

class SplitDateTimeWidget(MultiWidget):
    """
    A Widget that splits datetime input into two <input type="text"> boxes.
    """
    date_format = DateInput.format
    time_format = TimeInput.format

    def __init__(self, attrs=None, date_format=None, time_format=None):
        if date_format:
            self.date_format = date_format
        if time_format:
            self.time_format = time_format
        widgets = (DateInput(attrs=attrs, format=self.date_format),
                   TimeInput(attrs=attrs, format=self.time_format))
        super(SplitDateTimeWidget, self).__init__(widgets, attrs)

    def decompress(self, value):
        if value:
            return [value.date(), value.time().replace(microsecond=0)]
        return [None, None]

これは、次に、拡張する必要があるMultiWidget定義をdjango.forms.widgets拡張します。それはあなたがオーバーライドできる多くの便利なメソッドを持っています。

class MultiWidget(Widget):
"""
A widget that is composed of multiple widgets.

Its render() method is different than other widgets', because it has to
figure out how to split a single value for display in multiple widgets.
The ``value`` argument can be one of two things:

    * A list.
    * A normal value (e.g., a string) that has been "compressed" from
      a list of values.

In the second case -- i.e., if the value is NOT a list -- render() will
first "decompress" the value into a list before rendering it. It does so by
calling the decompress() method, which MultiWidget subclasses must
implement. This method takes a single "compressed" value and returns a
list.

When render() does its HTML rendering, each value in the list is rendered
with the corresponding widget -- the first value is rendered in the first
widget, the second value is rendered in the second widget, etc.

Subclasses may implement format_output(), which takes the list of rendered
widgets and returns a string of HTML that formats them any way you'd like.

You'll probably want to use this class with MultiValueField.
"""
def __init__(self, widgets, attrs=None):
    self.widgets = [isinstance(w, type) and w() or w for w in widgets]
    super(MultiWidget, self).__init__(attrs)

def render(self, name, value, attrs=None):
    # value is a list of values, each corresponding to a widget
    # in self.widgets.
    if not isinstance(value, list):
        value = self.decompress(value)
    output = []
    final_attrs = self.build_attrs(attrs)
    id_ = final_attrs.get('id', None)
    for i, widget in enumerate(self.widgets):
        try:
            widget_value = value[i]
        except IndexError:
            widget_value = None
        if id_:
            final_attrs = dict(final_attrs, id='%s_%s' % (id_, i))
        output.append(widget.render(name + '_%s' % i, widget_value, final_attrs))
    return mark_safe(self.format_output(output))

def id_for_label(self, id_):
    # See the comment for RadioSelect.id_for_label()
    if id_:
        id_ += '_0'
    return id_
id_for_label = classmethod(id_for_label)

def value_from_datadict(self, data, files, name):
    return [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)]

def _has_changed(self, initial, data):
    if initial is None:
        initial = [u'' for x in range(0, len(data))]
    else:
        if not isinstance(initial, list):
            initial = self.decompress(initial)
    for widget, initial, data in zip(self.widgets, initial, data):
        if widget._has_changed(initial, data):
            return True
    return False

def format_output(self, rendered_widgets):
    """
    Given a list of rendered widgets (as strings), returns a Unicode string
    representing the HTML for the whole lot.

    This hook allows you to format the HTML design of the widgets, if
    needed.
    """
    return u''.join(rendered_widgets)

def decompress(self, value):
    """
    Returns a list of decompressed values for the given compressed value.
    The given value can be assumed to be valid, but not necessarily
    non-empty.
    """
    raise NotImplementedError('Subclasses must implement this method.')

def _get_media(self):
    "Media for a multiwidget is the combination of all media of the subwidgets"
    media = Media()
    for w in self.widgets:
        media = media + w.media
    return media
media = property(_get_media)

def __deepcopy__(self, memo):
    obj = super(MultiWidget, self).__deepcopy__(memo)
    obj.widgets = copy.deepcopy(self.widgets)
    return obj
于 2010-08-11T14:54:13.590 に答える
3

Jannis Leidel はかなり前にウィジェットをリリースしました。django-coordinatesfield 私が覚えている限りでは、マップから座標を取得して単一のフィールドに渡し、JavaScript によって 2 つのフィールドの 2 つの座標にカットされました。

カスタムフォームと組み合わせると、非常にうまく機能するはずです

于 2010-08-09T17:29:49.873 に答える
1

ModelForm の例を次に示します: http://www.adamalton.co.uk/blog/displaying-django-genericforeignkey-as-single-form-field/

追加のフォーム フィールドをフォームに (単一のウィジェット用に) 追加し、2 つの「実際の」フィールドを除外してから、init メソッドと save メソッドをオーバーライドして、それを機能させる追加のロジックを実行します。

また、同じ質問: Django で 2 つのフィールドを設定する単一のウィジェットを取得するにはどうすればよいですか?

于 2011-04-21T15:26:21.033 に答える
0

ウィジェットに 2 つの (非表示の) html 入力をレンダリングさせることができます。これらの名前は、入力する必要があるモデルのフィールドに関連しており、javascript を介して必要な値をそれらに割り当てます!

于 2010-08-13T17:08:36.947 に答える