2012-04-24 7 views
12

Ich habe eine ModelForm, die ein ModelChoiceField mit dem RadioSelect-Widget enthält.So erhalten Sie ModelChoiceField-Instanzen in der Vorlage

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

Es gibt Attribute auf MyBModel, die ich neben dem Optionsfeld anzeigen möchte. Ich würde label_from_instance für eine Unterklasse von ModelChoiceField außer Kraft setzen, aber dies erlaubt mir nicht zu tun, was ich will, wie ich will, dass der Radio-Button in einer Tabelle erscheint, die eine Zeile für jedes Auswahlelement hat.

Also irgendwo in meiner Vorlage möchte ich so etwas wie ...

{% 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 %} 

Leider field.choices kehrt ein Tupel der ID des Objekts und das Etikett und keine Instanz vom queryset.

Gibt es eine einfache Möglichkeit, Instanzen der Auswahlmöglichkeiten für ein ModelChoiceField innerhalb einer Vorlage zu erhalten?

Antwort

10

Nach dem Eintauchen in die Django-Quelle für ModelChoiceField entdeckte ich, dass es eine Eigenschaft "queryset" hat.

ich in der Lage war so etwas wie ...

{% 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

Hi, wenn ich dieses ** {{field.name}} ** drucke, dann druckt es alle Namen der Felder, aber wenn ich es in der Bedingung '{% if field.name =" attributes "%}' oder '{% if field.name = attributes%}' und versuche, etwas auf den Erfolg dieser Bedingung zu drucken, dann druckt nichts, dh die Bedingung gibt false zurück. Aber der Feldname, den ich in diesem Zustand benutzte, ist ein Ausdruck, den er druckt. ** WARUM SO ?? ** – Inforian

+1

Sie haben eine einzige = es sollte == sein –

2

Normalerweise müssen Sie verwenden nicht das eigentliche Objekt, aber seine Überstellung.

Betrachten Sie diesen Code:

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 

Dann können Sie es wie folgt verwenden:

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

Dann in dem Template-Code, {{ form.object }} ausgeben wird ein verstecktes Feld mit der Objekt-ID, mit einigen verketteten Etikette. Natürlich sollte Ihr SomeModel __unicode__ oder eine andere Methode implementieren, die ein schönes, menschenlesbares Etikett zurückgibt.

4

Ich wollte etwas tun fast identisch mit OP-Frage (Tabelle und alle), war in ähnlicher Weise frustriert von Django mangelnde Zusammenarbeit, und endete ähnlich in die Quelle eintauchen, um mit meiner eigenen Umsetzung zu kommen. Was ich herausgefunden habe, ist ein bisschen anders als die angenommene Antwort, und ich mochte es besser, weil ich eine einfache {{ form.as_table }} in meiner Vorlage verwendete und nicht unnötig durch visible_fields durchschleifen oder einen Radioknopf in meinem Code fest codieren wollte Vorlage, die lediglich Djangos aktueller Implementierung ähnelt (was sich ändern könnte). Hier ist, was ich tat, statt:

RadioInput und RadioFieldRenderer

Djangos RadioSelect Widget verwendet RadioFieldRenderer eine generator von RadioInputs zu ergeben, was tun die eigentliche Arbeit der Radio-Buttons machen. RadioSelect scheint eine undokumentierte Funktion zu haben, durch die Sie einen anderen Renderer als diesen Standard übergeben können, so dass Sie beide unterteilen können, um das zu erhalten, was OP will.

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) 

Wenn das getan ist, wir müssen nur das RadioSelect Widget sagen, unsere eigene Renderer zu verwenden, wann immer wir es irgendwo in unserem Formular-Code aufrufen:

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

Und das ist es!

Sie beachten Sie, dass dies in der Vorlage zu verwenden, werden Sie wahrscheinlich über das Feld Schleife wollen, anstatt sie direkt aufrufen, das heißt das:

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

eher als das:

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

Wenn Sie das letztere tun, wird es versuchen, Ihre Tabellenelemente in <ul><li>...</li></ul> zu verpacken.

Verwandte Themen