2010-11-18 11 views
4

Ich möchte in der Lage sein, die Liste der Modelle auf der Seite anzuzeigen und dem Benutzer zu ermöglichen, einige von ihnen gleichzeitig auszuwählen.Formsets mit Kontrollkästchen

Zum Beispiel sagen, dass ich ein Benutzerauswahlbildschirm haben, für das Modell:

class User(model): 
    first = # char field 
    last = # char field 
    birthdate = # date 

Dann möchte ich die Benutzer zeigen und ihnen eine von ihnen wählen lassen:

Please select users: 
[] John Smith Jan, 2001 
[] Mike Davis Feb, 2002 
[] John Doe  Dec, 2000 

[Continue] 

Diese Form wird dann POSTed und verarbeitet.

Eine Möglichkeit, die ich mir vorstellen kann, ist mit ModelFormset. Das Problem ist, wenn ich versuche, die ModelFormsets für die Anzeige der Benutzer zu verwenden, kann ich das Kontrollkästchen nicht hinzufügen.

Eine andere Möglichkeit, die mir einfällt, ist ein Formular zu erstellen und darauf eine ganze Reihe von Checkboxen mit einer bestimmten ID auszugeben. Dann beim Senden - über alle ausgewählten Kontrollkästchen iterieren. Nicht sicher, wie das mit Djangos Formen funktionieren würde.

Alle Vorschläge sind willkommen. Vielen Dank.

BEARBEITEN: Nun, es stellt sich heraus, dass, indem Sie jedem Kontrollkästchen eine ID (zB Patienten-ID) innerhalb der gleichen Namen Gruppe und einfach Blick auf POST-Wörterbuch in der Django-Ansicht gibt mir genau das, was ich brauche!

+0

ich diese Frage beantwortet haben: http://stackoverflow.com/a/27545910/1005499 – seddonym

Antwort

1

Sie möchten kein Formularset, da dies zur Bearbeitung von Daten in einer Reihe von Modellinstanzen dient. Was Sie wollen, ist ein einzelnes Formular mit einem ModelChoiceField oder ModelMultipleChoiceField - Sie müssen möglicherweise das Widget ändern, um CheckboxSelectMultiple zu verwenden.

+0

Das Problem mit diesem ist - es zeigt nur ein Etikett pro Modell. Während ich möchte, dass die Seite eine Tabelle mit Spalten "Vorname, Nachname, Geburtsdatum" usw. zeigt, während MultipleModelChoideField nur eine "Bezeichnung" pro Modell hat. Es sei denn, ich verwechsle etwas? – drozzy

+1

@drozzy: Sie können überschreiben, was (Multiple) ModelChoiceField zeigt, indem Sie die Funktion 'label_from_instance()' function descs in den Dokumenten verwenden. Um diese Daten in die Tabelle zu übertragen, benötigen Sie jedoch eine benutzerdefinierte JS-Verarbeitung. –

+0

Ich bin nicht sehr begierig darauf, Teile von HTML in meinen Python Code zu schreiben. Nun, danke für den Vorschlag sowieso. – drozzy

3

Ich habe einen benutzerdefinierten select-Widget für die zusammen mit einem Kontrollkästchen zur Auswahl von Objektdetails Anzeige zurück, wenn Formen noch newforms genannt wurden - es scheint immer noch zu funktionieren:

from django.forms import CheckboxInput, SelectMultiple 
from django.utils.encoding import force_unicode 
from django.utils.html import escape 
from django.utils.safestring import mark_safe 

class TableSelectMultiple(SelectMultiple): 
    """ 
    Provides selection of items via checkboxes, with a table row 
    being rendered for each item, the first cell in which contains the 
    checkbox. 

    When providing choices for this field, give the item as the second 
    item in all choice tuples. For example, where you might have 
    previously used:: 

     field.choices = [(item.id, item.name) for item in item_list] 

    ...you should use:: 

     field.choices = [(item.id, item) for item in item_list] 
    """ 
    def __init__(self, item_attrs, *args, **kwargs): 
     """ 
     item_attrs 
      Defines the attributes of each item which will be displayed 
      as a column in each table row, in the order given. 

      Any callables in item_attrs will be called with the item to be 
      displayed as the sole parameter. 

      Any callable attribute names specified will be called and have 
      their return value used for display. 

      All attribute values will be escaped. 
     """ 
     super(TableSelectMultiple, self).__init__(*args, **kwargs) 
     self.item_attrs = item_attrs 

    def render(self, name, value, attrs=None, choices=()): 
     if value is None: value = [] 
     has_id = attrs and 'id' in attrs 
     final_attrs = self.build_attrs(attrs, name=name) 
     output = [] 
     str_values = set([force_unicode(v) for v in value]) # Normalize to strings. 
     for i, (option_value, item) in enumerate(self.choices): 
      # If an ID attribute was given, add a numeric index as a suffix, 
      # so that the checkboxes don't all have the same ID attribute. 
      if has_id: 
       final_attrs = dict(final_attrs, id='%s_%s' % (attrs['id'], i)) 
      cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values) 
      option_value = force_unicode(option_value) 
      rendered_cb = cb.render(name, option_value) 
      output.append(u'<tr><td>%s</td>' % rendered_cb) 
      for attr in self.item_attrs: 
       if callable(attr): 
        content = attr(item) 
       elif callable(getattr(item, attr)): 
        content = getattr(item, attr)() 
       else: 
        content = getattr(item, attr) 
       output.append(u'<td>%s</td>' % escape(content)) 
      output.append(u'</tr>') 
     return mark_safe(u'\n'.join(output)) 

Beispiel Form:

class JobSelectionForm(forms.Form): 
    jobs = forms.MultipleChoiceField(widget=TableSelectMultiple(
       item_attrs=('formatted_number', 'name', 'client', 'get_status_display'))) 

    def __init__(self, accessible_jobs, *args, **kwargs): 
     super(JobSelectionForm, self).__init__(*args, **kwargs) 
     self.fields['jobs'].choices = [(j.id, j) \ 
             for j in accessible_jobs] 

Mit dem obigen Formular übergeben Sie die Liste der Elemente, die als erstes Argument beim Instanziieren des Formularobjekts angezeigt werden sollen.

Vorlage:

{% if form.jobs.errors %}{{ form.jobs.errors }}{% endif %} 
<table> 
<thead> 
    <tr> 
    <th>&nbsp;</th> 
    <th>Number</th> 
    <th>Name</th> 
    <th>Client</th> 
    <th>Status</th> 
    </tr> 
</thead> 
<tbody> 
{{ form.jobs }} 
</tbody> 
</table> 
+0

Danke, das sieht nach Spaß aus. Aber als ich auf Daneils Antwort geantwortet habe, fühle ich mich wirklich unwohl dabei, HTML in meinen Python-Code zu mischen. – drozzy

+0

In diesem Fall sollte es * möglich sein, Tupel von '(rendered_input_field, related_object)' aus einem benutzerdefinierten Widget zu erhalten, aber es beinhaltet auch das Erstellen eines benutzerdefinierten 'BoundField' (das bekommt man, wenn man' {{form .fieldname}} '), um das Widget entsprechend zu verwenden, anstatt seine Rendermethode aufzurufen. –