2010-11-18 15 views
4

Je veux être en mesure d'afficher la liste des modèles sur la page, et permettre à l'utilisateur de sélectionner quelques-uns d'entre eux en même temps.Formsets avec cases à cocher

Par exemple, dire que j'ai un écran de sélection de l'utilisateur, pour le modèle:

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

Ensuite, je veux montrer les utilisateurs et les laisser choisir l'un d'eux:

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

[Continue] 

Cette forme sera ensuite POSTed et traité.

Une façon dont je peux penser à le faire est avec ModelFormset. Le problème est lorsque j'essaie d'utiliser les ModelFormsets pour afficher les utilisateurs, je ne peux pas ajouter la case à cocher.

Une autre façon que je peux penser est de créer un formulaire, et afficher tout un tas de cases à cocher avec un identifiant particulier. Soumettez ensuite toutes les cases cochées. Je ne sais pas comment cela fonctionnerait avec les formulaires de Django.

Toutes les suggestions sont les bienvenues. Merci. EDIT: Eh bien, il s'avère qu'en donnant à chaque case à cocher un ID (par exemple ID du patient) dans le même groupe de noms et en regardant simplement le dictionnaire POST dans la vue django me donne exactement ce dont j'ai besoin!

+0

J'ai répondu à cette question ici: http://stackoverflow.com/a/27545910/1005499 – seddonym

Répondre

1

Vous ne voulez pas de jeu de formulaires, car il s'agit d'éditer des données dans un ensemble d'instances de modèle. Ce que vous voulez est un formulaire unique avec un ModelChoiceField ou ModelMultipleChoiceField - vous devrez peut-être changer le widget pour utiliser CheckboxSelectMultiple.

+0

Le problème qui est - il ne montre qu'une seule étiquette par modèle. Alors que je veux que la page affiche un tableau avec des colonnes "Prénom, Nom, date de naissance" etc ... Alors que MultipleModelChoideField n'a qu'une "étiquette" par modèle. À moins que je ne confonde quelque chose? – drozzy

+1

@drozzy: Vous pouvez remplacer ce que montre ModelChoiceField (Multiple) en utilisant la fonction 'label_from_instance()' décrite dans les docs. Néanmoins, pour mettre ces données dans le tableau, vous aurez besoin d'un traitement JS personnalisé. –

+0

Je ne suis pas très désireux de commencer à mettre des morceaux de HTML dans mon code python. Eh bien, merci pour suggestion de toute façon. – drozzy

3

J'ai créé un widget selectMultiple personnalisé pour afficher les détails de l'objet avec une case à cocher pour la sélection arrière lorsque les formulaires appelait encore newforms - il semble encore travailler:

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)) 

Exemple forme:

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] 

Avec le formulaire ci-dessus, vous passeriez la liste des éléments à afficher comme premier argument lors de l'instanciation de votre objet formulaire.

Modèle:

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

Merci, cela semble amusant. Mais comme j'ai répondu à la réponse de Daneil, je me sens vraiment mal à l'aise en mélangeant html dans mon code python. – drozzy

+0

Dans ce cas, il * devrait * être possible de générer des tuples de '(rendered_input_field, related_object)' à partir d'un widget personnalisé, mais cela implique aussi de créer un 'BoundField' personnalisé (ce que vous obtenez quand vous faites' {{form .fieldname}} ') pour utiliser le widget de manière appropriée au lieu d'appeler sa méthode de rendu. –