0

私はこれを成し遂げるために本当に髪を引っ張ってきました。次のように、モデルの 1 つに 2 つの整数フィールドがあります。

#models.py

class TestModel(models.Model):
    """
    This is a test model.
    """

    max = models.IntegerField()
    min = models.IntegerField()

上記の 2 つの値は相互に関連しているため、カスタム ウィジェットを使用する必要がありました。ここがポイントです。これらのフィールドで 1 つのカスタム ウィジェット (MultiWidget) を使用する必要がありますが、値をデータベース内の別の列に保持したい [実際には解析したくなく、さらに検索機能は上記のフィールドは別々に保存したいので、別々に保存したい]。

カスタム ウィジェットは単一のモデル フィールドからの値のみを受け入れるため、上記のモデルの ModelForm で使用するカスタム フォーム フィールドを作成することにしました。

これが私のフォームの外観です。

class MinMaxField(forms.MultiValueField):

    def __init__(self, *args, **kwargs):

        all_fields = ( 
            forms.CharField(),
            forms.CharField(),
            )   

        super(MinMaxField, self).__init__(all_fields, *args, **kwargs)

    def compress(self, values):
        if values:
            return '|'.join(values)
        return ''

class TestModelForm(forms.ModelForm):

    minmax = MinMaxField(widget=widgets.MinMaxWidget(),
             required=False)

    class Meta:
        model = models.TestModel
        fields = ('min', 'max', 'minmax')
        widgets = {
            'min' : forms.HiddenInput(),
            'max' : forms.HiddenInput(),
        }

    def full_clean(self, *args, **kwargs):
        if 'minmax_0' in self.data:
            newdata = self.data.copy()
            newdata['min'] = self.data['minmax_0']
            newdata['max'] = self.data['minmax_1']
            self.data = newdata
        super(TestModelForm, self).full_clean(*args, **kwargs)

上記のフォームは、基本的にモデル フィールドを非表示にし、minmax次のようにカスタム ウィジェットを使用するフィールドのみを表示します。

class MinMaxWidget(widgets.MultiWidget):

    def __init__(self, attrs=None):
        mwidgets = (widgets.Select(attrs=attrs, choices=MIN),
                   widgets.Select(attrs=attrs, choices=MAX))
        super(MinMaxWidget, self).__init__(mwidgets, attrs)

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

            value = [int(values[0]), int(values[1])]
        except ValueError:
            raise ValueError('Value %s for %s did not validate. \
                              a list or a tuple expected' % (value, self.widget))
        return value

    def decompress(self, value):
        if value:
            if isinstance(value, list):
                return value
            return [value[0], value[1]]
        return [None, None]

    def format_output(self, rendered_widgets):
        rendered_widgets.insert(0, '<span class="multi_selects">')
        rendered_widgets.insert(-1, '<label id="min">Min</label>')
        rendered_widgets.append('<label id="max">Max</label></span>')
        return u''.join(rendered_widgets)

この時点で、すべてが正常に機能し、値はそれぞれのフィールドに個別に保存されます。しかし、オブジェクトのインスタンスでフォームを編集しようとすると問題が発生しTestModelます。編集の場合、フォームがレンダリングされると、実際の値は隠しフィールドminmax入力フィールドに格納され、最小最大フィールドには値がありません。[もちろんフォームフィールドです。]

私がしなければならないオプションは何ですか

  1. およびフィールドminmaxからフォーム フィールドの値を事前入力します。[注: Javascript や jQuery を使用しない場合]minmax

  2. または、2 つの異なるモデル フィールドの値を使用する MultiWidget を作成します。

どんな助けでも大歓迎です。ありがとう。

4

1 に答える 1

0

私は現在、同様の問題に直面しており、2 つのフィールドにウィジェットを使用する方法があります。私はバージョン 1.4 を使用していますが、他のバージョンでは試していません。

あなたの見解では、おそらく次のようなものがあります。

form = TestModelForm(instance=instance)

編集するデータベースのオブジェクトはどこにinstanceありますか。フォームを作成するときに Django が何をしているのか見てみましょう

# django/forms/models.py

class BaseModelForm(BaseForm):
     def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
                  initial=None, error_class=ErrorList, label_suffix=':',
                  empty_permitted=False, instance=None):
          ...
          if instance is None:
              ...
          else:
              self.instance = instance
              object_data = model_to_dict(instance, opts.fields, opts.exclude)
          # if initial was provided, it should override the values from instance
          if initial is not None:
              object_data.update(initial)
          ...
          super(BaseModelForm, self).__init__(data, files, auto_id, prefix, 
                                              object_data, error_class, 
                                              label_suffix, empty_permitted)

次に、フォームがレンダリングされると、BoundFieldクラスはこれobject_dataを使用して初期値をウィジェットに割り当てます。

したがって、必要なことはinitial、フォーム コンストラクターで提供することだけです。

class TestModelForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        if 'instance' in kwargs:
            initial = dictionary with initial values
            initial.update(kwargs['initial'] or {})
        ...
        super(TestModelForm, self).__init__(initial=initial, *args, **kwargs)

また、フォームfieldsリストにフィールドを追加することを忘れないでください。

そうすれば、未使用の非表示のウィジェットが 2 つなくなるため、javascript と imo を使用する必要がなくなり、コードがわかりやすくなります。

于 2013-07-23T15:47:52.597 に答える