I am trying to freeze my Flask blog app with Frozen Flask but the problem is I can't get the pagination to work correctly after the freeze().
I'm using the app factory pattern.
Here's my main.routes.py:
#bp.route('/home')
#bp.route('/index')
#bp.route('/')
def index(form=None, methods=['GET', 'POST']):
latest_posts = load_latest_posts(10)
with db_session(autocommit=False):
page = 1
posts = load_all_posts().paginate(page, 10, False)
next_url = url_for('main.index', page=posts.next_num) \
if posts.has_next else None
prev_url = url_for('main.index', page=posts.prev_num) \
if posts.has_prev else None
if current_user.is_anonymous:
return render_template('main/index.html', title='Home', posts = posts,
prev_url=prev_url, next_url=next_url, latest_posts=latest_posts)
load_all_posts() does what is says, returning Post.query.order_by(Post.pub_date.desc())
load_latest_posts(n) is basically the same but fetches the latest (n) posts.
As you see, I'm passing the pagination object to posts which I use in my main/index.html template to render the pagination items:
{% extends 'base.html' %}
{% block posts_preview %}
{% for post in posts.items %}
{% include 'posts/_post.html' %}
{% endfor %}
{% endblock posts_preview %}
{% block footer %}
<ul class="pagination">
{% if prev_url %}
<li>«</li>
{% endif %}
{% for page_num in posts.iter_pages(left_edge=1, right_edge=1, left_current=2, right_current=3) %}
{% if page_num %}
{% if posts.page == page_num %}
<li><a class="active" href="{{url_for('main.index', page=page_num) }}">{{ page_num }}</a></li>
{% else %}
<li>{{ page_num }}</li>
{% endif %}
{% else %}
...
{% endif %}
{% endfor %}
{% if next_url %}
<li>»</li>
{% endif %}
</ul>
{% endblock footer %}
_post.html is nothing fancy, just another template that includes post structure.
If I run this in Flask, it works without a problem. When generating static site with Frozen Flask, page numbers are there but clicking on them wouldn't redirect me anywhere. I see the URL being changed from http://127.0.0.1:5000/ to http://127.0.0.1:5000/?page=2 but the new content doesn't load, only refreshing the current page.
What might be the issue here ? How can I load pages and pagination correctly?
According to the Frozen Flask documentation on how the filenames are generated:
Query strings are removed from URLs to build filenames. For example,
/lorem/?page=ipsum is saved to lorem/index.html. URLs that are only
different by their query strings are considered the same, and they
should return the same response. Otherwise, the behavior is undefined.
This means that, unfortunately, http://127.0.0.1:5000/ and http://127.0.0.1:5000/?page=2 will refer to exactly the same page. To get pagination to work, you'd need to make sure that the page number was part of the URL before the query string - something like http://127.0.0.1:5000/page2/.
Related
Hy, i am very new to web dev and jinja2. i encountered a problem with jinja, when i render template and want to output user name to web page who loged in...well i cant get one. Here is my index.html file with jinja expression in tag:
{% extends "base.html" %}
{% block content %}
{% if user %}
<h1>Welcome to Flask authentication site, {{ user.name }}!</h1>
{% else %}
<h2>Hello, log in or sign up</h2>
{% endif %}
{% endblock content %}
I have recently started working on some software that has been written in Python/ Django, and I am looking to change what information is displayed on one of the webpages.
The page currently displays a number of tabs, with different information from the database displayed on each tab. The tabs are titled: Overview, Budget, Works, & Items.
On the Items tab, there are four columns: Items, Initial Sum, Latest Sum, & Notes.
There are views in the Python code that are used to set up what's displayed on each tab, and the one for the Items tab that I want to change is defined with:
def report_ccis(request, project_id):
""" CCI items styled for pdf """
project = Project.objects.get(id=project_id)
budget = get_current_budget(project_id)
cci_total_exc = budget.cci_total_exc_vat_final
cci_grouped_items = budget.cci_items.all().order_by('project_room', 'name')
context = {
'project': project,
'cci_total_exc': cci_total_exc,
'cci_grouped_items': cci_grouped_items,
'webview': 1,
}
try: context['current_budget'] = project.budget_versions.get(current_marker=1) #For option name/date on top of pdfs
except ObjectDoesNotExist: pass
if request.GET.get('stage') == 'pd':
""" Render post deposit homepage """
context['html'] = render_to_string('costing/report2_ccis.html', context)
context['active_tab'] = '4'
return render(request, 'costing/reports_post_deposit.html', context)
else:
""" Render pre deposit homepage """
context['html'] = render_to_string('costing/report_ccis.html', context)
context['active_tab'] = '5'
return render(request, 'costing/reports_pre_deposit.html', context)
I want to remove the 'Latest Sum' column from this tab in the browser, but I'm not sure how to stop the view that's displaying the 'Items' tab from getting/ displaying that information on the tab...
The HTML files that are being populated with by this view have code similar to the following:
{% extends "pdf2_base.html" %}
{% block web_content %}
{% block content_payment_schedule %}
{% endblock content_payment_schedule %}
{% block content_overview %}
{% endblock content_overview %}
{% block content_ccis %}
{{block.super}}
{% endblock content_ccis %}
{% block agreed_variations %}
{% endblock agreed_variations %}
{% block agreed_variations_client %}
{% endblock agreed_variations_client %}
{% block agreed_variations_construction %}
{% endblock agreed_variations_construction %}
{% block unagreed_variations %}
{% endblock unagreed_variations %}
{% endblock web_content %}
As I understand, the HTML used to render the webpages in the browser is generated by the Python variables in the .html file- this is the part that is quite new to me... if it was just plain HTML, I would know how to locate the information that I want to remove from the page, but I can't find where it's being displayed in the HTML files here... or how it's being retrieved by the Python view...
If I 'inspect' the part of the page that I want to remove in the browser, it shows that it is in following structure:
<body>
...
<div class="wrapper">
...
<div class="content">
<div class="tabbed m-r-lg text-sm">
...
<pdf:nexttemplate name="nofooter">
<pdf:nextpage>
...
<pdf:nextpage>
<pdf:nextpage>
<table class="pdf-report left">
<thead>
<tr>
...
<th>Latest Sum (£)</th> == $0
...
</tr>
</thead>
<tbody></tbody>
</table>
...
</pdf:nextpage>
</pdf:nextpage>
</pdf:nexttemplate>
</div>
</div>
</div>
</div>
...
</body>
As is shown by the the HTML, the content that I want to change is displayed in a PDF embedded within the webpage, but obviously, I need to change it via the Python- I'm just not sure how I would do this, as I can't see anything in the Python that references the 'latest sum' field that I want to remove...
Can anyone point me in the right direction?
Edit
The child template for the web page is:
{% extends "costing/reports_tabbed.html" %}
{% load staticfiles utilities %}
{% block title2 %}
| Pre-deposit reports
{% endblock title2 %}
{% block page_title %}
<a id="topbar-shortcuts" data-view-url="{% url 'hub:open_sidebar' %}?app={{app.name}}&p={{project.id}}&po=1">
<span class="m-l-md">Reports</span> <img class="icon open text-sm m-l-md" src="{% static 'img/down-lt.png' %}" >
</a>
<div id="topbar-results" class="{{app.color}}" style="display:none;"></div>
{% endblock page_title %}
{% block tabs %}
{% with 'Overview, budget, works, items'|listify as tabs %}
{% for tab_name in tabs %}
{% with forloop.counter as tab %}
{% if not tab == active_tab|add:0 %}<a class="tab" href="{% url 'costing:report_tabbed' project.id %}?tab={{tab}}">{% else %}<a class="active tab">{% endif %}{{tab_name}}</a>
{% endwith %}
{% endfor %}
{% endwith %}
{% endblock tabs %}
The column I want to remove is displayed on the items tab, but I can't see anything in the view/ HTML that specifies what is displayed on each tab...
I have a Django (1.6) application that inherits a base template. I would like to include one of my (currently working) flatpages into the application landing page, something that the Django docs say is possible.
Here is my template:
{% extends "path/to/base.html" %}
{% load flatpages %}
{% get_flatpages as fp %}
{% block content %}
<h3>Flatpage inclusion</h3>
<p>Number of flatpages: {{ fp|length }}
<ul>
{% for page in fp %}
<li>{{ page.title }}</li>
{% endfor %}
</ul>
{% endblock content %}
This does not list any of the flatpages. However, if I remove the {% extends %} signal, so my code looks like this:
{% load flatpages %}
{% get_flatpages as fp %}
<h3>Flatpage inclusion</h3>
<p>Number of flatpages: {{ fp|length }}
<ul>
{% for page in fp %}
<li>{{ page.title }}</li>
{% endfor %}
</ul>
Everything works. I see the number of flatpages in my fp object (9) and my unordered list shows all the flatpage urls and titles.
This seems to me to be a bug in either how flatpages work, or how Django does template inheritance.
The base template (/path/to/base.html) doesn't have anything complex in it.
Django categorically says that this is possible:
When you load a custom tag or filter library, the tags/filters are only made available
to the current template – not any parent or child templates along the template-inheritance path.
For example, if a template foo.html has {% load humanize %}, a child template (e.g., one that
has {% extends "foo.html" %}) will not have access to the humanize template tags and filters.
The child template is responsible for its own {% load humanize %}.
This is a feature for the sake of maintainability and sanity.
Has anyone else noticed this bug? Is it an exception for just the built-in flatpages app?
EDIT 1:
Daniels answer is correct. The example code from the Django docs doesn't show including flatpage content within a {% block %}, so I didn't expect that it needed to be done:
{% load flatpages %}
{% get_flatpages as flatpages %}
<ul>
{% for page in flatpages %}
<li>{{ page.title }}</li>
{% endfor %}
</ul>
My fault I guess. Live and learn.
The problem is that your get_flatpages tag is outside any blocks from the parent template. That means it simply won't be called.
Move it into the content block and it should work.
Just to reiterate the correct solution- get_flatpages needs to be placed inside the block where it's going to be referenced. So this will work:
{% extends "index.html" %}
{% load flatpages %}
{% block footer %}
{% get_flatpages as flatpages %}
{% for page in flatpages %}
...
{% endfor %}
And this will not work:
{% extends "index.html" %}
{% load flatpages %}
{% get_flatpages as flatpages %}
{% block footer %}
{% for page in flatpages %}
...
{% endfor %}
And yes, Django documentation isn't very clear on that.
I have a small Flask app which renders blog posts:
views.py:
class ListView(MethodView):
def get(self, page=1):
posts = Post.objects.all()
return render_template('posts/list.html', posts=posts)
This is all good, but I would like to add pagination to the posts object. Looking at the project docs, I see there is a pagination class.
So I tried this:
class ListView(MethodView):
def get(self, page=1):
posts = Post.objects.paginate(page=page, per_page=10)
return render_template('posts/list.html', posts=posts)
But now I get an error:
TypeError: 'Pagination' object is not iterable
So how do I iterate through my posts in the template?
The Pagination object has an items list which will contain the mongoengine document objects (in your case the Post objects). This list can be iterated through to display the documents.
For example, in your template:
{% for post in posts.items %}
{{ post.title }}
{{ post.content }}
{% endfor %}
To get the actual page numbers for pagination links, use iter_pages():
<div id="pagination-links">
{% for page in posts.iter_pages() %}
{{ page }}
{% endfor %}
</div>
Both the documentation and the github link above, have a better example for pagination links:
{% macro render_pagination(pagination, endpoint) %}
<div class=pagination>
{%- for page in pagination.iter_pages() %}
{% if page %}
{% if page != pagination.page %}
{{ page }}
{% else %}
<strong>{{ page }}</strong>
{% endif %}
{% else %}
<span class=ellipsis>…</span>
{% endif %}
{%- endfor %}
</div>
{% endmacro %}
I have a view in a Django 1.4 project:
def index(request):
print reverse('menus_index')
latest_menu_list = Menu.objects.all().order_by('name')
return render_to_response('menus/index.html', {'latest_menu_list': latest_menu_list})
This works as expected and prints out the reversed URL which is /menus/.
Inside of the index.html template (which is called by this view) I have:
{% url menus_index %}
Which causes a NoReverseMatch at /menus/ error. Reverse for '' with arguments '()' and keyword arguments '{}' not found.
My application's urls.py is:
urlpatterns = patterns('menus.views',
url(r'^$','index', name='menus_index'),
url(r'^(?P<menu_id>\d+)/$','detail', name='menus_detail'),
)
Which is included in my project's urls.py file.
What am I doing wrong?
Update:
Here is the full index.html template code:
{% extends "base.html" %}
{% load url from future %}
{% block title %}
Menu Index
{% endblock %}
{% block content %}
{% if latest_menu_list %}
<ul>
{% for menu in latest_menu_list %}
<li>{{ menu.name }}</li>
{% endfor %}
</ul>
{% else %}
<p>No menus are available.</p>
{% endif %}
{% endblock %}
Answer: use {% url 'menus_index' %}. That {% load url from future %} makes the quotes a requirement per https://docs.djangoproject.com/en/1.4/ref/templates/builtins/#url
Maybe this not menus_index? Paste full template code this.
You should use add variable for reverse, somethink like this: {% url "menus_index" menu.slug %}