Python Flask SQLAlchemy-Paginator Access next page in html view - python

I have currently a problem to include the pagination function into my project. I know there is LIMIT/OFFSETor yield_per(), but I was not able to implement them.
I am using SQLAlchemy not Flask-SQLAlchemy so paginate wont work.
My Database is not that big. I am trying to show rooms which have been added by a user. So all in all a user will have 20~ rooms, big users maybe 100. I want to show on the profile page the 6 last inserted rooms and if there are more, there should be pagination, like page 2 shows the next 6 etc.
I am using SQLAlchemy-Paginator.
I already implemented it and tested it, it works fine. It also limits already the results depending on which page I am. But how do I access the next page while on HTML?
Here is the python code:
#app.route("/user/logged_in")
#login_required
#check_confirmed
def logged_in():
if current_user.email_verified:
users_room = db_session.query(Zimmer).filter_by(users_id=current_user.id).order_by(desc("id"))
paginator = Paginator(users_room, 2)
for page in paginator:
print "page number of current page in iterator", page.number
print "this is a list that contains the records of current page", page.object_list
return render_template('logged_in.html', paginator=paginator)
return redirect(url_for('unconfirmed'))
Here is the view. The solution must be somewhere here. I can access pages by page.previous_page_number or page.next_page_number. But there is no example in the docu how to do it in view.
<div class="user-rooms">
<h2> Ihre Zimmer </h2>
{% for page in paginator %}
{% if page.number == 1 % }
{% for zimmer in page.object_list %}
{% if zimmer.users_id == current_user.id %}
<div class="col-sm-4 col-xs-6 col-xxs-12 img-holder">
<img src="../static/userimg/{{ zimmer.hauptbild }}"/>
<div class="buttons-del-work"> Bearbeiten Löschen </div>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
If I manually change the page numbers here it show me the correct items, so I feel like I am close:
{% if page.number == 1 % }

Okay here is a solution (which does not use any further methods from the SQLAlchemy-Paginator package). I coded everything myself, but I would still like to know how it is done with page.next_page_number etc.
Explained:
First of all I added an argument (pagenumber) to my logged_in function. Everytime url_for("logged_in", pagenumber=1) is called the pagenumber has to be set to the defaultvalue 1.
I created an empty list, where I add all the page.number items, so I know how many pages my resultset will have:
pages_list = []
for page in paginator:
pages_list.append(page.number)
I use the pages_list also in the view to generate the buttons which can be clicked to see the next page, I also give the pagenumber to the html view:
return render_template('logged_in.html', paginator=paginator, pagenumber=pagenumber, pages_list=pages_list)
Here is the HTML view where I show the buttons:
<div class="col-xs-12">
{% for number in pages_list %}
{{ number }}
{% endfor %}
</div>
Now if a user clicks one of the Buttons the logged_in will be called with a new pagenumber argument (the actual pagesite you clicked)
In the logged_in I added also typecasted pagenumber to int before giving it to html view:
pagenumber = int(pagenumber)
Solution Code
Python:
def logged_in(pagenumber):
if current_user.email_verified:
users_room = db_session.query(Zimmer).filter_by(users_id=current_user.id).order_by(desc("id"))
paginator = Paginator(users_room, 2)
pages_list = []
for page in paginator:
pages_list.append(page.number)
pagenumber = int(pagenumber)
return render_template('logged_in.html', paginator=paginator, pagenumber=pagenumber, pages_list=pages_list)
return redirect(url_for('unconfirmed'))
HTML:
<div class="user-rooms">
<h2> Ihre Zimmer </h2>
{% for page in paginator %}
{% if page.number == pagenumber %}
{% for zimmer in page.object_list %}
<div class="col-sm-4 col-xs-6 col-xxs-12 img-holder">
<img src="../static/userimg/{{ zimmer.hauptbild }}"/>
<div class="buttons-del-work"> Bearbeiten Löschen </div>
</div>
{% endfor %}
{% endif %}
{% endfor %}
</div>
<div class="col-xs-12">
{% for number in pages_list %}
{{ number }}
{% endfor %}
</div>

Related

How can I open a page with only one django model object's info after clicking it's ImageField on another page?

I am displaying django models on one of my website's pages. When I press one's ImageField on the page, I want it to open another page including only that one object. How do I do that ?
I thought about using the filter method in my views.py for filtering through my objects and finding that exact one, but I don't know what arguments to use.
Any ideas? (I am a beginner in django)
VIEWS.PY
from django.shortcuts import render
import requests
from . import models
def index(request):
return render(request, 'base.html')
def new_search(request): ********NOT IMPORTANT (I THINK)********
search = request.POST.get('search')
models.Search.objects.create(search=search)
objects = models.Object.objects.all()
results = objects.filter(name__contains=search).all()
args = { 'results': results }
return render(request, "my_app/new_search.html", args)
def individual_page(request):
link = request.GET.get('object-link')
objects = models.Object.objects.all()
return render(request, "my_app/individual_page.html")
MY TEMPLATE
{% extends "base.html" %}
{% block content %}
{% load static %}
<h2 style="text-align: center">{{ search | title }}</h2>
<div class="row">
{% for result in results %}
<div class="col s4">
<div class="card medium">
<div class="card-image">
<a name="object-link" href="{% url 'individual_page' %}"><img src="{{ result.image.url }}" alt=""></a>
</div>
<div class="card-content">
<p>{{result.name}}</p>
</div>
<div class="card-action">
View listing: Price TEST
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock %}
So, the thing I want to do is: when I press the anchor tag that includes the image, I get redirectioned to another page which contains only that one object's info.

Pelican -- 'articles_page' is undefined

I've created my own theme for Pelican and I've been using it for a while to build my site. I've decided to start blogging again so I'm only now adding the blog features to the site.
I've created my own blog.html template to render the content in the way I want. I started by copying and pasting the code from the 'simple' theme that comes with Pelican to get me started, but even though it is unchanged I'm getting an 'articles_page' is undefined error when I try to build.
Where is the article_page variable set from? I tried adding to my pelicanconf.py file but it didn't help.
{% extends 'base.html' %}
{% block title %}{{ page.title }} — Ricky White{% endblock title %}
{% block content %}
<section class="wrapper">
<div class="container">
<div class="row">
<div class="col">
<ol id="post-list">
{% for article in articles_page.object_list %}
<li><article class="hentry">
<header> <h2 class="entry-title">{{ article.title }}</h2> </header>
<footer class="post-info">
<time class="published" datetime="{{ article.date.isoformat() }}"> {{ article.locale_date }} </time>
<address class="vcard author">By
{% for author in article.authors %}
<a class="url fn" href="{{ SITEURL }}/{{ author.url }}">{{ author }}</a>
{% endfor %}
</address>
</footer><!-- /.post-info -->
<div class="entry-content"> {{ article.summary }} </div><!-- /.entry-content -->
</article></li>
{% endfor %}
</ol><!-- /#posts-list -->
{% if articles_page.has_other_pages() %}
{% include 'pagination.html' %}
{% endif %}
</div>
</div>
</div>
</section>
{% endblock content %}
You must have referenced your template from one of the articles using:
Template: blog
If you remove that reference and add the following lines to your pelicanconf.py, Pelican will generate blog.html directly from your template file:
DIRECT_TEMPLATES = ['index', 'blog']
PAGINATED_DIRECT_TEMPLATES = ['blog']
(Do not forget to empty your output folder before running pelican. Tested on Pelican 3.7.1)
For the sake of future visitors who might come here looking for an answer like I did:
The problem can have a good number of very diverse reasons. In my case it was not a problem with the configuration of the pelican tooling, but rather an error in the metadata of some of my content pages. I had not included the correct category, date or tag fields. You'd never guess that from the error message now, would you?
I found this question looking for the same error.
In my case the reason was an issue which has been closed but not merged in the current release of the Attila theme. More precisely: the error is caused by a template in the templates folder of the theme which has a wrong reference inside it. In the specific case, inside the page template there was a wrong reference to article.
Changing the template manually fixed the issue:
--- a/attila-1.3/templates/page.html
+++ b/attila-1.3/templates/page.html
## -21,8 +21,8 ##
{% else %}
{% set selected_cover = SITEURL+"/"+HEADER_COVER %}
{% endif %}
-{% elif article.color %}
- {% set selected_color = article.color %}
+{% elif page.color %}
+ {% set selected_color = page.color %}
{% elif HEADER_COLOR %}
{% set selected_color = HEADER_COLOR %}
{% endif %}
I hope this helps debugging similar errors.
The page variable articles_page is set in only one place: Writer._get_localcontext and there is a guard condition:
if paginated and template_name in self.settings['PAGINATED_TEMPLATES']:
# ... code ...
paginated_kwargs.update(
{'%s_paginator' % key: paginator,
'%s_page' % key: page, # <-- Creates `article_page`
'%s_previous_page' % key: previous_page,
'%s_next_page' % key: next_page})
If this problem crops up, the easiest solution is to make sure the guard condition evaluates to True. Most likely, the problem is that template_name is not in PAGINATED_TEMPLATES in your configuration file. I opened writers.py, added a print(f"template_name is {template_name}") and got my answer (I didn't have author : None in my PAGINATED_TEMPLATES dictionary).

Flask & Bootstrap Multiple collapsibles, but they each only open the first

I have an HTML page displaying a database populated by emails. I have them displayed in a collapsible, and for each post the timestamp of it is what toggles it and the innards are the email itself. The HTML page is structured like this:
{% extends "base.html" %}
{% block content %}
{% for email in emails %}
<div><button class="btn" data-toggle="collapse" data-target="#demo">{{ email.timestamp }}</button>
<div id="demo" class="collapse">
{{ email.body }}
</div>
{% endfor %}
{% endblock %}
relevant portion of views.py
#app.route('/cruz')
def cruz():
u = Politician.query.get(1)
emails = u.emails.all()
return render_template('cruz.html',title='Ted Cruz',emails=emails)
which produces a webpage that looks like this: http://imgur.com/noqC40E
The problem is that no matter which of those timestamps I click, only the first collapsible opens and closes. I've tried a number of things to fix it, mostly messing around with the HTML page and the for blocks and where I place the {{ email.body }}, but nothing I do seems to work. Can anyone see where this is going wrong?
You are generating the same id attribute for your div each time:
<div id="demo" class="collapse">
You almost certainly need to generate unique ids. You could generate unique ids by adding the loop index perhaps:
<div id="demo-{{loop.index}}" class="collapse">

URLs being produced correctly but not directing to correct template

I'm trying to follow the Simple Blog tutorial at Django By Example. I've managed to get as far as producing a site that loads correctly, but while the index view is loading find, and the links to the individual posts show up and appear to be formatted correctly, they point back to the index template so all that happens when you click on them is that it reloads the index view. I'm new to Django and the tutorial is sparse to say the least and not helped by the fact it's written for an old version of Django and I'm using 1.5. I've been staring at it all afternoon and I'm pretty lost.
Here's my urls.py
from django.conf.urls import patterns, url
from blog import views
urlpatterns = patterns('blog.views',
#index
(r"$", 'main'),
#ex: /1/
(r"^(\d+)/$", 'post'),
#ex: /add_comment/1/
(r"^add_comment/(\d+)/$", 'add_comment'),
)
my views.py
from blog.models import Post, PostAdmin, Comment, CommentAdmin
class CommentForm(ModelForm):
class Meta:
model = Comment
exclude = ["post"]
def main (request):
"""Main Listing."""
posts = Post.objects.all().order_by("-created")
paginator = Paginator(posts, 10)
try: page = int(request.GET.get("page", '1'))
except ValueError: page = 1
try:
posts = paginator.page(page)
except (InvalidPage, EmptyPage):
posts = patinator.page(paginator.num_pages)
return render_to_response("blog/list.html", dict(posts=posts, user=request.user))
def post (request, pk):
"""single post with comments and comment form"""
post = Post.objects.get(pk=int(pk))
comments = Comment.objects.filter(post=post)
d = dict(post=post, comments=comments, form=CommentForm(), user=request.user)
d.update(csrf(request))
return render_to_response("blog/post.html", d)
and the list.html that contains the links that aren't going anywhere!
{% extends "blog/bbase.html" %}
{% block content %}
<div class="main">
<!-- Posts -->
<ul>
{% for post in posts.object_list %}
<div class="title">{{ post.title }}</div>
<ul>
<div class="time">{{ post.created }}</div>
<div class="body">{{ post.body|linebreaks }}</div>
<div class="commentlink">Comments</div>
</ul>
{% endfor %}
</ul>
<!-- Next/Prev page links -->
{% if posts.object_list and posts.paginator.num_pages > 1 %}
<div class="pagination" style="margin-top: 20px; margin-left: -20px; ">
<span class="step-links">
{% if posts.has_previous %}
newer entries <<
{% endif %}
<span class="current">
Page {{ posts.number }} of {{ posts.paginator.num_pages }}
</span>
{% if posts.has_next %}
>> older entries
{% endif %}
</span>
</div>
{% endif %}
</div>
{% endblock %}
The Django URL resolver will return the first URL pattern that matches the incoming request. The regex for your 'main' view r"$" will match ANY incoming request since you are only looking for $ which is an end of string character.
You need to alter your 'main' URL regex to be r'^$'.
Alternatively, if you would like a catch-all view, you could move the 'main' view to the bottom of your URLs

Jinja - Is there any built-in variable to get current HTML page name?

i'm very new to Jinja and Flask
I want to set different background color in the navigation bar to indicate the current page.
Is there any built-in Jinja variable or method that returns current HTML pages? If possible, I want the code that doesn't need to communicate with the Python file.
So if i'm currently in index.html, it will return "index" or "index.html"
Here's my navigation code in my template:
<ul>
{% for item in navigation %}
<a href="{{url_for(item.route)}}">
<li>
{{item.text}}
</li>
</a>
{% endfor %}
</ul>
I want to add if statement so the current page will get <li> that has class
{% if ??? %}
<li class="current">
...
</li>
{% else %}
...
{% endif %}
Thank You
There is a trick in jinja2 document for your problem: http://jinja.pocoo.org/docs/tricks/
If your list is simple enough, just using request object, something like that:
<li {% if request.endpoint == item.endpoint %} class='active' {% endif %}>
{{item.text}}
</li>
Normally, I write this snippet to a macro with an explicit argument to set active:
{% macro render_sitem(endpoint, display, cls='', icon-cls='', active='') %}
<li {% if request.endpoint == endpoint or active == endpoint %} class='active' {% endif %}>
<a class='{{cls}}' href="{{url_for(endpoint)}}"><i class="{{icon-cls}}"></i> {{display}}</a>
</li>
{% endmacro %}
The list will be something like:
<ul class="nav nav-list">
{{render_sitem('page.index', _('Pages'), icon-cls='icon-sitemap', active=active_page)}}
{{render_sitem('post.index', _('Posts'), icon-cls='icon-file', active=active_page)}}
{{render_sitem('user.index', _('Users'), icon-cls='icon-group', active=active_page)}}
</ul>
So if you have a child page which extends or includes your list, you can set active item like:
{% set active_page = 'page.index' %}
in the top of your child page.
In pyramid 1.5 there are no method like request.endpoint in Flask.
We use custom filter get_endpoint
request.path|get_endpoint
jinja2_custom_filters.py:
from pyramid_jinja2 import Environment
def get_endpoint(str):
"""
:param str:
:return:
"""
return str.split('/')[-1]
env = Environment()
env.filters['get_endpoint'] = get_endpoint
and in development.ini:
jinja2.filters =
model_url = pyramid_jinja2.filters:model_url_filter
route_url = pyramid_jinja2.filters:route_url_filter
static_url = pyramid_jinja2.filters:static_url_filter
get_endpoint = path to ... jinja2_custom_filters.get_endpoint
Maybe it will be useful to someone :)
In Flask 2.0.1, the request object is available in the template. With this you can easily use it to check the page using the request.path attribute.
An example of a check would be like this:
{% if request.path == "/" %}
<h1>You are at the root</h1>
{% endif %}

Categories