2010-05-31 4 views
0

Je suis en train de mettre en place une application simple de «classe» où l'enseignant serait capable de mettre à jour les notes sans changer le nom des étudiants (du moins pas sur la mise à jour page de grade). Pour ce faire, j'utilise l'un des trucs en lecture seule, le plus simple. Le problème est qu'après le SOUMETTRE la vue est ré-affichée avec des valeurs «vides» pour les étudiants. J'aimerais que les noms des étudiants réapparaissent.Valeurs CharField disparaissant après la sauvegarde (champ en lecture seule)

Voici l'exemple le plus simple qui présente ce problème. (C'est une mauvaise conception de DB, je sais, j'ai extrait juste les parties pertinentes du code pour mettre en évidence le problème.Dans l'exemple réel, étudiant est dans sa propre table, mais le problème existe toujours là.)

models.py

class Grade1(models.Model): 
    student = models.CharField(max_length=50, unique=True) 
    finalGrade = models.CharField(max_length=3) 

class Grade1OForm(ModelForm): 
    student = forms.CharField(max_length=50, required=False) 
    def __init__(self, *args, **kwargs): 
     super(Grade1OForm,self).__init__(*args, **kwargs) 
     instance = getattr(self, 'instance', None) 
     if instance and instance.id: 
      self.fields['student'].widget.attrs['readonly'] = True 
      self.fields['student'].widget.attrs['disabled'] = 'disabled' 
    def clean_student(self): 
     instance = getattr(self,'instance',None) 
     if instance: 
      return instance.student 
     else: 
      return self.cleaned_data.get('student',None) 
    class Meta: 
     model=Grade1 

views.py

from django.forms.models import modelformset_factory 
def modifyAllGrades1(request): 
    gradeFormSetFactory = modelformset_factory(Grade1, form=Grade1OForm, extra=0) 
    studentQueryset = Grade1.objects.all() 
    if request.method=='POST': 
     myGradeFormSet = gradeFormSetFactory(request.POST, queryset=studentQueryset) 
     if myGradeFormSet.is_valid(): 
      myGradeFormSet.save() 
      info = "successfully modified" 
    else: 
     myGradeFormSet = gradeFormSetFactory(queryset=studentQueryset) 
    return render_to_response('grades/modifyAllGrades.html',locals()) 

modèle

<p>{{ info }}</p> 
<form method="POST" action=""> 
<table> 
{{ myGradeFormSet.management_form }} 
{% for myform in myGradeFormSet.forms %} 
    {# myform.as_table #} 
    <tr> 
    {% for field in myform %} 
    <td> {{ field }} {{ field.errors }} </td> 
    {% endfor %} 
    </tr> 
{% endfor %} 
</table> 
<input type="submit" value="Submit"> 
</form> 

Répondre

1

Votre façon d'afficher le champ en lecture seule est le problème.

Étant donné que le champ Étudiant est désactivé, le formulaire ne l'aura pas comme entrée, de sorte que le formulaire d'erreur affiché avec les messages d'erreur de validation n'obtient pas la valeur initiale. C'est pourquoi ReadOnly Widget doit être plus complexe que d'être un champ html désactivé. Essayez d'utiliser un ReadOnlyWidget réel, celui qui remplace _has_changed.

Voici ce que j'utilise. Pour l'instanciation, il prend l'original_value et éventuellement display_value, s'il est différent.

class ReadOnlyWidget(forms.Widget): 

    def __init__(self, original_value, display_value=None): 
     self.original_value = original_value 
     if display_value: 
      self.display_value = display_value 
     super(ReadOnlyWidget, self).__init__() 

    def _has_changed(self, initial, data): 
     return False 

    def render(self, name, value, attrs=None): 
     if self.display_value is not None: 
      return unicode(self.display_value) 
     return unicode(self.original_value) 

    def value_from_datadict(self, data, files, name): 
     return self.original_value 
+0

Merci Lakshman, mais je suis encore nouveau à Django. Comment puis-je définir la valeur d'origine? J'essaie de l'appeler comme: student = forms.CharField (max_length = 50, widget = ReadOnlyWidget()) mais ce n'est pas correct car j'ai besoin de passer une valeur originale. Note ... Mon but éventuel est de l'utiliser dans un modelformset. – jamida

+0

jamida: Passez à ReadOnlyWidget, une instance d'étudiant, que vous souhaitez afficher en lecture seule sur le formulaire. –

+0

Comment faire cela dans un modelformset? Est-ce que je fais cela dans l'exemple de code ci-dessus ou dans la routine __init__ du formulaire? (ou soit?) Et si la routine init, comment faire référence au widget dans cette routine init? Je suis ouvert à la lecture de documentation, mais cette profondeur dans django n'est pas bien documentée. Si vous avez un lien, ce serait génial! – jamida

0

Je m'étire un peu ici, donc quelques réflexions:

% Avez-vous reniflé le trafic pour voir exactement ce qui est envoyé entre le navigateur et le serveur?

% Avez-vous besoin d'envoyer le nom de l'étudiant comme un champ caché (votre chose mise à jour db peut supposer que vous voulez l'étudiant vide si vous ne le faites pas)?

% Avez-vous regardé la source de votre HTML après que Python l'ait analysé?

+0

Merci d'avoir essayé Barry ... quelques commentaires relatifs. L'un est qu'un autre tour Readonly fonctionne très bien. Cependant, cette astuce ne fonctionnera pas si le champ Étudiant est une clé étrangère dans une autre table (mon application complète). Un autre est, j'ai regardé le HTML généré et les variables POST. Django ne récupère pas l'étudiant, ce qui est normal pour un champ DISABLED et/ou READONLY. Ce n'est pas un problème car la routine CLEAN le règle à la bonne valeur. Enfin, je mets une instruction print dans la clause else de la routine clean et cela n'est pas appelé. Donc l'étudiant est toujours disponible. – jamida

Questions connexes