Why it can't pass a validation with the django form when set data after initializing
Question:
Why it can’t pass a validation when I set data like this (form.data=request.POST)
@login_required
def add_delivery_view(request):
user = request.user
delivery = (Delivery.objects.filter(user=user) or [None])[0]
form = AddDeliveryForm(instance=delivery)
if request.method == 'POST':
form.data = request.POST
if form.is_valid():
delivery = form.save(commit=False)
delivery.user = request.user
form.save()
return redirect('/')
return render(request, 'order/add_delivery.html', {'form':form})
But when I set data like this, it works just fine
...
if request.method == 'POST':
form = AddDeliveryForm(data=request.POST, instance=delivery)
if form.is_valid():
delivery = form.save(commit=False)
...
I guess it something with initialization of form. Maybe I should add some method after form.data=request.POST
Question is: What is the difference underneath the hood?
Thanks in advance
Answers:
It sets extra fields, like .is_bound
to True
, which means that there is data (this can be through data=…
, or files=…
, or both). So it has some procedure that does not only set .data
to the request.POST
. While you could try to mimic this behavior, it is better to let the constructor do its work since certain forms might override the constructor, and in the future the behavior of Django’s Form
might change as well.
It also looks quite ugly, constructing a form with data makes it clear the form will process the data. As for the .filter(…)
part, this is equivalent to .first()
[Django-doc], and it is better to let the form commit, since then it can save many-to-many fields as well, so:
@login_required
def add_delivery_view(request):
delivery = Delivery.objects.filter(user=request.user).first()
if request.method == 'POST':
form = AddDeliveryForm(request.POST, request.FILES, instance=delivery)
if form.is_valid():
form.instance.user = request.user
form
return redirect('name-of-some-view')
else:
form = AddDeliveryForm(instance=delivery)
return render(request, 'order/add_delivery.html', {'form': form})
Why it can’t pass a validation when I set data like this (form.data=request.POST)
@login_required
def add_delivery_view(request):
user = request.user
delivery = (Delivery.objects.filter(user=user) or [None])[0]
form = AddDeliveryForm(instance=delivery)
if request.method == 'POST':
form.data = request.POST
if form.is_valid():
delivery = form.save(commit=False)
delivery.user = request.user
form.save()
return redirect('/')
return render(request, 'order/add_delivery.html', {'form':form})
But when I set data like this, it works just fine
...
if request.method == 'POST':
form = AddDeliveryForm(data=request.POST, instance=delivery)
if form.is_valid():
delivery = form.save(commit=False)
...
I guess it something with initialization of form. Maybe I should add some method after form.data=request.POST
Question is: What is the difference underneath the hood?
Thanks in advance
It sets extra fields, like .is_bound
to True
, which means that there is data (this can be through data=…
, or files=…
, or both). So it has some procedure that does not only set .data
to the request.POST
. While you could try to mimic this behavior, it is better to let the constructor do its work since certain forms might override the constructor, and in the future the behavior of Django’s Form
might change as well.
It also looks quite ugly, constructing a form with data makes it clear the form will process the data. As for the .filter(…)
part, this is equivalent to .first()
[Django-doc], and it is better to let the form commit, since then it can save many-to-many fields as well, so:
@login_required
def add_delivery_view(request):
delivery = Delivery.objects.filter(user=request.user).first()
if request.method == 'POST':
form = AddDeliveryForm(request.POST, request.FILES, instance=delivery)
if form.is_valid():
form.instance.user = request.user
form
return redirect('name-of-some-view')
else:
form = AddDeliveryForm(instance=delivery)
return render(request, 'order/add_delivery.html', {'form': form})