views:

104

answers:

1

I'm writing a test "grade book" application. The models.py file is shown below.

class Student(models.Model):
    name = models.CharField(max_length=50)
    parent = models.CharField(max_length=50)
    def __unicode__(self):
        return self.name

class Grade(models.Model):
    studentId = models.ForeignKey(Student)
    finalGrade = models.CharField(max_length=3)

I'd like to be able to change the final grade for several students in a modelformset but for now I'm just trying one student at a time. I'm also trying to create a form for it that shows the student name as a field that can not be changed, the only thing that can be changed here is the finalGrade. So I used this trick to make the studentId read-only.

class GradeROForm(ModelForm):
    studentId = forms.ModelChoiceField(queryset=Student.objects.all())
    def __init__(self, *args, **kwargs):
        super(GradeROForm,self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.id:
            self.fields['studentId'].widget.attrs['disabled']='disabled'
    def clean_studentId(self):
        instance = getattr(self,'instance',None)
        if instance:
            return instance.studentId
        else:
            return self.cleaned_data.get('studentId',None)
    class Meta:
        model=Grade

And here is my view:

def modifyGrade(request,student):
    student = Student.objects.get(name=student)
    mygrade = Grade.objects.get(studentId=student)
    if request.method == "POST":
        myform = GradeROForm(data=request.POST, instance=mygrade)
        if myform.is_valid():
            grade = myform.save()
            info = "successfully updated %s" % grade.studentId
    else:
        myform=GradeROForm(instance=mygrade)
    return render_to_response('grades/modifyGrade.html',locals())

This displays the form like I expect, but when I hit "submit" I get a form validation error for the student field telling me this field is required. I'm guessing that, since the field is "disabled", the value is not being reported in the POST and for reasons unknown to me the instance isn't being used in its place.

I'm a new Django/Python programmer, but quite experienced in other languages. I can't believe I've stumbled upon such a difficult to solve problem in my first significant django app. I figure I must be missing something. Any ideas?

+1  A: 

You should perform:

self.fields['studentId'].widget.attrs['readonly'] = True

and also make sure not to overwrite the value on postback.

Furthermore, if still having problems with the required field, you can do the following in your modelform:

studentID = forms.CharField(label="A label", help_text="Student ID", required=False)
Martin Eve
That did the trick! Seems ugly, but workable. This is a prototype anyway. I'm surprised by one thing though. I had to add "required=False" even though this is in fact a required field in the model level. The studentId field is not being sent back in the POST response, and w/o the "required=False" statement form validation fails BEFORE my customized Clean routine. But apparently with "required=false" my Clean routine has a chance to run and reset the proper studentId info.
jamida
Actually there's one small problem remaining. After I submit the form when it returns the "readonly field" is blank. It still works, probably because of the modified clean() but it's awkward. Any thoughts?
jamida
Can you repopulate the form value from the retrieved model, perhaps?
Martin Eve