Upload jpg with flask and html - python

I'm a beginner and i'm making a blog with flask and html right now but it only can post title and content, but i want to post some picture init, so if anyone know whats the essayist way to post it (print the pic on flask app) and can storage in db file, can u please help me out? Because i'm stuck in this for so long.

If you are using a flask form to upload a file, then from flask_wtf.file import FileField can be used as the form field to upload files.
class UploadImageForm(Form):
file = FileField(label='File Upload')
submit = SubmitField('Submit')
On the HTML side you will want:
<form action="{{ url_for(your.route, **kwargs) }}" method="POST" enctype = "multipart/form-data">
{{ form.name_of_file_field }}
{{ form.submit }} <!-- Or an input tag -->
</form>
Then your route that will be called when the form is submitted must require a POST method. In development, you can store your files locally within the static folder, but I would advise to store them in remote storage before putting the app on a server.
Once the file is stored and uploaded, then in your html you can render the image with:
<img class="image" src="{{ url_for('static', filename=image_file_path) }}"> where image_file_path is the path to the image. This path should be stored as an environment variable. The image file name can be stored on your db within one of your models data columns as a string. I suggest creating UUID's as the image file name so you are not dependent on the user's upload (i.e. if the filename is in a different language).
Miguel Grinberg has a great tutorial on this that will cover all of this and more including working with image objects, image validation, and security: https://blog.miguelgrinberg.com/post/handling-file-uploads-with-flask

Related

Download file stored in a model with FileField using Django

I'm planning on creating a simple file upload application in Django (incorporated in some bigger project). For this one, I'm using two links: /files/ - to list all the uploaded files from a specific user, and /files/upload/ - to display the upload form for the user to add a new file on the server.
I'm using a PostgreSQL database, and I'm planning to store all my files in a File model, that looks like this:
class File(models.Model):
content = models.FileField()
uploader = models.ForeignKey(User, on_delete=models.CASCADE)
My file upload view looks like this:
#login_required
def file_upload_view(request):
if request.method == "POST" and request.FILES['file']:
file = request.FILES['file']
File.objects.create(content=file, uploader=request.user)
return render(request, "file_upload_view.html")
else:
return render(request, "file_upload_view.html")
while my file list view looks like this:
#login_required
def file_view(request):
files = File.objects.filter(uploader = request.user)
return render(request, "file_view.html", {'files': files})
The problem is, I want my file_view() to display all files uploaded by a certain user in a template (which I already did), each file having a hyperlink to its download location. I've been trying to do this in my file list template:
{% extends 'base.html' %}
{% block content %}
<h2>Files of {{ request.user }}</h2>
<br>
{% for file in files %}
<a href="{{ file.content }}" download>{{ file.content }}</a>
{% endfor %}
{% endblock %}
but my browser is trying to download some .htm page, even if the file I've uploaded is of type .txt.
My question is: do I also need to upload the file in a certain directory, before adding it to the model? Do I need to actually add the file's URL as a new field in my model? Aren't the files automatically uploaded to my PostgreSQL database?
Any help would be greatly appreciated.
Thank you.
I guess you should show a folder to where you want to save your files like this:
content = models.FileField(upload_to='/myfiles/')
And then you can show the url ("{% url 'file.url' %}") in your html because the database actually saves the url of your file and stores the file itself in the folder you've showed in your model.

Django -- Dynamically showing Images that are created daily

What I want to accomplish is have an separate application generate graphs that will be placed in a directory of my Django project. Then in my template loop through and display all the graphs from that directory on the webpage. The generated graphs will be refreshed daily and will be of varying numbers depending on the day (so some days it could be 20 and/or the next it could be 50).
Option 1)
I don't think I want to use django manage.py collectstatic everyday. I've never gotten to the point of deploying a Django project to a server but my assumption is that I would have to collectstatic manually everyday on the server (which I could be thoroughly off on here)
Option 2)
I've tried creating a model Model Picture. The Char field is just my relative path to the picture. From there in my views I'm to rendering the path in the index(request) View Picture. From there in my template I'm trying to loop through the picture relative paths and show them Template Picture. When I remove photos = Photo.objects.all() & the {% for img in photos %} in the template the page does load Webpage working. However when I run it with all those parts in there I get a blank webpage with "what do you want to do with index" at the bottom. Webpage not working
Interested to hear if there are any better way of doing this . I'm not trying to user upload an image field because the amount of graphs will be substantial.
Details on Option 2:
---Notes---
Top Lvl Directory is--chartingtest-project
chartingtest is the project
sectors is the name of the app
media folder is a folder in the top level directory, not within the project or test folder
models.py in sectors app contains
class Photo(models.Model):
photoname = models.ImageField()
views.py in sectors app contains
from .models import Photo
def index(request):
pets = Pets.objects.all()
photos = Photo.objects.all()
return render(request, 'index.html', {'pets': pets}, {'photos': photos})
index.html template
{% load static %}
<H1>Hello World Index!</H1>
#Testing bringing in variables from a model (2020/05/07 Works!)
<ul>
{% for pet in pets %}
<li>{{pet.name}}</li>
{% endfor %}
</ul>
# ************************************
# This is where I'm trying display generated pictures from the media folder
# ************************************
<ul>
{% for img in photos %}
<li>
<img src="chart1s/{{img.image.url}}" />
</li>
{% endfor %}
</ul>
#Testing Static Image (2020/05/08 Works!)
<img src="{% static "OIP.jpg" %}"/>
<img src="{% static "temp_fig_00.png" %}"/>
settings.py in chartingtest project folder
#Media ROOT
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
MEDIA_URL = '/media/'
You are correct that using static files will not work, by their name static files are not supposed to change.
The solution is to create a media folder and have your application save the graphs to that folder. Your model picture should point to that folder. Like you said, in your templates you can then iterate over that model to display all the graphs.
In production, you should create a cloud storage bucket and save your images there. Then make sure your settings.py directs any requests for images to your storage bucket.
Your picture model should use models.ImageField(), not a typical CharField, image = models.ImageField(). In your template as you iterate, you should call each image with <img src="{{ picture.image.url }}"
Apart from that, I would need to look in depth at the code to see what's going on, and I don't click links on stackoverflow posts. If you'd post the code in a comment might be able to take a closer link, but try those things first!

Downloading a FileField with a View

I have a table which presents order information - attached to each order is a file. This file's file path displays as a link in the table already, but I am unable to download by clicking it. I would like to be able to click this link and have the proper .docx download.
I am referencing: how to download a filefield file in django view
but I really don't understand how this solution is working. Is no URL needed? How does the view know which file to pick? I opened a previous question and got marked as a duplicate and pointed to this link, but I really don't understand the solution that was given here. Do I need to somehow pass the pk of the order or something? Any help would be greatly appreciated. Below is my code.
models.py
class Orders(models.Model):
...
order_file = models.FileField(upload_to='web_unit', null=True, blank=True)
...
def __str__(self):
return self.reference
index.html
<div class="table-responsive">
<table id="main_table" class="table table-striped table-bordered" cellspacing="0" style="width="100%">
<thead>
<tr>
....
</thead>
<tbody>
{% for order in orders %}
<tr>
<td>
<!-- Update book buttons -->
<button type="button" class="update-book btn btn-sm btn-primary" style="color: #FFCF8B; border-color: #FFCF8B; background-color: #FFF;" data-id="{% url 'order_update' order.pk %}">
<span class="fa fa-pencil"></span>
</button>
</td>
....
<td>Download</td> #this is the link
</tr>
{% endfor %}
</tbody>
</table>
When the link in the table is clicked - I'd like for the file to be downloaded - I need help on how to define the URL and the View to make this happen.
Right now when I click the link I get an error showing all the urls that were tried.
This is a bit complex stuff at first, but let me break it down for you.
First of all, the files you upload thorough Django are stored somewhere in the Filesystem (in case of default settings). This usually means your uploaded files go to your /media folder which is in the same directory as your other Django files are.
The path to the media folder can be defined in your Django settings file using the MEDIA_ROOT parameter. By default its probably next to your manage.py file.
To be able to download your uploaded files, you need to serve the files using a webserver (Apache, nginx, etc...). Here's a config snippet from a classic nginx setup:
# Media files
location /media {
alias /home/riverman/my-django-project/media;
}
This means, your uploaded file could be accessed (and downloaded) thorough a browser using the following imaginary link:
http://my-django-project.test/media/images/cat.png
Now here's the tricky part: Django does not store the full url (http://my-django-project.test/media/images/cat.png) to your file in the database, but instead, only the static part of it (/images/cat.png). Both the media folder's name (media in this example) and the website's URL (my-django-project.test) are subject to later change so it would not be clever to store it all in the database, but instead, you generate the full URL on-the-fly.
When you access the path to your desired file ({{ order.order_file.url }}) you only receive a partial information, something like this: /media/images/cat.png.
You need to combine this partial information with your site's url. Here's an example how i'd do it. Add this in your views.py:
from django.contrib.sites.models import Site
current_site = Site.objects.get_current()
current_site.domain
and update your index.html to this:
<td>Download</td>
To setup a local webserver on your development machine is out of scope for this question, but you can refer to this question: Best lightweight web server (only static content) for Windows
Good luck!

Google AppEngine, Django and request.FILES

Situation: An html form (with the method="POST" and enctype="multipart/form-data" attributes set properly) is submitted to the server, which is Django on Google's AppEngine. It contains a file input, which is moved (not cloned) into said form and submitted through an iframe. For small files (~1mb or less) the submitted files are being found in the request.FILES dictionary, and stored in a blob. For files above 1mb, however, the look into request.FILES is returning an error, claiming the key (which is the name of the file input element) is not found in the given dict, and in fact that the request.FILES (and request.POST) dicts are empty.
Question: Is this due to a AppEngine limitation (if so, is there a workaround?) Is this related to Django in some way (do note that the enctype and method are set appropriately)? Is there some other element that is missing?
Additional Information: Please also note that I am a javascript programmer filling in for an absent Python programmer, and know only the basics. Please tailor your answers accordingly.
Relevant Python:
def media_image_upload(request):
if request.method == 'POST':
img = request.FILES['img']
Relevant html:
<form id="hiddenUpForm" style="display:none;" action="http://localhost:8080/media/imageUpload" enctype="multipart/form-data" target="upTarget" method="POST">
<input name="img" id="img" type="file" accept="image/*">
<iframe id="upTarget" name="upTarget" src="" style="width:0;height:0;border:0px solid #fff;"></iframe>
</form>
I believe that in order to upload files that are larger than 1 MB, you need to use the Blobstore API to create a special URL that is used for the upload; it can't be your regular <1 MB URL.
The controller code that generates the HTML page that contains the upload form would use upload_url = blobstore.create_upload_url('media/imageUploadBig') and would then add upload_url to your template values and render the template.
The template, in turn, would contain a FORM definition something like this:
<form id="hiddenUpForm" style="display:none;" action="{{ upload_url|safe }}" enctype="multipart/form-data" target="upTarget" method="POST">
This means that you either need to have two different forms -- one for files that are less than 1 MB and one for files that are larger -- or you can store all of your images in the Blobstore.
See the Blobstore docs for more information.

Django 1.1.1 chokes on multipart/form-data

Initial story
I'm trying to implement file upload using a simple form (I'm pasting stripped version, but all important parts are included):
<form method="POST" action="" enctype="multipart/form-data">
<input type="file" name="up_file" size="50">
<input type="hidden" name="cpk" value="{{c.pk}}">
<input type="submit" name="btn_submit">
</form>
Now, server-side script running under wsgi was receiving strange values for "cpk" field and request.FILES was empty empty request.FILES and request.POST dictionaries, so I decided to switch to development server for debugging.
Surprisingly, ipdb debugger hangs after typing both request.POST and request.FILES and pressing enter... On the other hand, when I remove enctype="multipart/form-data" from tag, I'm able to check both request.POST and request.FILES, but of course request.FILES is empty then.
(Also wsgi version seems to be healed by removal of enctype="multipart/form-data"...)
Update
I tried all combinations of Opera 10//Firefox 3.5, enctype="multipart/form-data"//no multipart/form-data and dev server//mod_wsgi. The result is that it's enctype="multipart/form-data" that breaks the show. So now I'm going to check Django bugtracker if it's a known issue.
Meantime, maybe someone here can point me in the right direction
You may need to provide your view and form code as we use form uploads with enctype="multipart/form-data" in Django 1.1.1 with great success.
The following dummy app, for example, works perfectly in the dev server.
views.py
from django import forms
from django.shortcuts import render_to_response
class UploadForm(forms.Form):
cpk = forms.CharField(max_length=256)
f = forms.FileField()
def my_upload_view(request):
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
print "Got cpk",form.cleaned_data['cpk']
print "Got file",request.FILES['f'].read()
else:
form = UploadForm()
return render_to_response('upload.html', {'form':form})
upload.html
<html>
<body>
<form enctype="multipart/form-data" method="post">
{{ form.f }}
{{ form.cpk }}
<input type="submit" />
</form>
</body>
</html>
I'm using the django form instance to render the file input, but it renders the very common <input type="file" name="f" id="id_f" />.
Using this sample, I get the content of the file (I've tested using a simple text file) printed to the terminal from my dev server. The few gotchas and tests I can recommend are:
ensure that the file you are uploading is less than settings.FILE_UPLOAD_MAX_MEMORY_SIZE (the default is 2.5 MB)
double-check that you haven't defined any custom file upload handlers that may be breaking the upload process (settings.FILE_UPLOAD_HANDLERS)
try uploading a very simple file (like a small text file) to see if the issue still persists with something basic
use a tool to inspect the raw HTTP request/response traffic (firebug will do this for you, and there are some stand-alone apps that will act as a proxy to help you here too)... sometimes the solution will jump out when you can see the raw traffic.
In case you haven't found them yet, the django file upload docs have a fair number of examples.

Categories

Resources