Django upload only csv file - python

I am trying to import csv file i am able to import without any problem but the present functionality accepts all file types, i want the functionality to accept only csv file. below is the view.py and template file.
myapp/views.py
def list(request):
# Handle file upload
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
importing_file(request.FILES['docfile'])
myapp/templates/myapp/index.html
<form action="{% url 'ml:list' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ form.non_field_errors }}</p>
<p>{{ form.docfile.label_tag }} {{ form.docfile.help_text }}</p>
<p>
{{ form.docfile.errors }}
{{ form.docfile }}
</p>
<p><input type="submit" value="Upload"/></p>
</form>
EDIT
I could find a workaround by adding validate_file_extension as per the
django documentation
myapp/forms.py
def validate_file_extension(value):
if not value.name.endswith('.csv'):
raise forms.ValidationError("Only CSV file is accepted")
class DocumentForm(forms.Form):
docfile = forms.FileField(label='Select a file',validators=[validate_file_extension])

form widget to validate file extension
csv_file = forms.FileField(widget=forms.FileInput(attrs={'accept': ".csv"}))

added code snippet to the forms.py file to validate file extension and now it is working fine.
def validate_file_extension(value):
if not value.name.endswith('.csv'):
raise forms.ValidationError("Only CSV file is accepted")
class DocumentForm(forms.Form):
docfile = forms.FileField(label='Select a file',validators=[validate_file_extension])

Related

wtforms/flask_wtf validator: FileAllowed, not catching forbidden file extensions

Observed Issue
I am trying to validate file extensions on a FileField field. The form successfully validates even when I pass files with extensions not included in the validator constructor. In this example I am using a test .pdf file which is not blank:
validators=[FileRequired(),FileAllowed(['jpg', 'png'], 'Images only!')]
form contents:
data = <FileStorage: 'test.pdf' ('application/pdf')>
errors = ()
Code
route
from flask import Flask, render_template
from forms import ConsoleUploadForm
app.config['SECRET_KEY'] = 'superdupersecret'
#app.route("/")
#app.route("/home", methods=['GET', 'POST'])
def home():
form = ConsoleUploadForm()
if form.validate_on_submit:
# this is just my debug statement
print(f"validated with -> {form.console_log_file.data} {form.console_log_file.errors}")
return render_template("home.html", form=form)
form class
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileAllowed, FileRequired
class ConsoleUploadForm(FlaskForm):
console_log_file = FileField("Choose File...", render_kw={"class": "custom-file-input"}, validators=[
FileRequired(), FileAllowed(['jpg', 'png'], 'Images only!')])
HTML
<html>
<form method="POST" action="/home" enctype="multipart/form-data">
{{ form.hidden_tag() }}
<legend>Log Selection:</legend>
<div class="form-row">
<div class="form-group col-md-6">
<label for="console_log_file">Console Log File</label>
<div class="input-group mb-3">
<div class="custom-file">
{{ form.console_log_file(class="custom-file-input") }}
{{ form.console_log_file.label(class="custom-file-label") }}
</div>
</div>
</div>
{% if form.console_log_file.errors %}
<div class="invalid-feedback">
{% for error in form.console_log_file.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% endif %}
</div>
</form>
</html>
Desired Outcome
On pressing submit having passed a forbidden file extension type to the FileField I would expect to see elements in forms.console_log_file.errors and see them in my debug print statement.
What am I missing here?
This is a case of the handwritten typo... left out the parenthesis when trying to call the validate_on_submit method.
route
if form.validate_on_submit: ---becomes---> if form.validate_on_submit():

upload a specific file form Django/Python

I just have a upload file form in my django project (I'm using 1.10). Everything is working pretty fine actually, but as people will be uploading a csv file into the server I need to limit my logic into just request the file with a specific name and format.
This is my code:
views.py
def list(request):
# Handle file upload
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(docfile = request.FILES['docfile'])
newdoc.save()
# Redirect to the document list after POST
return HttpResponseRedirect(reverse('action'))
else:
messages.add_message(request, messages.ERROR,
'Something went wrong with files! Please contact the system administrator')
return render(request, 'upaction-report.html')
# Load documents for the list page
# Render list page with the documents and the form
return render(request, 'upaction-report.html', {'form': form})
forms.py
class DocumentForm(forms.Form):
docfile = forms.FileField(
label='File format should be under the following format:',
help_text='- .csv format and under the name "Action_Report"',
)
template html
<form action="{% url "list" %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ form.non_field_errors }}</p>
<p>{{ form.docfile.label_tag }}</p>
<p> {{ form.docfile.help_text }}</p>
<p>
{{ form.docfile }}
</p>
<p><input type="submit" value="Upload File"/></p>
</form>
Any guess on how applying that limit? In case the file is not the right just to pop up a message telling to try again until it has the right format and name. Thanks for yout time, if you need more details just let me know.
You can validate filename inside DocumentForm clean method like this:
class DocumentForm(forms.Form):
docfile = forms.FileField(
label='File format should be under the following format:',
help_text='- .csv format and under the name "Action_Report"',
)
def clean(self):
docfile = self.cleaned_data.get('docfile')
if not docfile:
raise forms.ValidationError('file is required')
if not docfile.name.startswith('filename'):
raise forms.ValidationError('incorrect file name')
if not docfile.name.endswith('fileformat'):
raise forms.ValidationError('incorrect file format')
return super(DocumentForm, self).clean()

Django upload and process file with no data retention

Python: 2.7.11
Django: 1.9
I want to upload a csv file to Django and analyze it with a Python class. No saving is allowed and the file is only needed to reach the class to be analyzed. I'm using Dropzone.js for the form but I don't understand how I should configure/program the views to achieve this.
<form action="/upload/" method="post" enctype="multipart/form-data" class="dropzone" id="dropzone">
{% csrf_token %}
<div class="fallback">
<input name="file" type="file" multiple />
</div>
</form>
I have found an article about this but it describes saving and is based on Django 1.5.
view.py
def upload(request):
if request.method == 'POST':
file = FileUploadForm(request.POST)
if file.is_valid():
return HttpResponseRedirect('/upload/')
else:
file = FileUploadForm()
return render(request, 'app/upload.html', {'file': file})
forms.py
from django import forms
class FileUploadForm(forms.Form):
file = forms.FileField()
Closing Update:
The most important difference between the helping answer and my situation is that I had to decode my input.
See the following line as mine csv_file in handle_csv_data:
StringIO(content.read().decode('utf-8-sig'))
Access the csv file in the view function. If you are using python 3, you must wrap the InMemoryUploadedFile in a TextIOWrapper to parse it with the csv module.
In this example the csv is parsed and passed back as a list named 'content' that will be displayed as a table.
views.py
import csv
import io # python 3 only
def handle_csv_data(csv_file):
csv_file = io.TextIOWrapper(csv_file) # python 3 only
dialect = csv.Sniffer().sniff(csv_file.read(1024), delimiters=";,")
csv_file.seek(0)
reader = csv.reader(csv_file, dialect)
return list(reader)
def upload_csv(request):
csv_content=[]
if request.method == 'POST':
csv_file = request.FILES['file'].file
csv_content = handle_csv_data(csv_file)
return render(request, 'upload.html', {'content':content})
Your original code did not use django's form framework correctly, so I just dropped that from this example. So you should implement error handling when the uploaded file is invalid or missing.
upload.html
<form action="/upload/"
method="post"
enctype="multipart/form-data"
class="dropzone"
id="dropzone">
{% csrf_token %}
<div class="fallback">
<input name="file" type="file"/>
<input type="submit"/>
</div>
</form>
{% if content %}
<table>
{% for row in content %}
<tr>
{% for col in row %}
<td>{{ col }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
{% endif %}
I've added a 'submit' button so this works without the dropzone thing. I also removed 'multiple' from the file input, to keep the example simple. Finally there's a table if the template receives content from a parsed csv. But when using dropzone.js, you have to use a javascript callback function to display the table.

Uploading excel data into django without saving the file

I am a newbie to django and i'm desperate for help uploading and reading excel data without actually saving the data on the machine. I have written some code and taken some from the research i've done online.
These are my questions:
1. How do I Upload an excel file (without it being saved on the machine). I just want the excel file to populate some django fields and not save it.
How do I make django read the columns in the excel file and feeds into some other fields on another page. (How do I link them up?)
Most docs I've seen require me to hard-code the name of the excel file and its locations.IS there a way around this as I don't know where the user might be uploading from.
pls advice.
My views.py:
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from credit.models import Document
from credit.forms import DocumentForm
def list(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(docfile = request.FILES['docfile'])
newdoc.save()
return HttpResponseRedirect(reverse('credit.views.list'))
else:
form = DocumentForm()
documents = Document.objects.all()
return render_to_response('credit/list.html',
{'documents': documents, 'form': form},
context_instance=RequestContext(request)
)
My models.py is:
class Document(models.Model):
docfile = models.FileField(upload_to='documents/')
#these are the models I want the excel columns to feed into
policies = DecimalNumberField()
capital = DecimalNumberField()
inflation = DecimalNumberField()
My forms.py is:
import os
import xlrd
IMPORT_FILE_TYPES = ['.xls', ]
class DocumentForm(forms.Form):
docfile = forms.FileField(label='Select a file')
def clean(self):
data = super(DocumentForm, self).clean()
if 'docfile' not in data:
raise forms.ValidationError(_('The Excel file is required to proceed'))
docfile = data['docfile']
extension = os.path.splitext(docfile.name)[1]
if not (extension in IMPORT_FILE_TYPES):
raise forms.ValidationError(u'%s is not a valid Excel file. Please make sure your input file is an Excel file )' % docfile.name)
file_data = StringIO.StringIO()
for chunk in docfile.chunks():
file_data.write(chunk)
data['file_data'] = file_data.getvalue()
file_data.close()
try:
xlrd.open_workbook(file_contents=data['file_data'])
except xlrd.XLRDError, e:
raise forms.ValidationError(_('Unable to open XLS file: %s' % e))
return data
#i do not want to do this (specify the exact file name). Need an alternative
sh = xlrd.open_workbook('documents\june.xls').sheet_by_index(1)
inflation = open("inflation.txt", 'w')
policies= open("policies.txt", 'w')
capital= open("access_to_finance.txt", 'w')
try:
for rownum in range(sh.nrows):
inflation.write(str(rownum)+ " = " +str(sh.cell(rownum, 1).value)+"\n")
policies.write(str(rownum)+ " = " +str(sh.cell(rownum, 2).value)+"\n")
capital.write(str(rownum)+ " = " +str(sh.cell(rownum, 3).value)+"\n")
finally:
inflation.close()
policies.close()
capital.close()
Then I have a list.html file:
{% if documents %}
<ul class="nav nav-tabs">
{% for document in documents %}
<li>{{ document.docfile.name }}</li>
{% endfor %}
</ul>
{% else %}
<p>Click Upload to go to Upload page</p>
{% endif %}
<form action="{% url list %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ form.non_field_errors }}</p>
<p>{{ form.docfile.label_tag }} {{ form.docfile.help_text }}</p>
<p>
{{ form.docfile.errors }}
{{ form.docfile }}
</p>
<p><input type="submit" value="Upload" /></p>
</form>
Answer for question 1:
If your upload file is less then FILE_UPLOAD_MAX_MEMORY_SIZE(2.5MB), django puts the uploaded file in memory. If your file is bigger than 2.5MB, you can change FILE_UPLOAD_MAX_MEMORY_SIZE in your settings file

'Document' object is not subscriptable Django

I am getting this error while uploading the excel file and saving that file.
My model.py file
class Document(models.Model):
docfile = models.FileField(upload_to='documents/%Y/%m/%d')
In view i am saving that file to specific location
views.py file is
def excel(request):
print "you in main"
if request.method == 'POST':
print "you in post"
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(docfile = request.FILES['docfile'])
path = os.path.join(settings.MEDIA_ROOT, newdoc)
print path
print "you in"
newdoc.save()
wb = xlrd.open_workbook(path)
sh = wb.sheet_by_index(0)
c = 1
while c < len(sh.col(0)):
first = sh.col_values(0)[c]
second = sh.col_values(1)[c]
c=c+1
return HttpResponseRedirect(reverse('upload.views.excel'))
else:
form = UploadFileForm() # A empty, unbound form
documents = Document.objects.all()
return render_to_response('property/list.html',{'documents': documents, 'form': form},context_instance=RequestContext(request))
My html file is
<!-- List of uploaded documents -->
{% if documents %}
<ul>
{% for document in documents %}
<li>{{ document.docfile.name }}</li>
{% endfor %}
</ul>
{% else %}
<p>No documents.</p>
{% endif %}
<!-- Upload form. Note enctype attribute! -->
<form action="/property/excel/" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ form.non_field_errors }}</p>
<p>{{ form.docfile.label_tag }} {{ form.docfile.help_text }}</p>
<p>
{{ form.docfile.errors }}
{{ form.docfile }}
</p>
<p><input type="submit" value="Upload" /></p>
</form>
Error i am getting is
TypeError at /property/excel/
'Document' object is not subscriptable
Request Method: POST
Request URL: http://127.0.0.1:8000/property/excel/
Django Version: 1.5
Exception Type: TypeError
Exception Value:
'Document' object is not subscriptable
Exception Location: C:\Python27\lib\ntpath.py in splitdrive, line 125
Please help me out to solve this problem
your problem is here:
path = os.path.join(settings.MEDIA_ROOT, newdoc)
^ newdoc is a model instance and you don't want the model itself to be a part of your path - you want the uploaded file path there, thus:
path = os.path.join(settings.MEDIA_ROOT, newdoc.docfile)

Categories