2012-04-24 12 views
12

Ho un ModelForm che contiene un ModelChoiceField che utilizza il widget RadioSelect.Come ottenere istanze ModelChoiceField nel modello

class MyAForm(forms.ModelForm): 
    one_property = models.ModelChoiceField(
     widget=forms.RadioSelect, 
     queryset=MyBModel.objects.filter(visible=True), 
     empty_label=None) 
    class Meta: 
     model = MyAModel 

Ci sono attributi su MyBModel che desidero visualizzare accanto al pulsante di opzione. Sostituirei label_from_instance in una sottoclasse di ModelChoiceField ma questo non mi consente di fare ciò che voglio in quanto voglio che il pulsante di scelta venga visualizzato all'interno di una tabella che ha una riga per ogni elemento di selezione.

Quindi, da qualche parte nel mio modello voglio qualcosa di simile ...

{% for field in form.visible_fields %} 
    {% if field.name == "one_property" %} 
    <table> 
     {% for choice in field.choices %} 
      <tr> 
       <td><input value="{{choice.id}}" type="radio" name="one_property" />{{choice.description}}</td> 
       <td><img src="{{choice.img_url}}" /></td> 
      </tr> 
     {% endfor %} 
    </table> 
    {% endif %} 
{% endfor %} 

field.choices Purtroppo restituisce una tupla di id dell'oggetto e l'etichetta e non un'istanza dal set di query.

C'è un modo semplice per ottenere istanze delle scelte per un ModelChoiceField da utilizzare all'interno di un modello?

risposta

10

Dopo aver esplorato il sorgente django per ModelChoiceField ho scoperto che ha una proprietà "queryset".

ero in grado di usare qualcosa di simile ...

{% for field in form.visible_fields %} 
    {% if field.name == "one_property" %} 
    <table> 
     {% for choice in field.queryset %} 
      <tr> 
       <td><input value="{{choice.id}}" type="radio" name="one_property" />{{choice.description}}</td> 
       <td><img src="{{choice.img_url}}" /></td> 
      </tr> 
     {% endfor %} 
    </table> 
    {% endif %} 
{% endfor %} 
+0

Ciao, Se stampo questo ** {{field.name}} ** allora stampa tutto il nome dei campi ma se l'ho usato in condizione '{% if field.name =" attributes "%}' o '{% if field.name = attributes%}' e prova a stampare qualcosa sul successo di questa condizione, quindi niente stampa significa che la condizione restituisce false. Ma il nome del campo che ho usato in questa condizione è uno che stampa .. ** PERCHÉ COSÌ ?? ** – Inforian

+1

Hai un singolo = dovrebbe essere == –

2

Di solito non occorre l'oggetto reale, ma la sua resa.

Considerate questo codice:

class LabelledHiddenWidget(forms.HiddenInput): 

    def __init__(self, get_object, *args, **kwargs): 
     super(LabelledHiddenWidget, self).__init__(*args, **kwargs) 
     self.get_object = get_object 

    def render(self, name, value, attrs=None): 
     s = super(LabelledHiddenWidget, self).render(name, value, attrs) 
     if value: 
      s += SafeUnicode("<span>%s</span>" % self.get_object(value)) 
     return s 

quindi è possibile utilizzare in questo modo:

class SomeForm(forms.Form): 
    object = forms.ModelChoiceField(
     SomeModel.objects.all(), 
     widget=LabelledHiddenWidget(get_object=lambda id: get_object_or_404(SomeModel, id=id))) 

Poi nel codice del modello, {{ form.object }} uscita volontà un campo nascosto con l'oggetto id, concatenato con un po ' etichetta. Ovviamente il tuo SomeModel dovrebbe implementare __unicode__ o qualche altro metodo che restituisca una bella etichetta leggibile dall'uomo.

4

Volevo fare qualcosa di quasi identico alla domanda di OP (tavolo e tutto), ero ugualmente frustrato dalla mancanza di collaborazione di Django, e allo stesso modo ho finito per scavare nella fonte per arrivare alla mia implementazione. Quello che mi è venuto in mente è un po 'diverso dalla risposta accettata, e mi è piaciuto di più perché stavo usando un semplice {{ form.as_table }} nel mio modello e non volevo fare un ciclo attraverso visible_fields inutilmente o codice hard-button un pulsante radio nel mio modello che assomiglia semplicemente all'attuale implementazione di Django (che potrebbe cambiare). Ecco quello che ho fatto, invece:

RadioInput e RadioFieldRenderer

widget di Django RadioSelect utilizza RadioFieldRenderer per produrre un generator di RadioInputs, che fanno il lavoro effettivo di rendervi i pulsanti di opzione. RadioSelect sembra avere una funzione non documentata in cui è possibile passare a un renderer diverso da quello predefinito, in modo da poter creare una sottoclasse di entrambi per ottenere ciò che l'OP vuole.

from django import forms 
from django.utils.safestring import mark_safe 

class CustomTableRadioInput(forms.widgets.RadioInput): 

    # We can override the render method to display our table rows 
    def render(self, *args, **kwargs): 
     # default_html will hold the normally rendered radio button 
     # which we can then use somewhere in our table 
     default_html = super(CustomTableRadioInput, self).render(*args, **kwargs) 
     # Do whatever you want to the input, then return it, remembering to use 
     # either django.utils.safestring.mark_safe or django.utils.html.format_html 
     # ... 
     return mark_safe(new_html) 

class CustomTableFieldRenderer(forms.widget.RadioFieldRenderer): 
    # Here we just need to override the two methods that yield RadioInputs 
    # and make them yield our custom subclass instead 
    def __iter__(self): 
     for i, choice in enumerate(self.choices): 
      yield CustomTableRadioInput(self.name, self.value, 
              self.attrs.copy(), choice, i) 

    def __getitem__(self, idx): 
     choice = self.choices[idx] # Let the IndexError propogate 
     return CustomTableRadioInput(self.name, self.value, 
             self.attrs.copy(), choice, idx) 

Fatto questo, abbiamo solo bisogno di dire al widget di RadioSelect di utilizzare il nostro renderer personalizzato ogni volta che chiamiamo da qualche parte nel nostro codice del modulo:

... 
radio = forms.ChoiceField(widget=forms.RadioSelect(renderer=CustomTableFieldRenderer), 
          choices=...) 
... 

E questo è tutto!

si noti che per utilizzare questo nel modello, probabilmente si vorrà al ciclo sul campo piuttosto che chiamare direttamente, vale a dire questo:

<table> 
    <tbody> 
    {% for tr in form.radio %} 
    <tr>{{ tr }}</tr> 
    {% endfor %} 
    </tbody> 
</table> 

piuttosto che questo:

<table> 
    <tbody>{{ form.radio }}</tbody> 
</table> 

Se lo fai, cercherà di avvolgere gli elementi della tabella in <ul><li>...</li></ul>.

Problemi correlati