A way to detect session expiration in flask? - flask

In Flask I want to delete some files when the session times out. I don't have user-login - anyone can upload images and do stuff with them. I want the images to disappear when the session does. But how do I detect when that is?
I believe it will not make sense to store the images in RAM, that's why I want to save them on the server and remove them again.
edit: code for view function and html for rendering PDF with images that are to be deleted when session times out.
views.py:
from flask import (render_template, request)
from flask_weasyprint import HTML, render_pdf
from .forms import toPDF
from .helpers import (custom_images_in_collection)
#user_blueprint.route("/collection", methods=["GET", "POST"])
def collection():
form = toPDF()
# 'collection' is a list of preexisting/permanent image objects selected by user and stored in session
# custom_images is a list of extra image objects created by user on the fly. They're stored
# and retreived from the database from their own table of temporary images with a time field.
# I want the custom images to disappear when the session expires
custom_images = custom_images_in_collection(collection)
collection.append(custom_images)
if request.method == "POST":
if collection:
if form.validate_on_submit():
template = render_template("mypdf.html", collection=collection)
html = HTML(string=template)
# This bit of CSS is dynamically generated, the rest is hard coded in the template
return render_pdf(html)
return render_template("collection.html", collection=collection, form=form)
mypdf.html:
{% if collection|length %}
<div class="innerbox">
{% for word in collection %}
<div class="imgcard">
<div class="imgcontainer">
<img class="card-img-top" src="{{ url_for('static', filename='images/' + word.image.name)}}"
alt="image of '{{ word.word }}'">
</div>
<div class="card-body">
<h2 class="card-title">{{ word.word }}</h2>
</div>
</div>
</div>
{% endfor %}
{% else %}
<div class="col-4">
<p>You don't have a collection yet!</p>
</div>
{% endif %}

Related

In django, creating a randomized object list with paging

I've been trying to get a list of model objects, randomize their order and paginate them in the template. I thought I've done it until I realize that in each next page call I had been re-randomizing objects rather than using the previously randomized list.
Is there anyway I can randomize the object list only while opening the page first time and after that use the same list without randomizing it while going to the next page?
Thanks.
Views.py
class index(View):
def get(self, request):
all_items = list(Electronics.objects.all())
random.shuffle(all_items)
paginator = Paginator(items, 24)
page = request.GET.get('page')
items = paginator.get_page(page)
return render(request, 'home.html', {'items':items, 'header':'Homepage'})
home.html
<div class='container'>
<div class='row'>
{% for item in items %}
<div class='col-xs-6 col-sm-8 col-lg-4'>
<img src='{{ item.image.url|cut:"%22"|thumb}}'>
<h4>{{item.name}}</h4>
<p>$ {{item.price}}</p>
<form method='get'>
<input value='Add to cart' type='submit' formaction= "{% url 'addtocart' item.id %}">
<input value='View Details' type='submit' formaction= "{% url 'detail' item.id %}">
</form>
<div><br><br>
{% endfor %}
</div>
</div>
<div class='pagination' align='center'>
<span class='step-links'>
{% if items.has_previous %}
« first
previous
{% endif %}
<span class="current">
Page {{items.number}} of {{items.paginator.num_pages}}
</span>
{% if items.has_next %}
next
last »
{% endif %}
</span>
</div>
You can use this great snippet to implement a randomized seeded queryset mixin. This will basically allow you to create a GET request to the view using the mixin (via a url such as /electronics?page=1), and then it will generate a seed which will be cached in your session, and then reused to pull iterative sets of records.
This shouldn't be too much of a burden on your load, since caching a seed is inexpensive - it is the use of order_by('?') which can be expensive, but 3-4 thousand records isn't considered that big for such a task.

Django: how to get the second last object from database?

I have a model for news. I add news to the database with Django admin. Model Post consists of title, body and image.
On the main.html page of my project i have a carousel with 3 slides.
I already have some news in my database, and I want to display the last, the second last and so on images in that carousel.
My question is: what code should I add in html to display the last, the second last and the third last images?
<img src="???"> {# the last image #}
<img src="???"> {# the second last image #}
<img src="???"> {# the third last image #}
You can try like this:
posts= Post.objects.all().order_by('-id')
last_post = posts[0]
second_last_post = posts[1]
third_last_post = posts[2]
last_three_posts = posts[0:3]
But make sure to have atleast three posts or else it will throw index error
# views.py
from django.views.generic import TemplateView
class HomePageView(TemplateView):
template_name = 'home.html'
posts = Post.objects.all().order_by('-id')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context = {
'carousel_posts': self.posts[:3],
}
return context
Use this make a for loop over the carouser_posts keyword in your template and extract the information you need.
<div class="carousel-inner">
{% for post in carousel_posts %}
<div class="carousel-item active">
<img src="{{post.image}}">
<p>{{post.title}}"</p>
</div>
{% endfor %}
</div>
UPDATE
Answer to your update. Using context keyword carousel_posts provided by our HomePageView we can access post objects one by one using for loop.
In template, assuming that your Post model has an image field called image.
{% for post in carousel_posts %}
<img src="{{ post.image.url }}">
{% endfor %}

flask database global variable

in my flask app I am using mongoDB and on the home page I have a form that is returning all the known collections in that particular database. I am asking the user to pick a collection they want to use as I will use that collection set to return all the documents in other routes or views.
Im struggling how to make this global "selected_collection" a global variable that all the routes and views can use.
for example on the index page I am able select a collection then on the submit it would redirect me to view db_selected there I was trying to make the selected_collection a global variable but if i got to the about view it get an error related to
I imagine I should use flask.g but im not sure how to get it to work. I have read some of the documents but they are a little vague to me.
AttributeError: '_AppCtxGlobals' object has no attribute 'selected_collection'
how can i make this work?
app.py file:
# INDEX
#app.route('/', methods=['GET', 'POST'])
def index():
coll_name = get_db_collection()
return render_template('index.html', coll_name=coll_name)
# LOGIN
#app.route('/db_selected', methods=['GET', 'POST'])
def db_selected():
if request.method == 'POST':
selected_collection = request.form['Item_4']
selected_collection = g.selected_collection
return render_template('db_selected.html',
selected_collection=selected_collection)
#app.route('/about')
def about():
app.logger.info('selected_collection is {}'.format(g.selected_collection))
return render_template('about.html')
index.html file:
{%extends 'layout.html'%}
{%block body%}
<div class="jumbotron text-center">
<h1>Welcome to the index.html file !</h1>
</div>
<div class="container">
{% include 'db_query_bar.html' %}
</div>
{%endblock%}
db_query_bar.html
<form class="form-horizontal" action="{{ url_for('db_selected') }}" name="Item_1" method="POST">
<fieldset>
<legend>Select DB</legend>
<div class="form-group">
<label for="select" class="col-lg-2 control-label">Database Collection:</label>
<select id="DB" class="form-control" name="Item_4" style="width: 70%" >
<!-- <option value="">All</option> -->
{% for item in coll_name %}
<option value="{{item}}">{{item}}</option>
{% endfor %}
</select>
<br>
</div>
<div class="form-group">
<div class="col-lg-10 col-lg-offset-2">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</div>
</fieldset>
</form>
Just to answer this for the global variable I ended up placing
app.selected_collection = "Some Value"
in the top of my flask code this will create a global variable I can use in all the views.
app = Flask(__name__)
# CONFIG MONGO CONNECTION DETAILS
app.config['MONGO_HOST'] = 'DB-Host'
app.config['MONGO_DBNAME'] = 'DB-Collection'
app.selected_collection = "Some Value"
# INIT MONGODB
mongo = PyMongo(app)

How can I allow user to download or view a PDF online that I am hosting in my static folder?

I have a modal that stores the file_name, file_link, is_active and an uploaded on field. I can add the PDF via the admin site but I am trying to allow the users the ability to download the PDF or view the PDF on a new tab from the front end webpage that I created.
Currently, the view that I created is getting the information for each PDF so that I can display the name, link, etc. If I put the {{ pdf.file_link }} into an it doesn't do anything.
I would really appreciate getting some assistance on this so that I can stope spinning my wheels. Thank you in advance!+
EDIT:
Models:
class HelpfulPDF(models.Model):
file_name = models.CharField(max_length=256)
file_link = models.FileField(upload_to='pdfs')
uploaded_on = models.DateField(auto_now=True)
is_active = models.BooleanField(default=True)
def __str__(self):
return "{} - {}".format(self.id, self.file_name)
View:
As you can see in the view, I am only getting the date from the model. I am not doing anything special with the actual PDF yet. That is the question.
#login_required()
def help_center_view(request):
data = dict()
data['userprofile'] = request.user.userProfile
data['get_pdfs'] = HelpfulPDF.objects.filter(is_active=True)
return render(request, 'help_center.html', data.items())
Template:
{% extends 'base.html' %}
{% load staticfiles %}
{% block content %}
<hr>
<div class="text-center">
<b>Please look at the below PDF's.</b> <i>(You can view in browser or download to your computer)</i>
</div>
<hr>
<br><br>
<div class="text-center">
{% if get_pdfs %}
{% for each_pdf in get_pdfs %}
<div style="display: inline-block; width:275px; height: auto; border: thin grey solid; border-radius: 15px ">
<br>
{% if each_pdf.file_name %}
<h6>{{ each_pdf.file_name }}</h6>
{% else %}
<h6>Helpful PDF</h6>
{% endif %}
<hr>
<div style="margin-top: 13em">
<hr>
<button class="btn-primary btn-sm">Download PDF</button>
<button class="btn-primary btn-sm">View PDF Online</button>
</div>
<br>
</div>
{% endfor %}
{% else %}
<h4>There are no PDF's to display at this time.</h4>
{% endif %}
</div>
{% endblock %}
If your link to the pdf file works (try going to it in your browser and see what happens), then it should be possible to do the equivalent of this in your template:
<a href={{ your_link }}></a>
Add the target="_blank" attribute to have it open in another tab.
You can add a download attribute to the anchor element if you want it to download on click.
The browser should handle opening or downloading the PDF automatically. If not, then either your link is not valid (the most likely cause) or (much less likely because you'd probably know it) your server is sending the wrong content-type header in the response (which you can inspect from the browser dev console in the Network tab on Chrome).
You can create a view to proxy the PDF files and return specific headers, see the following answer: How to show a PDF file in a Django view

Django - troubleshooting slow page load times for a comments page

I have a page on my site that is dedicated to group comments. Users can create posts (i.e. questions or comments), and other users can comment on them. Comment hierarchy is identical to Facebook; there is no comment threading like reddit. Using Heroku.
I'm using django-debug-toolbar to optimize this page. Right now the page loads in 5-6 seconds; that's just the time that my browser waits for a response from the server (doesn't include JS/CSS/IMG load times). There are 30 SQL queries take between 80-100ms to load (am using prefetch_related on relevant m2m fields). The size of the page returned is 344KB, so not a monster page at all. The comments are paginated and I'm only returning 10 posts + all comments (in test each post only has 3-4 comments).
I can't figure out why it takes such a long time to load the page when the SQL queries are only taking up to 100ms to complete.
Below is the relevant code. I am getting back the 'posts' object and using a for loop to render each object. Within that 'posts' for loop I'm doing another for loop for the 'comments'.
What else can I do to further optimize this/reduce page load time?
# views.py
def group_app(request, course_slug):
# get the group & plan objects
group = Group.objects.get(slug=course_slug)
# load the page with a new Post and comment form
group_post_form = newGroupPost()
post_comment_form = newPostComment(auto_id=False)
# gather all the Posts that have already been created
group_posts = GroupPost.objects.filter(group=group).prefetch_related('comments', 'member', 'group')
paginator = Paginator(group_posts, 10)
page = request.GET.get('page')
try:
posts = paginator.page(page)
except PageNotAnInteger:
page = 1
posts = paginator.page(page)
except EmptyPage:
posts = paginator.page(paginator.num_pages)
variables = RequestContext(request, {
'group': group,
'group_post_form': group_post_form,
'posts': posts,
'next_page': int(page) + 1,
'has_next': posts.has_next(),
'post_comment_form': post_comment_form
})
if request.is_ajax():
return render_to_response('group-post.html', variables)
else:
return render_to_response('group-app.html', variables)
# group-app.html
{% extends 'base.html' %}
{% block canvas %}
<div id="group-body" class="span8">
{% include "group-body.html" %}
</div>
{% endblock canvas %}
# group-body.html
{% for post in posts %}
{% include 'group-post-item.html' %}
{% endfor %}
# group-post-item.html
{% with post_avatar=post.member.get_profile.medium_image.url %}
<div class="groupPost" id="{{ post.id }}">
<div class="avatarContainer">
{% if post_avatar %}
<img class="memberAvatar" src="{{ post_avatar }}" alt="">
{% else %}
<img class="memberAvatar" src="{{ STATIC_URL }}img/generic-avatar.png">
{% endif %}
</div>
<div class="postContents">
<div class="groupPostComment">
<h6 class="comment-header">SHOW/ADD COMMENTS</h6>
<div class="comments">
{% for comment in post.comments.all %}
{% with comment_avatar=comment.member.get_profile.medium_image.url %}
<div class="commentText">
<p class="comment-p">{{ comment.comment }}</p>
</div>
{% endwith %}
{% endfor %}
</div>
</div>
</div>
</div>
{% endwith %}
As I understand this is related to avatar image and not SQL queries. Working with remote storages like S3 is a bit slow. But it should not hamper the user experience as you have faced above.
I didn't get what you are using to get 'avatar' image, but I would suggest to use some third party packages like.
Easy_thumbnails - It is really nice and has lots of options. I recently shifted to this you. In case of remote storages, you could pregenerate avatars with celery and serve saved thumbnail
sorl-thumbnail - I used this for my previous project. It doesn't have option to pregenerate, so first request is slow. But once thumbnail is created, next request will be fast. It has smart caching. I tried to fork it here to use with celery https://github.com/neokya/sorl-thumbnail-async
I hope it helps.