In short: I have a model with several fields, and on this model is a foreign-key field connecting it to to a User instance. Then, I have a model-form(1) that generates HTML for this model's fields, and that with a custom ModelChoiceField-instance that presents UserProfile-instances instead of User-instances. To validate the submitted information, I have a form (regular form) that validates that a UserProfile-instance was submitted. If it was, I update request.POST (through a copy of it), and validate against a second model-form(2) that exactly mirrors the underlying model.
This all means that I first have to pass a request.POST with a lot of un-validated information into a form that only validates part of it, then updated a copy of a partially validated request.POST, edit it, and then finally validate it again.
Is all of this safe? Is it considered bad practice/unsafe to pass a request.POST with a lot of information into a form that only validates one part of it? Additionally, is it safe to make a copy of a request.POST that is only partially validated - and then update it?
To what degree can you work with objects containing unsafe information, such as request.POST, without it becoming a problem?
Thank you!
Related
I have a form with a bunch of fields but I also pass in values like the current page they submitted the form from and the user, which are hidden fields.
So from what I understand is_valid() will fail because of that. So do I need to run is_valid()?
For example, one of my forms has the line:
self.fields['user'].widget = forms.HiddenInput()
Yes, the form will be validated for all fields - depending upon required/non-required or default etc - for hidden fields as well with respect to provided data.
If you want to save or use the submitted data, you should validate the form using is_valid().
I have a formset that generates two forms that have different initial values. When the user submits the form, they're only going to fill out one of them, not both. These initial values get populated in the view, which (I think) means FormSet can't figure out if they've changed or not, so default validation fails. But really, all I care about is if one of them is valid. I'd like to take that valid form and process it. What's the best way to go about this?
Not only formset.is_valid() exists, but also
for form in formset.forms:
if form.is_valid():
..
should work.
Can anyone explain this? It's really unintuitive that a form validation method would update the related model. I understand it doesn't commit to the database, but why even modify the model in memory? Why not do it with form.save()?
I believe the reason is this:
The first time you call is_valid() or
access the errors attribute of a
ModelForm has always triggered form
validation, but as of Django 1.2, it
will also trigger model validation.
This has the side-effect of cleaning
the model you pass to the ModelForm
constructor. For instance, calling
is_valid() on your form will convert
any date fields on your model to
actual date objects.
From here. Logically, this does actually make sense. Suppose I have a model like this:
class SomeModel(models.Model):
Somefield = models.CharField(unique=True)
Then as far as the modelform is concerned, validating the field TextInputField would work for any value. However, from the model's perspective, not any value will do - that value must be unique.
The validation mechanisms are described here. Basically, calling full_clean on a model causes that model to check its own fields as per the OO paradigm. So for this reason, the in memory image of the model can be updated and validating it will check it can actually be written to the database.
Thus it makes sense, because your guard:
if form.is_valid():
Really ought to ensure that:
form.save()
Can actually happen.
I have a ModelForm field that is based on the following Model:
class Phrase(models.Model):
subject = models.ForeignKey(Entity) # Entity is unique on a per Entity.name basis
object = models.ForeignKey(Entity) # Entity is unique on a per Entity.name basis
The modelform (PhraseForm) has a field 'subject' that is a CharField. I want users to be able to enter a string. When the modelform is saved, and the string does not match an existing Entity, a new Entity is created.
This is why I had to overwrite the "subject" field of the Modelform, as I cannot use the automatically generated "subject" field of the Modelform (I hope I'm making myself clear here).
Now, all tests run fine when creating a new Phrase through the modelform. But, when modifying a Phrase:
p = Phrase.objects.latest()
pf = PhraseForm({'subject': 'anewsubject'}, instance=p).
pf.is_valid() returns False. The error I get is that "object" cannot be None. This makes sense, as indeed, the object field was not filled in.
What would be the best way to handle this? I could of course check if an instance is provided in the init() function of the PhraseForm, and then assign the missing field values from the instance passed. This doesn't feel as if it's the right way though, so, is there a less cumbersome way of making sure the instance's data is passed on through the ModelForm?
Now that I'm typing this, I guess there isn't, as the underlying model fields are being overwritten, meaning the form field values need to be filled in again in order for everything to work fine. Which makes me rephrase my question: is the way I've handled allowing users to enter free text and linking this to either a new or existing Entity the correct way of doing this?
Thanks in advance!
Why are you modifying using the form.
p = Phrase.objects.latest()
p.subject = Entity.objects.get_or_create(name='anewsubject')[0]
docs for get_or_create
If you are actually using the form it should work fine:
def mod_phrase(request, phrase_id=None):
phrase = get_object_or_404(Phrase, pk=phrase_id)
if request.method == 'POST':
form = PhraseForm(request.POST, instance=phrase)
if form.is_valid():
form.save()
return HttpResponse("Success")
else:
form = PhraseForm(instance=phrase)
context = { 'form': form }
return render_to_response('modify-phrase.html', context,
context_instance=RequestContext(request))
Setting the instance for the ModelForm sets initial data, and also lets the form know which object the form is working with. The way you are trying to use the form, you are passing an invalid data dictionary (lacks object), which the form is correctly telling you isn't valid. When you set the data to request.POST in the example above, the request.POST includes the initial data which allows the form to validate.
I am creating some custom validation, that on save checks if certain fields values have changed.
On POST, is there a way in the view that I can check what fields have changed for that formset?
I am currently looping through the formset and am able to view individual field values, so I could compare them to a query. It just seems to be a bit more than necessary considering the formset doesn't go through the save process if nothing has changed.
Any help is appreciated.
Add a method to your formset:
def has_changed()
return all([form.has_changed() for form in self.forms])
Similarly, the form also documents the changed_fields, which you can concatenate in your formset.
I don't think formset.save() blindly overwrites all entries into the db. It checks the changed forms, which I think is available in formset.changed_forms and it updates only those rows in the db.