Django {% url %} reverse not working - python

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 %}

Related

Frozen-Flask pagination links doesn't load the new page

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/.

Conditionally extending a template in Django

I have a download page on a Django site that I want to serve for both users who are logged in and who aren't. Instead of having a user_download.html and login_download.html, I want to have a single download.html that conditionally extends the correct base.
However, I get an error when I use the following code.
{% if user.is_authenticated %}
{% extends 'user_base.html' %}
{% else %}
{% extends 'login_base.html' %}
{% endif %}
{% block content %}
<h2>Downloadable content</h2>
...
{% endblock %}
The error I receive is
TemplateSyntaxError at /download/
Invalid block tag: 'else'
What's wrong with the else? I tried
{% if user.is_authenticated %}
{% extends 'user_base.html' %}
{% else %}{% if AnonymousUser.is_authenticated %}
{% extends 'login_base.html' %}
{% endif %}{% endif %}
{% block content %}
<h2>Downloadable content</h2>
...
{% endblock %}
but this didn't work, either.
Thanks,
erip
The {% extends %} tag supports variables. See the doc for reference.
def my_view(request):
if request.user.is_authenicated
base_template_name = 'user_base.html'
else:
base_template_name = 'login_base.html'
# Pass base template name to the renderer
return render_to_response('your_template.html', {'base_template_name':base_template_name})
Template (please note that the value is not quoted):
{% extends base_template_name %}
...
You're getting an error because extends needs to be defined at the top of the template. extends controls template inheritance: you are basically creating a subclass from some parent class, which is why extends needs to be the first thing in the template.
Imagine writing a class, and in the __init__() you said something like
class DoesntKnowWhereToInheritFrom(object):
def __init__():
if something:
self.inherits_from(x)
else
self.inherits_from(y)
The compiler/interpreter would freak out.
The common way to do what you are trying to do here is to check for is_authenticated in the view, and then render the appropriate template.

Rendering Django template from withing template

So, I have a number of objects I wish to render in a loop. I.E. Render each of the 5 latest posts on the home page. Each of these posts will be displayed differently whether or not the user is logged in.
I have a question: How would I go about making this distinction? I imagine having a template like this
{% if user.is_logged_in %}
{% for post in latest_posts %}
post.render_long_form
{% endfor %}
{% else %}
{% for post in latest_posts %}
post.render_short_form
{% endfor %}
{% endif %}
How can I make the functions render_short_form and render_long_form return the appropriate HTML snippits? I would like them to call other templates for rendering under the hood.
Thanks!
Why don't not use {% include %} tag?
{% if user.is_logged_in %}
{% for post in latest_posts %}
{% include 'long_form.html' %}
{% endfor %}
{% else %}
{% for post in latest_posts %}
{% include 'short_form.html' %}
{% endfor %}
{% endif %}
Or, more DRY version:
{% for post in latest_posts %}
{% if user.is_logged_in %}
{% include 'long_form.html' %}
{% else %}
{% include 'short_form.html' %}
{% endif %}
{% endfor %}

Django flatpages template tag doesn't work with template inheritance

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.

Django: set default behavior in include template

I'm looking to set a default behavior in a template to be included.
I have a problem with Django template system not allowing to set variables in template (I've read about the Django Philosophy, and I understand it).
Here is my example problem:
I want to include a template to render a newsfeed:
template.html:
...
{% include "_newsfeed.html" with slicing=":20" %}
...
I would like to not be forced to enter the slicing argument, and set a default behavior, let's say ":20"
In my _newsfeed.html , I would like to do (pseudo-code, it doesn't work):
_newsfeed.html:
...
{% if not slicing %}{% with slicing=":20" %}{% endif %}
{% for content in newsfeed_content|slice:slicing %}
{# Display content #}
{% endfor %}
{% if not slicing %}{% endwith %}{% endif %}
Instead, I end up doing this below, that doesn't follow the DRY rule (and doesn't satisfy me!):
_newsfeed.html:
...
{% if not slicing %}{% with slicing=":20" %}
{% for content in newsfeed_content|slice:slicing %}
{# Display content #}
{% endfor %}
{% endwith %}{% else %}
{% for content in newsfeed_content|slice:slicing %}
{# Display content #}
{% endfor %}
{% endif %}
How should I do ?
If you want to do this via your template not your views file, you could create your own filter based on slice e.g.
from django.template.defaultfilters import slice_filter
#register.filter("slice_default", is_safe=True)
def slice_filter_20(value, arg=":20"):
return slice_filter(value, arg)

Categories