I have an interesting problem, first of all the model itself:
class Reserva(models.Model):
cliente = models.CharField(max_length = 200)
empleado_de_turno = models.ForeignKey(User, on_delete = models.CASCADE)
The problem consists:
Create an instance of the model
Reserva
through the viewCreateView
, which will create a form to create said instance, but in the form I do not want the field to be displayed,empleado_de_turno
I want only the field to be displayedcliente
and that by default the value of the fieldempleado_de_turno
is the user that login at that time.
Well to fix the said problem, try modifying the data sent by the POST method, in the method get_form_kwargs
:
class ReservaCreateView(CreateView):
model = Reserva
template_name = 'testapp1/example.html'
fields = ['cliente']
success_url = reverse_lazy('home')
def get_form_kwargs(self):
form_kwargs = super().get_form_kwargs()
if form_kwargs.get('data'):
user = User.objects.get(username = self.request.user)
post = self.request.POST.copy()
post['empleado_de_turno'] = str(user.pk)
form_kwargs['data'] = post
return form_kwargs
The thing is that it doesn't work for me, I always get this error:
django.db.utils.IntegrityError: NOT NULL constraint failed: testapp1_reserva.empleado_de_turno_id
I started trying to figure out where exactly the error occurred, because the method solution get_form_kwargs
that I showed before, should fix the error, and the error occurs in the method form_valid
, specifically in the form.save()
, here the source code of the method form_valid
:
ModelFormMixin:
def form_valid(self, form): """If the form is valid, save the associated model.""" self.object = form.save() return super().form_valid(form)
FormMixin:
def form_valid(self, form): """If the form is valid, redirect to the supplied URL.""" return HttpResponseRedirect(self.get_success_url())
IMPORTANT
I did the following with the intention of seeing how the data structure is when the instance was created, without omitting fields and when the field was omitted
empleado_de_turno
, in order to be able to replicate the structure when omitting said field and when modifying said data, and thus be able to solve the problem, avoiding errors
Case 1:
In the method form_valid
, I printed form.data
when the attribute fields
had the following value:
fields = ['cliente']
Case 2:
Later I did the same but in this case the attribute fields
had the following values:
fields = ['cliente', 'empleado_de_turno']
I printed the following in the data method form_valid
, since this was where the error specifically arose and it was possible that the data was not being sent as I modified it previously:
def form_valid(self, form):
print(form.data)
The result of printing it in the 2 cases was as follows:
<QueryDict: {'csrfmiddlewaretoken': ['SBRSJa0aw4iNJOMPwtQVsFL6V6y1cFMNGne9Kr0fA7YmtYeD8xshhDq6pc1mKjQs'], 'cliente': ['code2'], 'empleado_de_turno': ['1']}>
That means that whether you print form.data
in Case 1 or in Case 2 , the data is exactly the same, which means that the problem is not the data at the time of modifying it, it is something else (I deduce).
Since even when printing form.is_valid
in the method form_valid
, it returns True
.
So , why does it give an error, if I am replicating the data structure correctly when modifying it? I do not find sense in this error, I am doing the whole modification process correctly.
Update:
Many think that in reality the field empleado_de_turno
expects form
an object in the or in the view, I sent an id
and they may think that this is the error, but it is not.
First of all, I already tried it, instead of putting a id
in the field empleado_de_turno
, I put an object, and it gave the same error.
The reason that the field empleado_de_turno
expects a id
and not an object is because that is how the view manages it, at least when receiving the data through the POST method, I don't know why it manages it that way, that is how the developers decided of Django
.
This can be verified in Case 2 , no field is omitted, the two fields are filled normally and form_valid
the data entered is printed in the method as follows: form.data
.
Printing it results in:
<QueryDict: {'csrfmiddlewaretoken': ['SBRSJa0aw4iNJOMPwtQVsFL6V6y1cFMNGne9Kr0fA7YmtYeD8xshhDq6pc1mKjQs'], 'cliente': ['code2'], 'empleado_de_turno': ['1']}>
It can clearly be seen that the key
empleado_de_turno
value is a id
, not an object.
There you can check that the field empleado_de_turno
does not expect an object as such, at least not at that stage of the view.
I'm the one who inspired you to delve deeper into this question hehe, I already found a solution that seems to be more verbose that works for me:
big greeting!!
@Marcelo says the following in his answer (thanks to him I was able to find the answer):
What caught my attention was that it said "only accept the fields that you declare in the view" as a consequence I began to investigate the source code of the view
CreateView
, but I did not find anything about what it said, as it had to do with the attributefields
(as I mentioned), then it had to do more with the forms, the case is to investigate the source code of theBaseModelForm
.(It should be noted that if we do not define the attribute
form_class
in the view, with the form, the methodget_form_class
, of the view, is responsible for creating it automatically, just passing the attributefields
defined in the view, to the form)And I found the answer to "Why does the error happen?" , then I will explain in more depth how this happens and why the problem occurs:
Everything starts when the method is executed
is_valid
in the view (specifically in thepost
view method), said method calls the methodForm.full_clean
, which in turn calls the following methods (to do the respective validations):_clean_fields
_clean_form
_post_clean
When the method is called
_post_clean
, in one part it is responsible for validating the instance, where you can see how it callsModel.full_clean
the instance method, passing it the fields excluded from the method_get_validation_exclusions
:Once
Model.full_clean
the instance method is called, it callsModel.clean_fields
the instance method (among others), passing the excluded fields as parameters, in this way the methodModel.clean_fields
only creates the valid attributes, not the excluded ones :And that's it, the method
is_valid
returnsTrue
(but they have already done the respective validations), and proceed to create the instance in the methodModelForm.save
:But since the validations were executed, the attribute is not created
empleado_de_turno_id
despite having added it in the POST data and as a consequence the error occurs, preventing the creation of the instance whenform_valid
the view method tries to create it.Trying to replicate your problem I came to this method, it only accepts the fields that you declare in the view, this case will only be client:
You can verify this with this:
You will get the following error:
When you use:
This method simply overrides
form_kwargs
and ignoresempleado_de_turno
, the same thing happens inform_valid
that it will only validate what you declared in fields.That's why I recommend you use what the documentation points out :