I want to be able to do:
def update_profile(request, username):
user = Profile.objects.get(user__username=username)
# update subset of profile, eg value_to_company is set in request.POST
# but user (or an arbitrary number of other attributes) is not
profile = ProfileForm(request.POST, instance=user)
if not profile.is_valid():
print profile.errors #user is required
The problem that I have with this is that user definitely exists and is part of the instance, and I can't find anything in the docs to suggest that instance data wouldn't get into the bound data. But I also can't find anything that explicitly says that it will.
(The rest of this is justification for why I'm doing things the way I am, and ideas for solutions, you probably don't need to read it.)
If you want to know why I'm going through a form--not essential to the question--it's because I've got something like this:
class Profile(models.Model):
user = models.ForeignKey(auth.models.User)
value_to_company = models.IntegerField()
class ValueField(forms.Field):
def to_python(self, value):
vals = {'high': 0,
'mid': 1}
return vals[value]
class ProfileForm(forms.ModelForm):
value_to_company = ValueField()
class Meta:
model = Profile
That is, I'm doing type api-to-internal-representation-coercion in my forms already and I'd like to continue using that.
I could reimplement the forms.is_valid() loop, just checking for the fields on the form that already exist, something hacky like:
# replaces `if not profile.is_valid()` above:
errors = []
for field in request.POST.iterkeys():
if field in profile.f开发者_JS百科ields:
profile.fields[field].to_python()
if not profile.fields['field'].clean():
errors.append #something
(I haven't actually looked at the logic internal logic, so I know that's wrong, but you get the idea.)
You likely just need to add exclude = ('user',)
to your form. The forms instance mostly determines initial values, if your form includes the user field, but that field is posted with a blank value it will try to blank out the user field, resulting in the error.
I found this solution in django-piston.
It adds additional form method merge_from_initial
which you call before you call is_valid
and it populates the missing fields from the object given.
I have changed it a bit and am using:
class BaseModelForm(forms.ModelForm):
"""
Subclass of `forms.ModelForm` which makes sure that the instance values are present in the form
data, so you don't have to send all old values for the form to actually validate. Django does not
do this on its own.
"""
def merge_from_instance(self):
# Internals
self.data._mutable = True
fun = lambda v: v not in self.data.keys()
for field in filter(fun, self.fields.keys()):
if field not in self.initial:
continue
value = self.initial.get(field)
if isinstance(self.instance._meta.get_field(field), related.ManyToManyField):
self.data.setlist(field, value)
else:
self.data[field] = value
精彩评论