Flask and sqlalchemy: Get uploaded file using path stored on database - python

I'm using flask and sqlalchemy and I'm having issues regarding file uploading.
I'm uploading the file with a form and storing the URL to the DB, so I can display it inside on the page.
Here's the form's code:
boxart = request.files['boxart']
if boxart:
filename = secure_filename(boxart.filename)
boxart_path = os.path.join(app.config['UPLOAD_FOLDER']+"boxart/", filename)
boxart.save(boxart_path)
game_son.boxart = boxart_path
db.session.add(game_son)
db.session.commit()
So I go to my template and I can print the content of game_son.boxart and there is the full path to the file. If I click on the link, I can access, but the image will not display inside tag...
I tried getting it on the template by using:
<img src="{{ game_son.boxart_url() }}" />
Inside the model, I defined a boxart_url method, in case I need to parse the string before sending it
class Game_son(db.Model):
__searchable__ = ['version_name']
son_id = db.Column(db.Integer, primary_key=True)
version_name = db.Column(db.String(128), index=True, unique=False)
son_timestamp = db.Column(db.DateTime)
dad = db.Column(db.Integer, db.ForeignKey('game_dad.id'))
console = db.Column(db.Integer, db.ForeignKey('platform.id'))
boxart = db.Column(db.String(256))
thumbnail = db.Column(db.String(256))
genre = db.Column(db.String(32))
subgenre = db.Column(db.String(32))
def boxart_url(self):
return self.boxart
def __repr__(self):
return '<Game %r>: %r' % (self.version_name, self.dad)
The page's view just send the entire object (game) to the page.
The url that is arriving on the page is:
/home/removed_my_pc_user/app/media/boxart/image.jpg
Edit:
I just remembered that I'm using a virtual environment on the project.
Is it related to chmod?
Thanks in advance!!

Well, It's strange but I will answer my own question.
The solution is simple, I chose to store only the file names inside the database.
Then I created a route to a view that will return the file using the send_from_directory function from flask.ext.uploads
#app.route('/boxart/<filename>')
def uploaded_boxart(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'],filename)
So, inside the template we can use the url_for() function to load the file, sending as a parameter the filename stored inside the database.
<img src="{{ url_for('uploaded_boxart', filename = game_son.boxart)}}" />
Also, I think that one of my mistakes was putting the media/ folder inside the app/ folder, and not inside the root directory. So I also reconfigured the UPLOAD_FOLDER in app.config.

Related

Query using variable from url not executing - Flask-SQLAlchemy

I have jquery send out a request to the api endpoint. Code below:
$.get("http://localhost:5000/api/update/"+ elm2, function(data){});
The endpoint is defined like this:
#app.route('/api/update/<id>', methods=['GET'])
def check_new_entries(id):
result = Trades.query.filter_by(id=id).first_or_404()
new_entries = Trades.query.filter(Trades.time_recorded > result.time_recorded).all()
The table being queried from is:
class Trades(db.Model):
id = db.Column(db.String,default=lambda: str(uuid4().hex), primary_key=True)
amount = db.Column(db.Integer, unique=False)
time_recorded = db.Column(db.DateTime, unique=False)
The problem:
Jquery is successfully sending the correct request, with the variable being of type string but when the first query is executed, it fails to return anything.
I have a similar endpoint in another application and it works fine.. why is this one an exception? What could be missing? I am sure that the record that I am querying is in the database so it should return it.
#app.route('/api/update/<id>', methods=['GET',])
def check_new_entries(id):
result = Trades.query.filter_by(id=id).first()
if result:
new_entries = Trades.query.filter(Trades.time_recorded >= result.time_recorded).all()
The problem is that some unexpected space was before the rest of the string. The error originated from jquery and jinja where jquery was getting the html contents of an element and sending them as the variable. The jinja syntax was something like:
<p> {{ variable }}</p>
When jquery fetches the html contents, it takes the space btwn the paragraph opening tag and the opening curly braces as well. The variable transmitted had a leading space which caused the error.

render html strings in flask templates [duplicate]

This question already has answers here:
Passing HTML to template using Flask/Jinja2
(7 answers)
Closed 9 months ago.
I'm building a web page to show articles.
In my database, I have to attributes, one of which is used to put Markdown code and the other to save HTML code converted from the Markdown code. I want to get the HTML and add it to my base HTML.
I use Flask framework and SQLAlchemy and the data is saved in sqlite database.
My model:
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
body = db.Column(db.String)
timestamp = db.Column(db.String)
tag = db.Column(db.String)
info = db.Column(db.String)
body_markdown = db.Column(db.String)
and my view function:
def post(post_id):
post = db.session.query(Post).filter(Post.id == post_id).first()
return render_template('post.html',
post = post,)
Firstly, I tried:
<div class="post-body">
{{post.body}}
</div>
It shows the whole string with HTML tag.
post.body.decode("utf-8") and post.body.decode("ascii") didn't work, either.
When I debug the program, I found that the database returns u'string'.
How can I get pure HTML code and apply it in my base HTML file?
By default Jinja2 (the template engine for Flask) assumes input inside of {{ }} is unsafe and will not allow js or html inside them. You can solve this by using the safe filter inside your template. This will tell Jinja2 that the content is safe to render as is, and not to escape the html/javascript.
{{ post.body | safe }}

Create folders in flask

I want to create folders using flask . I Google for this a lot but didn't find any help. Evey search shows me the folder structure of flask but I want to know how can I create folders using code.
Actually I want to create folders for every user at register time.So suggest me a way how can I create a simple empty folder at particular path in flask.
Use os.mkdir or os.makedirs according to your need.
import os
os.makedirs('/path/to/directory')
create folders for every user at register time.
You can override the default constructor of sqlalchemy orm like this and do custom stuff
inside the function. Here is semi working code.
from app import db
import os
class User(db.Model):
# ...
folder = db.Column(db.String(80), unique=True, nullable=False)
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
# do custom stuff call folder create method here
self.create_folder()
# method to create folder
def create_folder(self):
#give path to location where you want to create folder
path = "my path where i want to create folder"
os.mkdir(path)
#update some property in User database
self.folder = "path to my user folder"
from flask import Flask, request
import os
app = Flask(__name__)
app.config["TEMPLATES_AUTO_RELOAD"] = True
app.config['UPLOAD_FOLDER'] = 'static/users/'
#app.route('/register', methods=["POST", "GET"])
def register():
username = request.form.get("username")
if request.method == "POST":
username = username.strip().capitalize()
user_folder = os.path.join(app.config['UPLOAD_FOLDER'], username)
os.mkdir(user_folder)
return f"folder is created under the name {username} and the full path is {user_folder}"
return """
<form method="post" action="/register">
<input type="text" name="username" required>
<button type="submit" >Submit</button>
</form>
"""

Uploading files in django admin, download through public site

I am running a Django website and I want to be able to upload a file through my admin panel and then have visitors to the main site be able to download it. I am running my site using Django-nonrel, Django FileTransfers and Google App Engine. I believe the upload functionality is working correctly as I am able to see the file in my App Engine Blob Storage. What I can't seem to figure out is how to present a download link to the specified file on the public website. I have pasted the relevant classes below:
I have an app called Calendar, that has the following model:
class CalendarEvent (models.Model):
start = models.DateTimeField(auto_now=False, auto_now_add=False)
end = models.DateTimeField(auto_now=False, auto_now_add=False)
title = models.CharField(max_length=500)
description = models.TextField()
file = models.FileField(upload_to='uploads/%Y/%m/%d/%H/%M/%S/')
Here is the view:
def calendar(request):
events = CalendarEvent.objects.exclude(start__lt=datetime.datetime.now()).order_by('start')
return render_to_response('home/calendar.html',{'events': events},context_instance=RequestContext(request))
def download_handler(request, pk):
upload = get_object_or_404(CalendarEvent, pk=pk)
return serve_file(request, upload.file, save_as=True)
Here is my admin:
class calendarAdmin(admin.ModelAdmin):
list_display = ('title','start','end')
admin.site.register(CalendarEvent, calendarAdmin)
Finally, here is the relevant part of my template:
{% for e in events %}
{% url Calendar.views.download_handler pk=e.pk as fallback_url %}
Download
{% endfor %}
{% firstof e.file|public_download_url fallback_url %} is just returning blank, i'm not sure where I am going wrong.
The GAE blob store does not support public download according to the documentation here, so if you use the default backend for public download urls, it returns None. So my guess is that e.file|public_download_url always return None. You could verify that.
Then I think your template is wrong. You're trying to access e.views.download_handler where it should be Calendar.views.download_handler if your app is named Calendar.
I think the sample on the django-filetransfers page is error prone because the variable used in the template loop has the same name as the sample app: "upload".
If this doesn't fix it, could you post your urls.py from Calendar app. It could be that the template's url method is not able to resolve the url for Calendar.views.download_handler if there is no mapping in urlpatterns.
You should have something like
urlpatterns = patterns('Calendar.views',
...
(r'^download/(?P<pk>.+)$', 'download_handler'),
...
)
in this file.
I don't see anything special, eg
Download File
should work, or just use e.file.url directly?
I haven't deployed on Google App Engine myself, but this appears to be what django-filetransfers was designed for:
http://www.allbuttonspressed.com/projects/django-filetransfers#handling-downloads
edit: I believe I've answered this in the other question you posted, then: Trouble downlaoding file using Django FileTransfers
I think easiest way is to write a view since this file blob cannot be retrieved directly to write a function which is such:
def file_transfer(req, calendar_event_id):
try:
ce = CalendarEvent.objects.get(calendar_event_id)
except CalendarEvent.DoesNotExist:
raise Http404()
file = ce.file (Write Google appengine specfic routine to pull file)
return HttpResponse(file, media_type='file content type')
Hook it up on urls.py

Dynamic URL's inside Google's AppEngine

Good Afternoon,
I'm currently trying to build something incredibly simple inside of the Google AppEngine. The goal is to build a simple photo sharing application that will connect back to my iPhone application. It's all a learning experience for both Python and Objective-C.
(I've been a PHP programmer for quite some time).
The goal, create URL's that look like the following:
/img/{{ model.key.id }}
The problem is that it seems no matter how I do the python script, I either end up with an error or simply get nothing to display on my template page that's wrapped in the FOR statement.
My App.yaml File:
application: randomwebappname
version: 1
runtime: python
api_version: 1
handlers:
- url: /media
static_dir: media
- url: /b/.*
script: beta.py
login: required
- url: /.*
script: main.py
My Model (inside beta.py):
class Photo(db.Model):
author = db.StringProperty()
title = db.StringProperty()
slugline = db.StringProperty()
content = db.StringProperty(multiline=True)
coordinates = db.StringProperty()
avatar = db.BlobProperty()
date = db.DateTimeProperty(auto_now_add=True)
My Class For Viewing The Image Page:
class ViewPage(webapp.RequestHandler):
def get(self, id):
template_values = {
'image': image,
}
path = os.path.join(os.path.dirname(__file__), 'templates/view.html')
self.response.out.write(template.render(path, template_values))
I tried all of the following in my class, but they all end in failure. I tried them with key, key.name, and key.id in the URL:
photos = db.Query(Photo).filter('key', slug).fetch(limit=1)
photos = Photo.get_by_key_name(id)
photos = Photo.get_by_key_name(key)
key = db.Key.from_path('Photo', id)
photos = db.GqlQuery("SELECT * FROM Photo WHERE __key__ = :key", key=key)
photos = db.get(photo_key)
photos = self.request.get("id")
My URL's:
application = webapp.WSGIApplication([
('/b/', HomePage),
('/b/upload', UploadPage),
('/b/add', MainPage),
('/b/img', Image),
('/b/img/([-\w]+)', ViewPage),
('/b/submit', Submission)
], debug=True)
The Template Query:
{% for photo in photos %}
<img alt="" title="" src="img?img_id={{ photo.key }}" alt="main image" />
{% endfor %}
This seems like it would be something incredibly simple and I know I'm missing something, but I'm just not sure where it's at. I would write this in PHP, but I like the concept of AppEngine and I've said above, it's a good Python learning experience.
As a side note, this application does work on the homepage of the site. I simply have a GQL query and it outputs the images fine, it just fails when I try to go to the /img/id pages.
Any advice guys (and gals)? Thanks in advance!
UPDATE #1:
As per Jonathan's request, the following is the Image class:
class Image (webapp.RequestHandler):
def get(self):
photo = db.get(self.request.get("img_id"))
if photo.avatar:
self.response.headers['Content-Type'] = "image/png"
self.response.out.write(photo.avatar)
else:
self.response.out.write("No image")
Also, after posting this, I realized that this was part of the problem as I was trying to do /img as the actual image and /img/ to display the view page. I've since changed this and now have a working model. But it's based upon the Key and not key.id:
URL:
('/b/i', ViewPage)
New ViewPage Class:
class ViewPage(webapp.RequestHandler):
def get(self):
image = db.get(self.request.get("id"))
template_values = {
'image': image,
}
path = os.path.join(os.path.dirname(__file__), 'templates/view.html')
self.response.out.write(template.render(path, template_values))
So... to get to the View page (which includes comments and such), you now have to go to the following URL:
/b/i?img_id={{ image.key }}
Atleast the page is working now, but I would much prefer to get the page to look like the following as stated above:
/b/img/{{ image.key.id }}
Update #2: ViewPage class updated as well as URL's:
class ViewPageV2(webapp.RequestHandler):
def get(self, id):
images = [ db.get(id) ]
template_values = {
'image': image,
}
path = os.path.join(os.path.dirname(__file__), 'templates/view.html')
self.response.out.write(template.render(path, template_values))
New URL:
('/b/image/([-\w]+)', ViewPageV2),
The following are two screenshots with one being proof of id "1" existing and the error that is coming with it currently.
alt text http://img215.imageshack.us/img215/9123/screenshot20091130at937.png
alt text http://img215.imageshack.us/img215/2207/screenshot20091130at938.png
Again, Thanks All!
A simple way to grab them all:
photos = Photo.gql('ORDER BY __key__')
For more, see Queries on Keys in the App Engine docs.
Are you storing your photos with predefined keys?
photo = Photo(key_name="xzy123")
photo.put()
Then you can retrieve it in your ViewPage:
photos = [ Photo(key_name="%s" % id) ]
See Getting an Entity Using a Key.
Finally, to retrieve a photo based on its appengine-assigned key and assuming this key is present in the URL (e.g., http://host/b/img/ahByY..., so change your template to generate URLs of this form), use
class ViewPage(webapp.RequestHandler):
def get(self, id):
photos = [ db.get(id) ]
...

Categories