2009-12-01 4 views
15

Mon champ de formulaire ressemble à quelque chose comme ce qui suit:Django créer un champ de formulaire qui est lu seulement en utilisant des widgets

class FooForm(ModelForm): 
    somefield = models.CharField(
     widget=forms.TextInput(attrs={'readonly':'readonly'}) 
    ) 

    class Meta: 
     model = Foo 

Geting une erreur comme celle-ci avec le code ci-dessus: initialisation() a obtenu un argument mot-clé inattendu 'widget'

Je pensais que c'est une utilisation légitime d'un widget de forme?

+0

Cela a changé en django 1.9 https://stackoverflow.com/questions/324477/in-a-django-form-how -do-i-make-a-field-readonly-ou-disabled-so-that-it-ne-peut-pas- – zudebluvstein

Répondre

40

Vous devez utiliser un champ de formulaire et non un champ de modèle:

somefield = models.CharField(
    widget=forms.TextInput(attrs={'readonly':'readonly'}) 
) 

remplacé par

somefield = forms.CharField(
    widget=forms.TextInput(attrs={'readonly':'readonly'}) 
) 

devrait corriger.

+1

Ça n'a l'air pas sûr! – jpic

+0

Pour le point de jpic, les utilisateurs pourraient simplement désactiver le drapeau en lecture seule et toujours soumettre des informations. Voir http://stackoverflow.com/a/325038/4972 pour une réponse plus complète. –

17

Notez que l'attribut readonly n'empêche pas Django de traiter les valeurs envoyées par le client. S'il est important pour vous que la valeur ne change pas, peu importe la créativité de vos utilisateurs avec FireBug, vous devez utiliser une méthode plus complexe, par exemple. un ReadOnlyField/ReadOnlyWidget comme démontré dans un blog entry par Alex Gaynor.

1

Comme Benjamin (https://stackoverflow.com/a/2359167/565525) bien expliqué, en plus de rendre correctement, vous devez traiter le champ sur le backend correctement.

Il y a un SO question and answers qui a beaucoup de bonnes solutions. Mais de toute façon:

1) première approche - suppression du champ dans la méthode save(), par ex. (Non testé;)):

def save(self, *args, **kwargs): 
    for fname in self.readonly_fields: 
     if fname in self.cleaned_data: 
      del self.cleaned_data[fname] 
    return super(<form-name>, self).save(*args,**kwargs) 

2) deuxième approche - champ remise à la valeur initiale dans la méthode propre:

def clean_<fieldname>(self): 
    return self.initial[<fieldname>] # or getattr(self.instance, <fieldname>) 

Sur la base de la deuxième approche je l'ai généralisé comme ceci:

from functools     import partial 

class <Form-name>(...): 

    def __init__(self, ...): 
     ... 
     super(<Form-name>, self).__init__(*args, **kwargs) 
     ... 
     for i, (fname, field) in enumerate(self.fields.iteritems()): 
      if fname in self.readonly_fields: 
       field.widget.attrs['readonly'] = "readonly" 
       field.required = False 
       # set clean method to reset value back 
       clean_method_name = "clean_%s" % fname 
       assert clean_method_name not in dir(self) 
       setattr(self, clean_method_name, partial(self._clean_for_readonly_field, fname=fname)) 


    def _clean_for_readonly_field(self, fname): 
     """ will reset value to initial - nothing will be changed 
      needs to be added dynamically - partial, see init_fields 
     """ 
     return self.initial[fname] # or getattr(self.instance, fname) 
3

J'allais dans le même problème donc j'ai créé un Mixin qui semble fonctionner pour mes cas d'utilisation.

class ReadOnlyFieldsMixin(object): 
    readonly_fields =() 

    def __init__(self, *args, **kwargs): 
     super(ReadOnlyFieldsMixin, self).__init__(*args, **kwargs) 
     for field in (field for name, field in self.fields.iteritems() if name in self.readonly_fields): 
      field.widget.attrs['disabled'] = 'true' 
      field.required = False 

    def clean(self): 
     cleaned_data = super(ReadOnlyFieldsMixin,self).clean() 
     for field in self.readonly_fields: 
      cleaned_data[field] = getattr(self.instance, field) 

     return cleaned_data 

Utilisation, il suffit de définir ceux qui doivent être lus seulement:

class MyFormWithReadOnlyFields(ReadOnlyFieldsMixin, MyForm): 
    readonly_fields = ('field1', 'field2', 'fieldx') 
Questions connexes