Django ModelForm fails validation with no errors - django

Ok, I have been staring at this for hours trying to figure out what's going on, to no avail.
I am trying to create a ModelForm using the 'instance' keyword to pass it an existing model instance and then save it.
Here is the ModelForm (stripped considerably from the original in my attempts to identify the cause of this problem):
class TempRuleFieldForm(ModelForm):
class Meta:
model = RuleField
and here is the code I'm running:
>>> m = RuleField.objects.get(pk=1)
>>> f = TempRuleFieldForm(instance=m)
>>> f.is_valid()
False
The model object (m above) is valid and it saves just fine, but the form will not validate. Now, as far as I can tell, this code is identical to the Django docs example found here: http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#the-save-method, though obviously I am missing something. I would greatly appreciate some fresh eyes to tell me what I've got wrong.
Thanks

Note that your link doesn't call f.is_valid(), it just saves directly. This is potentially a bit misleading.
The point is that instantiating a form with just an instance parameter but no data does not bind it to data, and the form is therefore not valid. You will see that f.is_bound is False.
Behind the scenes, instance is really just the same as passing initial data, which as the docs note is only used to display the data initially and is not used for saving. You would probably benefit from reading the notes on bound and unbound forms.

If u still want to validate the object that was in the database, you can serialize it first and then create the Form with it.
from django.utils import simplejson
from django.core.serializers import serialize
(...)
fields_dict = simplejson.loads(serialize('json', [obj]))[0]['fields']
form = forms.MyForm(fields_dict)
if form.is_valid
This is probably not the best way to do it but the only one that I have found to get a bound form from a model. I need it because I want to validate the current data in the database. I create a question since I don't think this is the best way of doing it:
Transform an unbound form to a bound one?

This isn't a solution for OP, but it is for the post title, which is quite high in Google. So I'll post it anyway, from here:
If you're already giving request.POST to your form using request.POST or None, but it's still invalid without errors, check that there isn't any redirect going on. A redirect loses your POST data and your form will be invalid with no errors because it's unbound.

Not so much a solution for OP but this was a problem I ran into, specifically when running unit tests on ModelForms, it was a nuisance to keep on having to bind the form then also define an instance with the very same data. I created a small helper function to make things easier which others may find useful — I'm only using this for testing purposes and would be cautious to deploy it anywhere else without significant tweaks (if at all)
def testing_model_form(instance, model_form_class):
"""
A function that creates instances ModelForms useful for testing, basically takes an instance as an argument and will take care
of automatic binding of the form so it can be validated and errors checked
"""
fields = model_form_class.Meta.fields
data_dict = {}
for field in fields:
if hasattr(instance, field):
# The field is present on the model instance
data_dict[field] = getattr(instance, field)
x = model_form_class(data=data_dict)
x.instance = instance
return x

Related

Django filter the queryset of ModelChoiceField - what did i do wrong?

I know that many questions exist about this same topic, but i am confused on one point.
My intent is to show two ModelChoiceFields on the form, but not directly tie them to the Game model.
I have the following:
forms.py
class AddGame(forms.ModelForm):
won_lag = forms.ChoiceField(choices=[('1','Home') , ('2', 'Away') ])
home_team = forms.ModelChoiceField(queryset=Player.objects.all())
away_team = forms.ModelChoiceField(queryset=Player.objects.all())
class Meta:
model = Game
fields = ('match', 'match_sequence')
Views.py
def game_add(request, match_id):
game = Game()
try:
match = Match.objects.get(id=match_id)
except Match.DoesNotExist:
# we have no object! do something
pass
game.match = match
# get form
form = AddGame(request.POST or None, instance=game)
form.fields['home_team'].queryset = Player.objects.filter(team=match.home_team )
# handle post-back (new or existing; on success nav to game list)
if request.method == 'POST':
if form.is_valid():
form.save()
# redirect to list of games for the specified match
return HttpResponseRedirect(reverse('nine.views.list_games'))
...
Where i am confused is when setting the queryset filter.
First i tried:
form.home_team.queryset = Player.objects.filter(team=match.home_team )
but i got this error
AttributeError at /nine/games/new/1
'AddGame' object has no attribute 'home_team'
...
so i changed it to the following: (after reading other posts)
form.fields['home_team'].queryset = Player.objects.filter(team=match.home_team )
and now it works fine.
So my question is, what is the difference between the two lines? Why did the second one work and not the first? I am sure it is a newbie (i am one) question, but i am baffled.
Any help would be appreciated.
Django Forms are metaclasses:
>>> type(AddGame)
<class 'django.forms.forms.DeclarativeFieldsMetaclass'>
They basically create a form instance according to the information given in its definition. This means, you won't get exactly what you see when you define the AddGame form. When you instantiate it, the metaclass will return the proper instance with the fields provided:
>>> type(AddGame())
<class 'your_app.forms.AddGame'>
So, with the instance, you can access the fields by simply doing form.field. In fact, it is a bit more complicated than that. There are two types of fields you can access. With form['field'] you'll be accessing a BoundField. Which is used for output and raw_input.
By doing form.fields['fields'] you'll be then accessing to a field that python can understand. This is because if the from already got any input, there's where validation and data conversion take places (in fact, those are the fields used for this, the general process of validation is a bit more complicated).
I hope this might clear a little the issue for you but as you may see, the whole form's API is really big and complicated. Is very simple for end-users but it has a lot of programming behind the curtains :)
Reading the links provides will help clear your doubts and will improve your knowledge about this very useful topic and Django in general.
Good luck!
UPDATE: By the way, if you want to learn more about Python's Metaclasses, this is a hell of an answer about the topic.
In you views.py, you have this line:
form = AddGame(request.POST or None, instance=game)
So form is a Form object of class AddGame (Side note: you should change the name to AddGameForm to avoid confusion).
Since home_team is a field in AddGame class, it's not an attribute in form object. That's why you can't access it via form.home_team.
However, Django Form API provides fields attribute to any form object, which is a dict contains all form fields. That's why you can access form.fields['home_team'].
And finally since home_team is a ModelChoiceField, it can contain a queryset attribute, that's why you can access form.fields['home_team'].queryset

Dealing with model forms in Django

I thought it would be easier to start using models form instead of regular forms(giving up on all the easy things modelform provides).
But when I try to do this:
>>> m = Model.objects.get(pk=1)
>>> f = ModelForm(instance=m)
>>> f.is_valid()
False
>>> f.save()
AttributeError: 'ModelForm' objects has no attribute 'cleaned_data'
I think django documentation is wrong by saying:
Every form produced by ModelForm also has a save() method. This
method creates and saves a database object from the data bound to
the form. A subclass of ModelForm can accept an existing model
instance as the keyword argument instance; if this is supplied, save()
will update that instance. If it’s not supplied, save() will create a
new instance of the specified model
Cause this is just not working for me.
Am I right that the django documentation is wrong?
Thanks in advance.
You may have forgotten to add "data" into the ModelForm instantiation according to the user's request.POST data.
f = ModelForm(data=request.POST, instance=m)
f.is_valid() # is True if the data is ok.
In any case, it would be better to post your relevant code: The model class, the model form class and your view.
EDIT: you must add a data= parameter (or the first parameter, if you don't name it) to a ModelForm initialization, if you want is_valid() to work. is_valid is here to check the given data against the various validation rules, and only if it's ok, lets you save the ModelForm. Initializing a ModelForm just with the instance= named parameter does not trigger any validation, because there is nothing new to validate.

Instanting ModelForm with where modelfields are overwritten

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.

how to find associated Django ModelForm given the Model

I have dozens of Models, each with ONE associated ModelForm (whose Meta.model refers to the Model in question).
E.g.
class FooModel(Model):
pass
class FooModelForm(ModelForm):
class Meta:
model = FooModel
# current approach using a classmethod
FooModelForm.insert_in_model() # does cls.Meta.model.form = cls
So, obviously, it's easy to find FooModel given FooModelForm. What I want is to know the best way to do the REVERSE: find FooModelForm when I am presented with FooModel or even the string "Foo".
Assume only one ModelForm for each model, although solutions that return multiple are fine.
My current approach is to stash the model in the form class (as shown above), but I'm interested in knowing better approaches especially ones that could compute it centrally (without the final line above).
EDIT: I've reviewed things like Django: Display Generic ModelForm or predefined form but I believe this is a simpler question than those. The Django admin code must do something along the lines of what I seek. But get_model equivalent for ModelForms? suggests that might be voodoo and that it would be best to just do dict['Foo']=FooModelForm or its equivalent to keep track of the association explicitly. Seems repetitious.
If you have under 20 forms, sounds like mapping out a dictionary is the easiest way. Django does this kinda thing internally too.
For ModelForms, django admin just creates them on the fly via modelform_factory, so there is no comparable method to get_model
I do see, your method is bullet proof, but requires a line in ever model def.
If you only have one ModelForm per model, you could potentially iterate through the ModelForm subclasses until you find your form.
find FooModelForm when I am presented
with FooModel or even the string
"Foo".
modelforms = forms.ModelForm.__subclasses__()
def get_modelform(model):
try:
return filter(lambda x:x.Meta.model == model, modelforms)[0]
except IndexError:
print "apparently, there wasn't a ModelForm for your model"
If you want to pull the ModelForm as a string, you'll need to make sure both
app_label and __name__ are correct, which means it will be easier to use get_model('app', 'model') in the function.
You could combine this with your method and automatically place an attribute on your models that point to its ModelForm.
Hook into the class_prepared signal at the top of your apps, find the corresponding ModelForm and attach it to your Model class.
Hope that helps or gives you some ideas.

django forms doubt

Here, I am a bit confused with forms in Django. I have information for the form(a poll i.e the poll question and options) coming from some db_table - table1 or say class1 in models. Now the vote from this poll is to be captured which is another model say class2. So, I am just getting confused with the whole flow of forms, here i think. How will the data be captured into the class2 table?
I was trying something like this.
def blah1()
get_data_from_db_table_1()
x = blah2Form()
render_to_response(blah.html,{...})
Forms have nothing to do with models in Django. They are just class meant to get informations from a dictionary (often request.POST) and check if each data linked to a key match a type and a format (e.g: is this a string of the form "bla#foo.tld").
You can ask django to create a form from a model, and in that case it will do its checking job, then if the data match, it will create a model, fill it and save it.
If a form is not created from a model, it will do nothing but checking. It will save nothing.
If it is created from a model, it will create a new instance of this particular model instance and save it.
If you want something more complicated, like, pre fill a form from various models or according to some conditions, or, say, you need to save several models according to the result of one form, you must do it manually.