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.
Related
I will preface by saying this is my first time working with flask & HTML to build a web app, so I may be using the wrong terminology in some places, apologies in advance.
I have used these two previous questions as reference for what i'm doing:
Flask render a word cloud
Passing a matplotlib figure to HTML (flask)
I am working a web app that allows a user to input a movie, and a wordcloud is returned.
The user starts on /search, where they input a movie name, I then redirect to /search_results where a list of movies with similar names are shown, the user selects the right film and submits. This part of the journey is all working fine, from here I take the movie name, I then apply a function I have built in python that creates a wordcloud based on this film name (in my code below this is what the function wordcloud_generator(1,session['search_value']) is doing in the fig route). The output of the wordcloud_generator() function is:
...
img = BytesIO()
wordcloud.to_image().save(img, 'PNG')
img.seek(0)
return img
I want to save this image to a route "/fig/<wordcloud_img>" and then be able to call it in the src of an img tag in the route "/images/<wordcloud_img>".
When running through this I get the error of at the point that I submit the movie name from /search_results and redirect to 'images'. Error: werkzeug.routing.BuildError: Could not build url for endpoint 'images'. Did you forget to specify values ['wordcloud_img']?
After this if I navigate manually to "localhost:5000/fig/wordcloud_img" then my function seems to run and the image is shown, and then if I manually navigate to "localhost:5000/images/wordcloud_img" the image is properly surfaced in the html.
It seems like I am doing this in the wrong order somehow and the function isn't running/generating the image before I try to access it on the /images source.
Routes
def search():
if request.method == 'POST':
movie_search = request.form['search_text']
session['returned_movies'], session['search_links'] = search_function(search_term = movie_search)
return redirect(url_for('search_result'))
return render_template('search.html',title='Search')
#app.route("/search_result", methods=['GET', 'POST'])
def search_result():
if request.method == 'POST':
movie = request.form['movie']
session['search_value'] = session['search_links'][session['returned_movies'].index(movie)]
return redirect(url_for('images'))
return render_template('search_results.html',title='Search Results')
#app.route("/images/<wordcloud_img>")
def images(wordcloud_img):
return render_template("wordcloud.html")
#app.route("/fig/<wordcloud_img>")
def fig(wordcloud_img):
img = wordcloud_generator(1,session['search_value'])
return send_file(img, mimetype='image/png')
wordcloud.html
{% extends "layout.html" %}
{% block content %}
<div>
{% if session['search_value'] %}
<p>Searching for a movie: {{ session['search_value'] }}</p>
{% else %}
<p>Oops, no movie selected </p>
{% endif %}
<body>
<img src="{{ url_for('fig', wordcloud_img = 'wordcloud_img') }}" alt="Image Placeholder" height="100">
</body>
</div>
{% endblock content %}
The problem is that you are not passing the image to the 'images' function that displays the template with the image. In your 'search_result' function, you will need to pass the wordcloud_image to the 'images' function like so:
return redirect(url_for('images'), wordcloud_img=your_image)
(replacing your_image with the actual image variable)
In your images route, you would then need to pass this received image to the template:
return render_template("wordcloud.html", wordcloud_img=wordcloud_img)
Then in your template, you can use this wordcloud_img variable like this:
<img src="{{ url_for('fig', wordcloud_img=wordcloud_img) }}" alt="Image Placeholder" height="100">
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
I have a webapp where a user can upload a pdf file. I want the user to be able to click a button to be able to see the file in the browser.
I'm not sure how to link the pdfs as when they are uploaded they get given a random name by django in my static root. eg: hello_0akcjs.pdf
My View
def pdf_view(request, pk):
Menufile = Menu.objects.get(pk=pk).menuFile
with open('Menufile', 'r') as pdf:
response = HttpResponse(pdf.read(), content_type='application/pdf')
response['Content-Disposition'] = 'inline;filename=some_file.pdf'
return response
URL
path('<int:pk>/', pdf_view, name='pdf_view'),
Model
class Menu(models.Model):
name = models.CharField(max_length=50)
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
menuFile = models.FileField(upload_to='menus')
Template
{% for menu in menus %}
View Menu {{ menu.name }}
{% endfor %}
As it stands, everything works, except i just dont know how to get the pdf to be rendered at the URL for that model instances PK.
I was making it far too complicated. All i needed to do was reference the menuFile URL in the template.
Example:
View Menu
To display a pdf you have to use an embed tag,
<embed src={% url 'pdf_view' menu.pk %} width="500" height="375" type="application/pdf">
I'm creating a docx file with user input in django. For now when the submit button is clicked the download link generates and users can download the file. However I want to save the file in file store and create a template where the user can see all the docx files (or a hyperlink) that he has created in the past. "Somewhat like a database"
models.py
class Timetables(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,
default=1, related_name='timetables_files', null=True)
timetable_files = models.FileField(
null=True, blank=True, upload_to='word_documents')
views.py
def school_view(request):
if request.method == 'POST':
worddocument = docx.Document()
documenttitle = worddocument.add_heading(
school_name_view.title(), 0)
file = io.BytesIO()
worddocument.save(file)
response = HttpResponse(
content_type='application/vnd.openxmlformats-
officedocument.wordprocessingml.document')
response['Content-Disposition'] = 'attachment; filename =quicktimetable.docx'
worddocument.save(response)
path = join(settings.MEDIA_ROOT, 'word_documents',
'quicktimetable.
file = open(path, 'w+b')
return response
class TimetablesView(ListView):
model = Timetables
template_name = 'quickmain/timetables_list.html'
timetables_list.html
{% if user.is_authenticated %}
{% for f in files %}
{{ f.files.url }}
{% endfor %}
{% for timetable_files in user.timetables_files.all %}
<embed src = "{{timetable_files.timetable_files.url}}" target="__blank">
{% endfor %}
{% else %}
How can I save the files which are generated and show all the files related to that user on a different a template.
I have a FileField in my models.py but no idea how to make use of it. I got a suggestion to regenerate file whenever users ask for it but I have no idea about that as well.
You have to create the relation between the file and user model.
Example
in your model
class DocxFile(models.Model):
user = models.ForiegnKey(User) #your user model name
file = models.FileField('upload_to='/')
then in your views
from yourappname.models import DocxFile
def docs_file(request):
files = DocxFile.object.filter(user=request.user)
return render('your template path', {'files':files})
in your template
{% for f in files%}
{{f.file.url}}
{% endfor %}
I am making a web site for a travel agency. I am using a modal field FileField to add PDF files to more pages.
models.py
class Travel(models.Model):
docfile = models.FileField(upload_to='documents')
It works fine when uploading just one file in admin and displaying one file on the template.
file.html
<div>
<h4>{{ travel.docfile.name }}</h4>
</div>
But what if I want to upload and display more than one file per page?
I now I could loop over all files to get all files for all pages:
{% if documents %}
<ul>
{% for document in documents %}
<li>{{ document.docfile.name }}</li>
{% endfor %}
</ul>
{% else %}
<p>No documents.</p>
{% endif %}
view.py
def vacation(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Travel(docfile = request.FILES['docfile'])
newdoc.save()
return HttpResponseRedirect(reverse('myapp.views.vacation'))
else:
form = DocumentForm()
documents = Travel.objects.all()
return render_to_response('myapp/file.html',{'documents': documents, 'form': form},context_instance=RequestContext(request))
But how can I upload and display more files for one page?
Thank you for your time.
there are a few ways (that I can see):
you allow up to N uploads each time: thus you just create a form with N fields to take care of it. It's not very fancy (nor the most usable UI), but it's easy to implement.
if you want to give the user the possibility to upload a "dynamic" number of files, you may implement a view that is called via ajax. This still requires your user to select one file at the time, and load it, but then you can upload several files one after the other, without reload the entire page. You may even find some reusable app, like the following, that already should implement the most of it: https://github.com/skoczen/django-ajax-uploader
you can use the HTML5 multiple attribute of the <input type="file" ... field. Even in this case there is an interesting reusable app: https://github.com/Chive/django-multiupload
Hope it helps