Odd behavior in Django Form (readonly field/widget)
I'm having a problem with a test app I'm writing to verify some Django functionality. The test app is a small "grade book" application that is currently using Alex Gaynor's readonly field functionality http://lazypython.blogspot.com/2008/12/building-read-only-field-in-django.html
There are 2 problems which may be related. First, when I flop the comment on these 2 lines below:
# myform = GradeForm(data=request.POST, instance=mygrade)
myform = GradeROForm(data=request.POST, instance=mygrade)
it works like I expect, except of course that the student field is changeable.
When the comments are the shown way, the "studentId" field is displayed as a number (not the name, problem 1) and when I hit submit I get an error saying that studentId needs to be a Student instance.
I'm at a loss as to how to fix this. I'm not wedded to Alex Gaynor's code. ANY code will work. I'm relatively new to both Python and Django, so the hints I've seen on websites that say "m开发者_StackOverflowaking a read-only field is easy" are still beyond me.
// models.py
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)
# testbed.grades.readonly is alex gaynor's code
from testbed.grades.readonly import ReadOnlyField
class GradeROForm(ModelForm):
studentId = ReadOnlyField()
class Meta:
model=Grade
class GradeForm(ModelForm):
class Meta:
model=Grade
// views.py
def modifyGrade(request,student):
student = Student.objects.get(name=student)
mygrade = Grade.objects.get(studentId=student)
if request.method == "POST":
# myform = GradeForm(data=request.POST, instance=mygrade)
myform = GradeROForm(data=request.POST, instance=mygrade)
if myform.is_valid():
grade = myform.save()
info = "successfully updated %s" % grade.studentId
else:
# myform=GradeForm(instance=mygrade)
myform=GradeROForm(instance=mygrade)
return render_to_response('grades/modifyGrade.html',locals())
// template
<p>{{ info }}</p>
<form method="POST" action="">
<table>
{{ myform.as_table }}
</table>
<input type="submit" value="Submit">
</form>
// Alex Gaynor's code
from django import forms
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.forms.util import flatatt
class ReadOnlyWidget(forms.Widget):
def render(self, name, value, attrs):
final_attrs = self.build_attrs(attrs, name=name)
if hasattr(self, 'initial'):
value = self.initial
return mark_safe("<span %s>%s</span>" % (flatatt(final_attrs), escape(value) or ''))
def _has_changed(self, initial, data):
return False
class ReadOnlyField(forms.FileField):
widget = ReadOnlyWidget
def __init__(self, widget=None, label=None, initial=None, help_text=None):
forms.Field.__init__(self, label=label, initial=initial,
help_text=help_text, widget=widget)
def clean(self, value, initial):
self.widget.initial = initial
return initial
Don't bother with the ReadOnlyField. Use the Widget instead.
Here is the one I use regularly.
class ReadOnlyWidget(forms.Widget):
def __init__(self, original_value, display_value):
self.original_value = original_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
Use it with CharField.
Django 1.2 (released about a week and a half ago) supports read-only fields for the admin out of the box:
http://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.readonly_fields
I'm not sure how hard it is to extend that new functionality into something like a ModelForm to be displayed on your site, but it would serve as a more up-to-date starting point than Alex's (excellent but dated) code.
精彩评论