2016-07-22 7 views
0

私はマルチステートのクリックボックスが必要でした。だから私はそれを作る素敵なDjangoのソリューションにいくつかの自由な時間を過ごす:Djangoカスタムウィジェットが上記の選択に失敗しました1(整数を入力してください)

class MultiStateChoiceInput(forms.widgets.ChoiceInput): 
    input_type = 'radio' 
    def __init__(self, name, value, attrs, choice, index, label_id): 
     # Override to use the label_id which is upped with 1 
     if 'id' in attrs: 
      self.label_id = attrs['id']+ "_%d" % label_id 

     super(MultiStateChoiceInput, self).__init__(name, value, attrs, choice, index) 

     self.value = force_text(self.value) 


    @property 
    def id_for_label(self): 
     return self.label_id 

    def render(self, name=None, value=None, attrs=None, choices=()): 
     if self.id_for_label:   
      label_for = format_html(' for="{}"', self.id_for_label) 
     else: 
      label_for = '' 
     attrs = dict(self.attrs, **attrs) if attrs else self.attrs 
     return format_html(
      '{} <label{}>{}</label>', self.tag(attrs), label_for, self.choice_label 
     ) 


class MultiStateRenderer(forms.widgets.ChoiceFieldRenderer): 
    choice_input_class = MultiStateChoiceInput 

    outer_html = '<span class="cyclestate">{content}</span>' 
    inner_html = '{choice_value}{sub_widgets}' 

    def render(self): 
     """ 
     Outputs a <ul> for this set of choice fields. 
     If an id was given to the field, it is applied to the <ul> (each 
     item in the list will get an id of `$id_$i`). 

     # upgraded with the label_id 
     """ 
     id_ = self.attrs.get('id') 
     output = [] 
     for i, choice in enumerate(self.choices): 
      choice_value, choice_label = choice 
      if isinstance(choice_label, (tuple, list)): 
       attrs_plus = self.attrs.copy() 
       if id_: 
        attrs_plus['id'] += '_{}'.format(i) 
       sub_ul_renderer = self.__class__(
        name=self.name, 
        value=self.value, 
        attrs=attrs_plus, 
        choices=choice_label, 
        label_id = (i+1) % (len(self.choices)) # label_id is next one 
       ) 
       sub_ul_renderer.choice_input_class = self.choice_input_class 
       output.append(format_html(self.inner_html, choice_value=choice_value, 
              sub_widgets=sub_ul_renderer.render())) 
      else: 
       w = self.choice_input_class(self.name, self.value, 
              self.attrs.copy(), choice, i, label_id = (i+1) % (len(self.choices))) # label_id is next one 
       output.append(format_html(self.inner_html, 
              choice_value=force_text(w), sub_widgets='')) 
     return format_html(self.outer_html, 
          id_attr=format_html(' id="{}"', id_) if id_ else '', 
          content=mark_safe('\n'.join(output))) 


class MultiStateSelectWidget(forms.widgets.RendererMixin, forms.widgets.Select): 
    ''' This widget enables multistate clickable toggles 
    Requires some css as well (see .cyclestate) 
    ''' 
    renderer = MultiStateRenderer 

https://stackoverflow.com/a/33455783/3849359ここで説明すると、クリックがそれに到達するまで、次の状態をトグルし、その後、先頭に続き、そこでれるようにこれは、フォームを作成します。

フォームが似私の見解で呼び出されます。

SomeFormSet= modelformset_factory(myModel, form=myModelForm, extra=0)  
SomeFormSet.form = staticmethod(curry(myModelForm, somevariable=somevariable)) 

formset = SomeFormSet(request.POST or None, queryset=somequeryset) 

そしてforms.pyは次のとおりです。

class myModelForm(forms.ModelForm): 
    CHOICES = (
     (0, _('a')), 
     (1, _('b')), 
     (2, _('c')), 
     (3, _('d')), 
     ) 


    field = forms.IntegerField(widget=MultiStateSelectWidget(choices=CHOICES)) 


    class Meta: 
     model = MyModal 
     fields = ('field',) 
     widgets = {'id': forms.HiddenInput(), 
       } 

    def __init__(self, *args, **kwargs): 
     self.variable= kwargs.pop('variable') 
     super(myModelForm, self).__init__(*args, **kwargs) 

     for field in myModelForm.fields: 

      if self.instance.pk: 

       if not getattr(self.instance, field): 
        self.initial[field]= 0 
       else: 
        self.initial[field]= 1 

       if anothercondition: 
        self.initial[field] = 3 
       else: 
        self.initial[field] = 2 

私はそれが非常によく働いたと思いました。クリックと保存がうまくいく(私はカスタム保存メソッドがある)。フォームフィールドの値が2または3の場合を除いて、突然エラーメッセージで失敗します。「フィールド」は整数でなければなりません。

私がアイデアを忘れていると誰かが助けてくれると助かりました!

EDIT:ちょっとでも...私はPOSTをチェックして、それは素晴らしいです。唯一の問題は、値が2であればDjangoがPOSTを解析するどこかで値を完全に失うことです(値は2になりますが、理由がわかりません)。

EDIT2:Django ModelFormはモデル検証もしているようです。モデルはBooleanFieldです。これが失敗する理由です。誰かがそれを無効にする良い方法を知っているなら、それはいいでしょう!

+1

整数を保存するには、モデルフィールドをIntegerFieldに変更します。ブール値フィールドに整数を保存することはできません。私はこれが問題だと思います。 – edgarzamora

答えて

0

@edgarzamoraあなたのコメントは答えではありませんが、近いです!

私は、FormクラスのMetaから「フィールド」を取り出し、それはのように見えたので:

class Meta: 
    model = MyModal 
    fields = ('',) 
    widgets = {'id': forms.HiddenInput(), 
      } 

そして、私は保存方法私の習慣を持っているので、今ではすべてが...、だから、愚かな作品、それは私の時間をコスト計算!ありがとう!

+0

パーフェクト!良いようです!また、フィールドタプルで 'field'を使用して、新しい1つのフィールドを宣言する代わりにinit関数でフィールドをオーバーライドすることもできます。しかし、私の質問は、もしあなたがあなたのモデルフィールドがブールフィールドであると言ったら、どのように0と3の間のintegeresを保存することができますか?この部分は私を混乱させています.. – edgarzamora

+0

私のセーブ方法では、整数(0から3)をブール値(0,1)に変換し、2または3のときにいくつかの追加機能を実行します。整数を0または1に変換し、代わりに2または3であるべきかどうかをチェックするためにいくつかの関数を実行します。 – gabn88

+0

Okey!あなたはそれを説明するのを忘れた。次回は、モデルコードを共有する方が良いはずです。ありがとう! – edgarzamora

関連する問題