When I try to use a form to edit an object that includes an image upload I get "This field is required". A similar form works fine to create the object, but when I retrieve the object and attempt to modify other fields, it fails on the image.
#-------models.py:
class Star(models.Model):
firstname = models.CharField(max_length=32)
lastname = models.CharField(max_length=32, blank=True)
portrait = models.ImageField(upload_to='images/')
#------views.py:
class StarForm(forms.ModelForm):
class Meta:
model = Star
fields = ["firstname", "lastname", "portrait"]
def staredit(request, star_id):
instance = Star.objects.get(pk=star_id)
form = StarForm(request.POST or None, instance=instance)
context = {
"form": form,
}
return render(request, "stars/edit.html", context)
def starchange(request):
form = StarForm(request.POST, request.FILES)
if form.is_valid():
newstar.save()
context = {
"message": "The form was posted",
}
return render(request, "stars/edit.html", context)
else:
context = {
"message": form.errors,
}
return render(request, "stars/edit.html", context)
#-----edit.html
<form action="/starchange" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
</form>
{{message}}
Error message:
portrait This field is required.
You are not updating the instance, since you never have passed the instance to the view that should update it. When you make a POST requrest, the browser only submits the content of the form elements. There is no data about what has rendered the previous form, that data is lost.
You should specify the instance
to update, so:
from django.shortcuts import get_object_or_404
def starchange(request, pk):
obj = get_object_or_404(Star, pk=pk)
form = StarForm(request.POST, request.FILES, instance=obj)
if form.is_valid():
form.save()
context = {
"message": "The form was posted",
}
return render(request, "stars/edit.html", context)
else:
context = {
"message": form.errors,
}
return render(request, "stars/edit.html", context)
in the urls, you thus should specify the primary key of the object to update:
urlpatterns = [
# …,
path('starchange/<int:pk>/', views.starchange, name='starchange')
]
and in the template, you should make a POST request to a view with the given instance:
<form action="{% url 'starchange' pk=form.instance.pk %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
</form>
This is one of the main reasons why often the same view is used both for a GET and POST request, since it makes it removes a lot of duplicate logic. Furthermore it is also more clean: you can use GET to retrieve the page, and POST to submit the page.
Note: In case of a successful POST request, you should make a
redirect
[Django-doc] to implement the Post/Redirect/Get pattern [wiki]. This avoids that you make the same POST request when the user refreshes the browser.
Note: It is often better to use
get_object_or_404(…)
[Django-doc], then to use.get(…)
[Django-doc] directly. In case the object does not exists, for example because the user altered the URL themselves, theget_object_or_404(…)
will result in returning a HTTP 404 Not Found response, whereas using.get(…)
will result in a HTTP 500 Server Error.
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments