I have an app that serves to update certain fields of a model. There are 4 possible fields that could be updated: resolution, upload4, upload5, and upload6. The upload fields are NOT required. If I do not include the request.FILES line, the uploaded file will not be saved to the database, but it seems like because I've included it, I need to always upload the 3 files, even though they are not required. The exception I am getting is "MultiValueDictKeyError" on the POST. How can I fix this? I want the option to add 3 files, but I don't want to have to every time. I understand how to make a field not required, I don't know how to code the request.FILES to understand that it is not required.
views.py
#login_required(login_url='/login/')
def report(request, case_id):
form = ReportForm()
case = get_object_or_404(Incident, pk=case_id)
# if this is a POST request we need to process the form data
if request.POST:
# create a form instance and populate it with the data from the request:
form = ReportForm(request.POST)
if form.is_valid():
resolution = (form.cleaned_data['resolution']) # grabbing action_taken from user input
case.resolution = resolution
case.upload4 = request.FILES['upload4']
case.upload5 = request.FILES['upload5']
case.upload6 = request.FILES['upload6']
case.status = Status.objects.get(status='closed')
case.save(update_fields=['resolution', 'status', 'upload4', 'upload5', 'upload6'])
context = { 'case': case,
'form': form}
return HttpResponseRedirect(reverse('dashboard_app:dashboard'))
template = "report.html"
#form = CaseForm()
context = { 'case': case,
'form': form}
return render(request, template, context)
The point is that you are ignoring the validation that form does, and going straight back to the data from the request. So, yes, that will break if the forms are not there. But this is exactly why we use forms.
case.upload4 = form.cleaned_data['upload4']
etc.
It would be even easier if you used a ModelForm; then you could pass case as the instance argument of the form, and just do form.save(), replacing almost all the code inside your is_valid block.
This will solve the problem.
case.upload4 = request.FILES.get('upload4')
case.upload5 = request.FILES.get('upload5')
case.upload6 = request.FILES.get('upload6')
Related
I made a model form in Django that enables user to create an instance of that model. All is well save one thing. Built-in error lists of Django is annoying. I want those errors to show itself when user actually made that error. When I open my page it looks like this
Please help me how do I remove these errorlist temporarily so that when user left input fields empty. It pop ups itself?
I don't know if you need these codes to solve this problem, but here it is:
views.py file here
#login_required(login_url='/accounts/login/')
def article_create(request):
article_form = ArticleForm(request.POST or None, request.FILES)
if article_form.is_valid():
instance = article_form.save(commit=False)
instance.author = request.user
article_form.save()
return render(request, 'blog/article-create.html', {'form': article_form})
Thank you
You use the request.POST or None idiom - which I dislike, but never mind - for the data parameter, but not for the files parameter. Since you pass an (empty) dict for the files, Django takes your form as bound, and therefore tries to validate it.
If you insist on using this idiom, you need to do it for both:
article_form = ArticleForm(request.POST or None, request.FILES or None)
But really, it's much more Pythonic to be explicit:
if request.method == 'POST':
article_form = ArticleForm(request.POST, request.FILES)
if article_form.is_valid():
instance = article_form.save(commit=False)
instance.author = request.user
article_form.save()
return redirect('/')
else:
article_form = ArticleForm()
return render(request, 'blog/article-create.html', {'form': article_form})
Note, you always need to redirect after a successful POST, as I have done above.
I'm trying to pass some data through a django context and am finding it kind of cumbersome. I'm very new to Python and a bit unfamiliar with storing data, I have a few suggestions here shown in the code:
1. A form to pass data:
def formview(request):
if request.method == 'POST':
form=LogonForm(request.POST)
if form.is_valid():
request.session['firstname']=form.cleaned_data['entrykey']
request.session['lastname']=form.cleaned_data['firstfield']
request.session['age']=form.cleaned_data['secondfield']
request.session['location']=form.cleaned_data['thirdfield']
return HttpResponseRedirect('thanks')
else:
form=LogonForm()
return render(request, 'logon/form.html', {'form': form})
A view which reacts to the submitted data, but not from database:
def thanksforform(request):
try:
#Using class
class thanksclass():
firstname=request.session['firstname']
lastname=request.session['lastname']
age=request.session['age']
location=request.session['location']
under18='Come back in %s years' % str(18-age)
over18='Step right up!'
#Using array
data=np.empty(6, dtype=object)
data[0]=request.session['firstname'] #etc. etc. ect.
#using list
listdata=[request.session['firstname']] #etc etc etc
except KeyError:
return HttpResponseRedirect('/logon/')
request.session.flush()
return render(request, 'logon/thanks.html', {'thanksclass': thanksclass})
Which type of object is preferred in Django for this kind of purpose, is there a better way to pass data through the view context?
PS. Please disregard the source being session data, It's just an example as I was messing around, I'm aware that you can call session data directly from the template aswell.
For myself, depends on how I'm retrieving/creating the data.
In general forms are a nice way to do it if you want to have some validation or allow the user to submit data or if you're representing something from the ORM but Django's templates accept a lot of data types.
Otherwise I'd recommend creating a dictionary and setting content equal to it. I.E.
varOne = '1'
varTwo = 'Two'
varThree = 3.0
myPassThrougData =
{'variableOne': varOne, 'variableTwo': varTwo, 'VariableThree':
varThree }
return render(request, 'logon/form.html', {'form': form}, content=myPassThoughData)
I have simple view in django app, which I want to show only when one of the forms is valid. I have something like:
#login_required
#require_role('admin')
def new_package(request):
invoicing_data_form = InvoicingDataForm(instance=request.user.account.company.invoicingdata)
if invoicing_data_form.is_valid():
# all here
return HttpResponse('Form valid')
else:
logger.info("Form invalid")
return HttpResponse(json.dumps(invoicing_data_form.errors)
I always get log info message that form is invalid, however, I get nothing in
invoicing_data_form.errors
It is very strange, because I am validating this form in other view using user input data and it works just fine. Any idea?
EDIT:
Just for clarification.
I am not requesting any data from user in this form.
I am using this form to validate some model instance (this form is subclassing from ModelForm).
That's because you're not "feeding" your form.
Do this:
invoicing_data_form = InvoicingDataForm(instance=invoice, data=request.POST or None)
You have an unbound form.
https://docs.djangoproject.com/en/1.7/ref/forms/api/#bound-and-unbound-forms
A Form instance is either bound to a set of data, or unbound.
If it’s bound to a set of data, it’s capable of validating that data and rendering the form as HTML with the data displayed in the HTML.
If it’s unbound, it cannot do validation (because there’s no data to validate!), but it can still render the blank form as HTML.
To bind data to a form, pass the data as a dictionary as the first parameter to your Form class constructor:
invoicing_data_form = InvoicingDataForm(request.POST or None, instance=invoice)
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.
I got this for AuthenticationForm which needs AuthenticationForm(None, request.POST) see Using AuthenticationForm in Django
I want to expand on the answer by #yuji-tomita-tomita
I typically use a CBV approach in Django, and how I'm handling forms:
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
# do things
Reading the source code I noticed that self.get_form() using get_form_kwargs(self) to populate the form with request.POST, thus getting bound to data. So if you're overloading it like I did:
def get_form_kwargs(self):
company = self.get_company()
return {"company": company}
Make sure to call the super(), and it will finally work:
def get_form_kwargs(self):
company = self.get_company()
kwargs = super().get_form_kwargs()
kwargs.update({"company": company})
return kwargs
I'm just starting with formsets and I have trouble making a user friendly error system.
So the user sees a list view that shows him all instances of a model already written into a formset. This pages is meant to show him the data and edit it as well.
Before I was using a lot of individual forms with a save button for every form. But now I want to improve it to have a formset that provides a single save button for all of the forms.
But there comes my problem: Before I used to send the user via "action" to another url (e.g. .../update/ ) which processes the request and then sends him back to the list view he saw before. That's to avoid multiple submits when hitting F5.
But now if I do this and only a single form is wrong all the information the user had entered is lost.
So instead I stopped using the extra URL and made the processing part of the list view. Now I can use form.error on every form, but also the user resubmits when hitting F5.
Is there a way to have both?
Also: I have 1 extra form. But if the user changes it, and I feed the POST data into the formset, save it and then put it back to the page I lost the extra formset, because now the former extra is showing the newly entered instance and there is no true extra field left until the page is refreshed without sending post data.
Here is my View:
class BVServerListView(View):
def get(self, request):
eigene_server = Server.objects.filter(user=request.user)
EigeneServerFormset = modelformset_factory(Server, extra=1, form=ServerForm)
eigene_server_formset = EigeneServerFormset(queryset=eigene_server)
context = {'eigene_server': eigene_server_formset,}
return render(request, 'bildverteiler/server_list.html', context)
def post(self, request):
eigene_server = Server.objects.filter(user=request.user)
EigeneServerFormset = modelformset_factory(Server, extra=1, form=ServerForm)
eigene_server_formset = EigeneServerFormset(request.POST, request.FILES)
for form in eigene_server_formset.forms:
if form.data.get('delete', False):
server = Server.objects.get(user=request.user, name=form.data['name'])
server.delete()
else:
if form.has_changed() and form.is_valid():
server = form.save(commit=False)
server.user = request.user
server.save()
context = {'eigene_server': eigene_server_formset,}
return render(request, 'bildverteiler/server_list.html', context)
There is no difference between using a single form or a formset here. The answer is the same: post to the same view, but redirect after a successful save.
The other thing that you are doing wrong here is to validate and save individual forms one by one. Don't do that, because you could end up with a situation that the first forms are valid and get saved, but subsequent ones are invalid and therefore the formset needs to be redisplayed. Instead, validate the formset as a whole:
if eigene_server_formset.is_valid():
for form in eigene_server_formset.forms:
if form.cleaned_data.get('delete'):
... delete ...
else:
form.save()
return HttpResponseRedirect('somewhere_else')
return render...
I have a model form:
class SnippetForm(ModelForm):
class Meta:
model = Snippet
exclude = ['author', 'slug']
and I want to be able to edit a particular instance by using this:
def edit_snippet(request, snippet_id):
#look up for that snippet
snippet = get_object_or_404(Snippet, pk=snippet_id)
if request.user.id != snippet.author.id:
return HttpResponseForbidden()
if request.method == 'POST':
form = SnippetForm(data=request.POST, instance=snippet)
if form.is_valid():
form.save()
return HttpResponseRedirect(snippet.get_absolute_url())
else:
form = SnippetForm(instance=snippet)
return render_to_response(SNIPPET_EDIT_TEMPLATE,
{'form':form, 'add':False, 'user':request.user},
RequestContext(request))
Notice that at the line
form = SnippetForm(data=request.POST, instance=snippet)
, I created a form that use the data supplied from the user, and bound it with the instance found using the primary key (received from the url). According to django documentation, when I call save() the existing instance should be updated with POSTED data. Instead, what I see is a new object is created and saved into the database. What went wrong? Thanks a lot.
[Edit] This is really embarrassed. The code indeed has nothing wrong with it. The only thing that messed up the whole thing was the action I put in the template (as I use a same template for add and edit a snippet)....Thanks a lot for your help, really appreciate that.
I don't see why it would happen. What version of django is it?
In any case, you can manually force update passing the corresponding argument.
form = SnippetForm(data=request.POST, instance=snippet, force_update=True)