I have a lot of problems when I figure out how to automatically create an instance of a model for a ForeignKey field when submitting a form. Here is a simple toy website that illustrates the problem:
I have two models: Model1 and Model2. Model2 contains ForeignKey for Model1. I want the user to be able to create an instance of Model2, either by specifically selecting an instance of Model1 to store in ForeignKey, or by leaving this value empty and allowing the instance of Model1 to be automatically generated.
Here it seems to me what this code should look like. My models.py code is very simple:
# models.py from django.db import models from django.core.validators import MinValueValidator class Model1(models.Model):
forms.py is a bit involved, but what happens is pretty simple. If Model2Form does not receive an instance of Model1, it tries to automatically create it in a clean method, validates it, and if it is valid, it saves it. If this is not valid, an exception is thrown.
#forms.py from django import forms from django.forms.models import model_to_dict from .models import Model1, Model2
However, this approach does not work. If I enter valid data in my form, it works fine. But, if I don't enter anything for ForeignKey and put a negative value for an integer, I get a ValueError.
Traceback: File "/Library/Python/2.7/site-packages/django/core/handlers/base.py" in get_response 111. response = callback (request, * callback_args, ** callback_kwargs) File "/Library/Python/2.7 /site-packages/django/views/generic/base.py "in View 48. return self.dispatch (request, * args, ** kwargs) File" /Library/Python/2.7/site-packages/django/views/ generic / base.py "to send 69. Return handler (request, * args, ** kwargs) File" /Library/Python/2.7/site-packages/django/views/generic/edit.py "after after 172. return super (BaseCreateView, self) .post (request, * args, ** kwargs) File "/Library/Python/2.7/site-packages/django/views/generic/edit.py" after 137. if form.is_valid ( ): The file /Library/Python/2.7/site-packages/django/forms/forms.py in is_valid 124. return self.is_bound, not bool (self.errors) The file /Library/Python/2.7/site -packages / django / forms / forms.py "in _get_e rrors 115. self.full_clean () File "/Library/Python/2.7/site-packages/django/forms/forms.py" in full_clean 272. self._post_clean () File "/Library/Python/2.7/site-packages /django/forms/models.py "in _post_clean 309. self.instance = construct_instance (self, self.instance, opts.fields, opts.exclude) file" /Library/Python/2.7/site-packages/django/forms/ models.py "in construct_instance 51. f.save_form_data (instance, cleaned_data [f.name]) File" /Library/Python/2.7/site-packages/django/db/models/fields/ init .py "in save_form_data 454. setattr (instance, self.name, data) The file "/Library/Python/2.7/site-packages/django/db/models/fields/related.py" in install 362. (instance._meta.object_name, self.field. name))
Exception Type: ValueError at / add / Exception Value: Cannot Assign None: "Model2.related_model1" does not allow null values.
So what happens is that Django catches my ValidationError and still creates an instance of Model2, although the validation fails.
I could fix this by overriding the _post_clean method so as not to instantiate Model2 if there are errors. But this decision is ugly. In particular, the behavior of _post_clean is very useful overall. In more complex projects, I need _post_clean to start for other reasons.
I could also let ForeignKey be null, but never set it to zero in practice. But, again, this seems like a bad idea.
I could even create the Model1 mannequin, which I use whenever the test on trying to create a new model 1 fails, but it also seems hacky.
In general, I can come up with a lot of hacks to fix this, but I have no idea how to fix this in a clean, pythonic way.