Handling file submissions with html and python - python

I'm trying to create a form that will handle video file uploads from the user, but I'm running into a bunch of problems. I am trying to avoid building a model form for this because I won't be saving the file to my database long term. I just want to get it to the server so that I can submit it to youtube. The html I have is:
<form method='post' action='/new/' enctype="multi-part/form-data">{% csrf_token %}
<input type='file' name='file' id='file'/>
<input type='submit' />
</form>
and then the view attempts to handle the file like so:
def create_video(request):
if request.method == 'POST':
video = request.POST['file']
command=subprocess.Popen('youtube-upload --email=' + email + ' --password=' + password + '--title=' + title + ' --description=' + description + ' --category=Sports ' + video, stdout=subprocess.PIPE)
vid = command.stdout.read()
# do stuff to save video instance to database
return show_video(request, video.id)
else:
form=Video()
return render_to_response('create_video.html', RequestContext(request, locals()))
note: youtube-upload is a python module to upload videos to youtube with that given command.
So for starters when I submit the form from the front end django sends a message saying "Key 'file' not found in <QueryDict:...
and given that I fix the form so that it will submit properly is the rest of the view properly handling the file?

request.POST doesn't contain file upload information. You need to use request.FILES.

Related

When handling a form POST in a Django views.py, it appears to ignore the HttpResponse type

I have a Django application that generates a table of data. I have a form where you enter parameters and click one button to see the results or another to download a CSV. Seeing the results is working, but downloading the CSV is not.
I handle the response in the views.py, set the content type and disposition, and return the response. Rather than downloading the CSV, it displays the data as text. (I tried both StreamingHttpResponse and plain HttpResponse.) The same exact code works when handling a URL passing in the parameters. So, I tried a HttpResponseRedirect instead, and it does nothing. I even tried just redirecting to a plain URL, with no effect. I believe the response type is being ignored, but I don't know why.
html:
<form action="" method="post" class="form" id="form1">
{{ form.days }} {{ form.bgguserid }}
<input type="submit" value="Go!" id="button-blue"/>
<input type="submit" name="csv-button" value="CSV" id="csv-button"/>
</form>
views.py attempt 1:
def listgames(request, bgguserid, days=360):
if 'csv-button' in request.POST:
# create CSV in variable wb
response = StreamingHttpResponse(wb, content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="collectionvalue.csv"'
return response
attempt 2, the same but with:
response = HttpResponseRedirect ('/collection/{0}/csv/{1}/'.format(bgguserid,days))
I'm open to other solutions like a client-side redirect to the functioning URL, but I don't want to lose the form validation, and my HTML/javascript skills are weak.
I figured out the problem.
The code in views.py (which I partly copied from somewhere) was creating a new HttpRequest object from the return value of the form handling method.
def indexform(request):
if request.method == 'POST':
form = IndexForm(request.POST)
# Check if the form is valid:
if form.is_valid():
# process the data in form.cleaned_data as required
response = listgames(request, bgguserid=form.cleaned_data['bgguserid'], days=form.cleaned_data['days'])
# redirect to a new URL:
return HttpRequest(response)
By changing that last line to just return response, it works as intended. Sorry for wasting anyone's time.

Django - how to get file input without using django form and attach the file to email

I am trying to get file input from a html form without using django form and then attach that file to email.
My HTML:
<form method="post" action="{% url 'foo' %}" enctype="multipart/form-data">
<input type="file" name="file1"/>
</form>
My views.py:
def foo(request):
if not request.FILES['file1']:
return render(request, 'index.html', {})
email_msg = EmailMessage(subject="email subject", body="email body",
from_email="email#adress", to=["email#adress"])
email_msg.attach_file(request.FILES['file1'])
email_msg.send()
return render(request, 'needs-confirmation.html', context
I have two questions. First, in the function foo, I put the if statement to first check if the user has put the file and that file1 exists. This works fine when a file is uploaded as file1 but, this gives an error when file1 does not have any file input. How can I check if file1 exists or not? Second question is when I am trying to attach file to email_msg, attach_file function does not work, giving this error:
'InMemoryUploadedFile' object has no attribute 'replace'
How can I get file from html form and attach the file to an email?
Thank you.
Your code should look something like this:
def foo(request):
#need to check that form was submitted
if request.method == "POST":
#this checks that a file exists
if len(request.FILES) != 0:
file1 = request.FILES['file1']
file1str = file1.read()
file_type = str(request.FILES['file1'].content_type)
email_msg = EmailMessage(subject="email subject", body="email body",
from_email="...", to=["..."])
#need to try to attach the file, using the attach method
try:
email_msg.attach('file1', file1str, file_type)
except Exception as e:
print(str(e))
email_msg.send()
return render(request, '/needs-confirmation.html', {})
You will need to fill in the emails again.
You've left out a lot of key steps, and your HTML needs a submit button and crsf_token. This works for text files, you may need to do some more processing for other file types.
Hope this helps.

django how to upload folder

I know how to upload multiple files through django, but I have a problem when uploading a folder if there are subfolders in it. The django can't receive subfolders. I found the reason, because browser use '.' to represent a folder, but django can't parse it then stop parsing. Is there an elegant way to fix it?
python code:
def uploader_single(request):
data = {}
if request.method == 'POST':
if True:
for afile in request.FILES.getlist('file'):
new_file = UploadFileSingle(file = afile)
new_file.save()
return HttpResponseRedirect('')
else:
print "form is not valid"
return HttpResponseRedirect('')
else:
print 'not post'
Python code:
class UploadFileSingle(models.Model):
file = models.FileField(upload_to='files/%Y/%m/%d', models.FilePath)
uploaded_at = models.DateTimeField(auto_now_add=True)
models.FilePathField.recursive = True
models.FilePathField.allow_folders = True
updated_at = models.DateTimeField(auto_now=True)
def some_folder = FilePathField(path='some_path', recursive=True, allow_files=True, allow_folders=True,)'
HTML code:
<input type="file" name="file" multiple = "true" webkitdirectory="true" directory = "true"/>
There is newer topic which asks the same question and I have tried to answer:
Django directory upload get sub-directory names
Basically it is the default behavior of Django if you want to have different behavior you need to write your own upload handlers
I came up with easy solution for this problem.
You could get folder name via html and javascript in frontend
pass it as a value to hidden form field
in backend you can create a directory with that name
and upload files in this directory.
HTML
<input type="hidden" name="dir_name" id="id_dir_name">
<input type="file" name="file" onchange="selectFolder(event)" webkitdirectory="" multiple="" required="" directory="" id="id_file">
JS
function selectFolder(e) {
var theFiles = e.target.files;
var relativePath = theFiles[0].webkitRelativePath;
var folder = relativePath.split("/");
$("#" + id).val(folder[0]);
}
views
directory_name = form.cleaned_data['dir_name']
os.mkdir(os.path.join(settings.MEDIA_ROOT, directory_name))
handle_uploaded_file(request.FILES['file'], directory_name)
you can use django filer
rerfer:https://github.com/stefanfoulis/django-filer

Getting error: Could not find session for *blob_key*

Im trying to make a form where users can upload a file. And I have a form that takes the users arguments and posts them to the UploadHandler that is mapped to the url that is passed as parameter to the renderfunction. It works up to the point that it renders the form, but when submitting the file it redirects me to a blank page and the console shows me the error:
ERROR 2013-03-17 11:53:30,769 dev_appserver_blobstore.py:404] Could not find session for ahhkZXZ-a3Vyc3NhbW1hbmZhdHRuaW5nYXJyGwsSFV9fQmxvYlVwbG9hZFNlc3Npb25fXxgxDA
INFO 2013-03-17 11:53:30,779 dev_appserver.py:3104] "POST /_ah/upload/ahhkZXZ- a3Vyc3NhbW1hbmZhdHRuaW5nYXJyGwsSFV9fQmxvYlVwbG9hZFNlc3Npb25fXxgxDA HTTP/1.1" 404 -
Im still new at this but I can't seem to find whats going on. I understand that the mappings somehow f***s up, but still I don't understand why it doesn't redirect me to the correct handler (SummaryHandler)?
The class that serves the form for file upload:
class CreateHandler(BaseHandler):
def get(self):
self.render('create.html', upload_url = blobstore.create_upload_url('/upload'))
The html-form ('create.html'):
<h2 class="main-title">Upload a file!</h2>
<form action="{{upload_url}}" method="post" enctype="multipart/form-data">
<label>
<div>Upload:</div>
<input type="file" name="file" accept="application/pdf"><br>
</label>
<div class="error">
{{file_error}}
</div>
<br>
<input type="submit" name="submit" value="Submit">
</form>
The uploadhandler that takes the form args:
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
def post(self):
upload_files = self.get_uploads('file')
blob_info = upload_files[0]
#validation of form-vars
have_error=False
file_data=dict()
if not (blob_info.content_type == 'application/pdf'):
file_data['file_error']="You can only upload files in pdf-format."
have_error=True
if have_error:
self.render('create.html', **file_data)
else:
#'parent' is just some stuff for organising the database.
#Not important in this context
the_file = A_File(parent = summary_key(),
file_key=str(blob_info.key()))
the_file.put()
if the_file:
self.redirect('/summary/%s' % str(the_file.key().id()))
else:
_error="Could not find the file"
self.render('welcome.html', error=_error)
The application handlers and their mappings:
app = webapp2.WSGIApplication([
(r'/', WelcomeHandler),
(r'/create', CreateHandler),
(r'/upload', UploadHandler),
(r'/summary/(\d+)', SummaryHandler) #I have not included this handler in the text
], debug=True)
It might be the fact that this session expired or it was used. The urls that created using create_upload_url have an expiration (I think 10 min). Before actually posting you might want to refresh that URL using JavaScript.
Also if for any reason the file was uploaded in the Blobstore you won't be able to use the same URL again, it is only good for one request (could have multiple files though).
In your example, try to remove all the file specific checks, try to upload something and check through the administrative console in the Datastore Viewer if you have any blobs or session keys. This can be accessed through this URL:
http://localhost:8080/_ah/admin/datastore

POSTing forms in Django's admin interface

I'm writing a Django admin action to mass e-mail contacts. The action is defined as follows:
def email_selected(self,request,queryset):
rep_list = []
for each in queryset:
reps = CorporatePerson.objects.filter(company_id = Company.objects.get(name=each.name))
contact_reps = reps.filter(is_contact=True)
for rep in contact_reps:
rep_list.append(rep)
return email_form(request,queryset,rep_list)
email_form exists as a view and fills a template with this code:
def email_form(request,queryset,rep_list):
if request.method == 'POST':
form = EmailForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
send_mail(
cd['subject'],
cd['message'],
cd.get('email','noreply#localboast'),['redacted#email.com'],
)
return HttpResponseRedirect('thanks')
else:
form = EmailForm()
return render_to_response('corpware/admin/email-form.html',{'form':form,})
and the template exists as follows:
<body>
<form action="/process_mail/" method="post">
<table>
{{ form.as_table }}
</table>
<input type = "submit" value = "Submit">
</form>
</body>
/process_mail/ is hardlinked to another view in urls.py - which is a problem. I'd really like it so that I don't have to use <form action="/process_mail/" method="post"> but unfortunately I can't seem to POST the user inputs to the view handler without the admin interface for the model being reloaded in it's place (When I hit the submit button with , the administration interface appears, which I don't want.)
Is there a way that I could make the form POST to itself (<form action="" method="post">) so that I can handle inputs received in email_form? Trying to handle inputs with extraneous URLs and unneeded functions bothers me, as I'm hardcoding URLs to work with the code.
You can use django's inbuilt url tag to avoid hardcoding links. see...
http://docs.djangoproject.com/en/dev/ref/templates/builtins/#url
Chances are you'd be better off setting up a mass mailer to be triggered off by a cron job rather than on the post.
Check out the answer I posted here
Django scheduled jobs
Also if you insist on triggering the email_send function on a view update perhaps look at
http://docs.djangoproject.com/en/dev/topics/signals/

Categories