Invalid form when uploading a file - django

I am trying a basic example in uploading a file with django.
I tried the code from the django documentaion but I keep getting invalid form. And when I don't test the validation of the form and try to handle the file directly, I get:
MultiValueDictKeyError at /neurons/nblast
"
'file'"
P.S:
Previously, I had used a model with a FileField and set the (upload_to), but in my current case I don't need to use the model, I only need to let the user uploads his files.
This is my code:
Template
<body>
<form action="" method="post">
{{ form }}
<br>
<button class="btn btn-success" name="btn_upload">
<span class="glyphicon glyphicon-upload"></span>
<b>Upload</b>
</button>
{% csrf_token %}
</form>
</body>
Views
def test(request):
if request.method == GET:
form = UploadFileForm()
if request.method == POST:
if 'btn_upload' in request.POST:
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
handle_uploaded_file(request.FILES['file'])
else:
print 'Not Valid'
form = UploadFileForm()
return render_to_response('test.html',
{'form': form},
context_instance=RequestContext(request))
Forms:
class UploadFileForm(forms.Form):
file = forms.FileField()
Thank you very much

Have you tried looking at The Django 'File Uploads' docs , especially the enctype="multipart/form-data" attribute?

u missed this one enctype="multipart/form-data"

Related

In Django, how can we stop losing the details filled in the form, if it fails validation?

I am using the UserCreationForm for user registration in my Django web app. When the user fills in the detail (username, password, confirm_password) and submits, then if the form fails validation (maybe because username already exists or password don't match or password is too short) then the user is redirected back to the register page.
Now my problem is that when he is redirected back to the register page, the form is blank again, i.e. he has to start all over again, which is not a good user experience because his form might fail one or more times.
I want that if the form fails validation and when he is redirected to the registration form, he should see his previously filled in details so that he can just correct the field which is causing the error and move on. Is there any way the data can be retained?
Here is my code of the register function in views.py:
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST or None)
if form.is_valid():
# some code
return redirect('login')
else:
messages.error(request, form.errors)
return HttpResponseRedirect(reverse("register"))
else:
return render(request, 'accounts/register.html')
my register.html:
<form method="POST" action="{% url 'register' %}">
{% csrf_token %}
<input name="username" type="text" class="..." style="...">
<input name="password1" type="password" class="..." style="...">
<input name="password2" type="password" class="..." style="...">
<button type="submit">Sign up</button>
</form>
Edit:
I have not passed the form in context to the template, so that I can control the CSS of my input fields, which I am not sure how to do otherwise without creating forms.py file separately.
If it's not possible to retain details in the form, then what should I do to at least improve the present situation?
Instead of return redirect just render the response with the form object again:
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST or None)
if form.is_valid():
# some code
return redirect('login')
messages.error(request, form.errors)
else:
form = UserCreationForm()
return render(request, 'accounts/register.html', {'form': form})
In the template since you want to control css use django-widget-tweaks:
{% load widget_tweaks %}
<form method="POST" action="{% url 'register' %}">
{% csrf_token %}
{% render_field form.username class="..." style="..." %}
{% render_field form.password1 class="..." style="..." %}
{% render_field form.password2 class="..." style="..." %}
<button type="submit">Sign up</button>
</form>
The load widget tweaks must be at the top of the html file like all load tags.
def register(request):
context = {}
if request.method == 'POST':
form = UserCreationForm(request.POST or None)
if form.is_valid():
# some code
return redirect('login')
else:
context['form'] = form
messages.error(request, form.errors)
context['form'] = UserCreationForm()
return render(request, 'accounts/register.html', context)
it should work

My django form returns empty cleaned_data

I have a simple Form, view and a html that renders the form. but the problem is that the form always returns form.is_valid == False.
So I have checked the cleaned data but I noticed that self.cleaned_data returns an empty list.
Here is the relevant code:
class GraphForm(forms.Form):
from_month = forms.DateField(widget=forms.Select(choices=MONTHS))
from_year = forms.DateField(widget=forms.Select(choices=YEARS))
to_month = forms.DateField(widget=forms.Select(choices=MONTHS))
to_year = forms.DateField(widget=forms.Select(choices=YEARS))
def clean(self):
return self.cleaned_data <<< will always stay be empty
def showgraph(request):
if request.method == 'POST':
form = GraphForm(request.POST)
if form.is_valid():
>>> will never happen <<<
...
...
...
else:
form = GraphForm()
return render(request, 'graph.html', {"form": form})
<form method="post">
{% csrf_token %}
{{ form.from_month }}
{{ form.from_year }}
<br>
{{ form.to_month }}
{{ form.to_year }}
<br>
<p align="center">
<button type="submit" class="btn btn-primary">send</button>
</p>
</form>
Can anyone help with this peculiar problem?
The <form> tag should have action besides method so that the submit button can work, like this.
<form action="{% url 'name_of_the_view' %}" method="post">
...
</form>
If the code doesn't reach inside form.is_valid(), then it means the form is not valid, add an else to if and print the form.errors() and return the same form to template also add form error in the template to see the errors.
def showgraph(request):
if request.method == 'POST':
form = GraphForm(request.POST)
if form.is_valid():
>>> will never happen <<<
else:
print(form.errors())
else:
form = GraphForm()
return render(request, 'graph.html', {"form": form})
Add error for each field:
<span class="text-danger">{{field.errors.as_text|cut:'* '}}</span>

request.FILES empty, though file is present in request

Following the example on the django website I'm trying to upload a file, perform checks on the contents, then feedback to the user and store the file contents.
However, I'm having trouble with the request.FILES which is always empty. My code is as follows (note the output after the print statements):
**forms.py**
class UploadFileForm(forms.Form):
data_import = forms.FileField()
class Meta:
model = Recipe
fields = ('data_import',)
**view**
def recipes_list(request):
template = 'recipes/recipes_list.html'
if request.method == 'GET':
user = request.user
queryset = Recipe.objects.filter(user=user)
form = UploadFileForm()
return render(request, 'recipes/recipes_list.html', context={'recipes': queryset, 'form': form})
elif request.method == 'POST':
print(request.FILES) # <MultiValueDict: {}>
print(request.POST) # <QueryDict: {'csrfmiddlewaretoken': ['...'], 'data_import': ['recette.json']}>
form = UploadFileForm(request.POST, request.POST.data_import)
if form.is_valid():
return HttpResponseRedirect(template)
else:
print(form.errors)
**template**
<form method="post">
{% csrf_token %}
{{ form }}
<button type="submit">submit</button>
</form>
The error I'm getting is:
<ul class="errorlist"><li>data_import<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
But i can see that the file is uploaded, and is in the request.POST.get('data_import').
I would like to run validation on the form, but I can't do this if request.FILES is empty.
I'm clearly doing something wrong, can someone please point me in the right direction?
<form method="post" enctype= multipart/form-data>
{% csrf_token %}
{{ form }}
<button type="submit">submit</button>
</form>
change your form to the upper one

How to redirect to previous page in Django after POST request

I face a problem which I can't find a solution for. I have a button in navbar which is available on all pages and it is a button responsible for creating some content.
View that links with button:
def createadv(request):
uw = getuw(request.user.username)
if request.method =='POST':
form = AdverForm(request.POST, request.FILES)
if form.is_valid():
form.instance.user = request.user
form.save()
return HttpResponseRedirect('/', {'username': request.user.username, 'uw': uw})
args = {}
args.update(csrf(request))
args['username'] = request.user.username
args['form'] = AdverForm()
args['uw'] = uw
return render_to_response('createadv.html', args)
If you can see now I always redirect to main page '/' after creating content but I want to go back to the page with which I launched the creation of content.
You can add a next field to your form, and set it to request.path. After you processed your form you can redirect to the value of this path.
template.html
<form method="POST">
{% csrf_token %}
{{ form }}
<input type="hidden" name="next" value="{{ request.path }}">
<button type="submit">Let's Go</button>
</form>
views.py
next = request.POST.get('next', '/')
return HttpResponseRedirect(next)
This is roughly what django.contrib.auth does for the login form if I remember well.
If you pass through an intermediate page, you can pass the 'next' value via the querystring:
some_page.html
Go to my form!
template.html
<form method="POST">
{% csrf_token %}
{{ form }}
<input type="hidden" name="next" value="{{ request.GET.next }}">
<button type="submit">Let's Go</button>
</form>
You can use the HTTP_REFERER value:
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
Note that this will not work if the client disabled sending referrer information (for example, using a private/incognito browser Window). In such a case it will redirect to /.
You can use this
return redirect(request.META.get('HTTP_REFERER'))
Make sure to import this
from django.shortcuts import redirect
My favorite way to do that is giving the request.path as GET parameter to the form.
It will pass it when posting until you redirect.
In Class-Based-Views (FormView, UpdateView, DeleteView or CreateView) you can directly use it as success_url.
Somewhere i read that it's bad practise to mix GET and POST but the simplicity of this makes it to an exception for me.
Example urls.py:
urlpatterns = [
path('', HomeView.as_view(), name='home'),
path('user/update/', UserUpdateView.as_view(), name='user_update'),
]
Link to the form inside of the template:
Update User
Class-Based-View:
class UserUpdateView(UpdateView):
...
def get_success_url(self):
return self.request.GET.get('next', reverse('home'))
In your function based view you can use it as follows:
def createadv(request):
uw = getuw(request.user.username)
if request.method =='POST':
form = AdverForm(request.POST, request.FILES)
if form.is_valid():
form.instance.user = request.user
form.save()
next = request.GET.get('next', reverse('home'))
return HttpResponseRedirect(next)
args = {}
args.update(csrf(request))
args['username'] = request.user.username
args['form'] = AdverForm()
args['uw'] = uw
return render_to_response('createadv.html', args)
you could do this easily with a simple one-liner JS
<button onclick="history.back()">Go Back</button>
This will take you back to the previous page of your history list.
If you don't have a history
https://www.w3schools.com/jsref/met_his_back.asp
Use HTTP_REFERER value:
for use in func return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
for use in template Go Back
In case this helps someone I got this to work in class based UpdateView
template
<form class="form" method="POST">
{% csrf_token %}
<!-- hidden form field -->
<input type="hidden" id="previous_page" name="previous_page"
value="/previous/page/url">
<!-- any other form fields -->
{{ form.name|as_crispy_field }}
{{ form.address|as_crispy_field }}
<!-- form submit button -->
<button class="btn btn-primary" type="submit" id="submit">Submit</button>
</form>
<!-- JS to insert previous page url in hidden input field -->
<script>
prev = document.getElementById("previous_page");
prev.value = document.referrer;
</script>
views.py
class ArticleUpdateView(generic.UpdateView):
model = Article
form_class = ArticleForm
template_name = 'repo/article_form.html'
def form_valid(self, form):
form.instance.author = self.request.user
# if form is valid get url of previous page from hidden input field
# and assign to success url
self.success_url = self.request.POST.get('previous_page')
return super().form_valid(form)
The view now redirects you back to the page where you had clicked the "Update/Edit" button. Any URL query parameters are also preserved.

Form is not rendered in the template after redirection (django)

I have a view and its template that handles and prints a form. The form has a ChoiceField that takes a list of models as choices. Here is my view, template and form:
*views.py*
def index(request):
form = dbForm()
print "form is: ", form
return render(request, 'Directories/index.html', {'form':form})
*index.html*
<div id="content" align="center">
<form action="" method="get"> {% csrf_token %}
{{form.as_p}}
<input type="submit" value="Edit" name="_add" />
</form>
*forms.py*
model_classes = []
class dbForm(forms.Form):
model_classes_field = forms.ChoiceField(choices=models())
def models():
apps = get_app('Directories')
for model in get_models(apps):
model_classes.append( (model._meta.verbose_name, model._meta.db_table), )
return model_classes
The model choice submitted is sent to another view where a ModelForm displays the model's fields and expects data for each of the fields to be submitted. The submitted data are then stored in the database and the user is redirected back to the index to start again from the beginning. Here is the view, template and form:
*views.py*
def modelUpdate(request):
if 'update' in request.POST: # If the form has been submitted...
form_class = get_dynamic_form(request.GET['model_classes_field'])
form = form_class(request.POST)
if form.is_valid(): # All validation rules pass
row = form.save() #saves into database
return render(request, 'Directories/index.html')
else:
print "form errors: ", form.errors
return HttpResponse('ERROR -- Return to form submission')
*create.html*
<form action="" method="post"> {% csrf_token %}
{% for f_name in field_names %}
{% if not forloop.first %}
{{f_name}}: <input id="edit-{{f_name}}" type="text" name={{f_name}} /><br />
{% endif %}
{% endfor %}<br />
<input type="submit" name="update" value="Update" />
<input type="reset" name="Clear" value="Clear" />
</form>
*forms.py*
#create a ModelForm using a dynamic model
def get_dynamic_form(c_model):
model_class = get_model('Directories', c_model)
class ObjForm(forms.ModelForm ):
class Meta:
model = model_class
return ObjForm
The problem occurs when the form is redirected back to the index.html return render(request, 'Directories/index.html') after the data have been saved into the database. What happens is that the index.html does not display the form {{form.as_p}}at all. Although when i check print "form is: ", form in my server (Apache) error.log, my form is there printed as it should be.
I cannot understand why the data are not rendered in my template after the redirection occurs but still they are displayed correctly in my server log.
You should pass the form instance to your template as you do in index view. Your code shall be updated to
def modelUpdate(request):
if 'update' in request.POST: # If the form has been submitted...
form_class = get_dynamic_form(request.GET['model_classes_field'])
form = form_class(request.POST)
if form.is_valid(): # All validation rules pass
row = form.save() #saves into database
#------------------------------------------------v pass it to template
return render(request, 'Directories/index.html', {'form': form})
else:
print "form errors: ", form.errors
return HttpResponse('ERROR -- Return to form submission')