Django how to define common url - django

I am implementing a directory service and as you know listing URLs appear on a lot of places. I am aware of using the {% url %} tag, it's still not bullet proof for cases like global listing url structure changes, say I had {% url id=listing.id %} and had to add slug to the URL like {% url id=listing.id slug=listing.slug %}
While global search & replace is an option, however I am curious if there is a canonical way
to approach this issue.
Currently my approach is have a listingurl.html which only has {% url id=listing.id slug=listing.slug %} and wherever needs to have the url will just include listingurl.html, however I am not sure if implementing a custom filter would be more efficient?

Not 100% sure this works and if it is the most elegant solution:
some_template.html
{% include 'listing.html' with url_thing %}
listing.html
{% url url_thing id=object.id %}

The best way to handle it IMHO is to add a get_absolute_url method to your model. Then, instead of working about reversing the URL in the template, you can just call that method:
#models.permalink
def get_absolute_url(self):
return ('listing_view_name', {'id': self.id, 'slug': self.slug})
Then:
{{ listing }}

Related

Check and clear filters with django-filter

I am using django-filter to filter a ListView and would like to display a "Clear all filters" link if any filters are applied.
Due to the generic nature of the filtering system I haven't yet found a straightforward way to achieve this.
The only thing I came up with so far is to return the regular queryset in the get_queryset method of the view if a "clear" flag is present in the request, however this doesn't actually clear the filters - it just returns all the data.
Does anyone have a solution/idea for this?
Update: Solution
After Jerin's comment I decided to solve this problem in 2 separate parts:
has filter:
I check if any of the fields I defined in my filter class are in the request. My solution looks a bit different as I'm using class based views so I abstracted it away in a mixin but if you're using simple views like here, you could just do:
def product_list(request):
f = ProductFilter(request.GET, queryset=Product.objects.all())
has_filter = any(field in request.GET for field in
set(f.get_fields()))
return render(request, 'my_app/template.html', {
'filter': f,
'has_filter': has_filter
})
clear all filters:
A simple redirect to your list view:
{% if has_filter %}
{% trans 'Clear all filters' %}
{% endif %}
Here is the mixup version of the answer (combination of mine and Chris)
You could place a Clear all filters button and that will redirect to your default ListView (/host/end/point/).
But some non-filter parameters (such as pagination or something else) may occur in URL. So the better option is, check for any filter fields in URL and if so, display the filter clearing link
The opted solution is,
def product_list(request):
f = ProductFilter(request.GET, queryset=Product.objects.all())
has_filter = any(field in request.GET for field in set(f.get_fields()))
return render(request, 'my_app/template.html', {
'filter': f,
'has_filter': has_filter
})
and in template,
{% if has_filter %}
{% trans 'Clear all filters' %}
{% endif %}
Just make a button and point to the base search field.
<a class="btn btn-warning" href="{% url 'App:FilterView' %}">Reset</a>
If your FilterSet instance is available on the template you can check for filter.is_bound like this:
{% if filter.is_bound %}
Clear filters
{% endif %}
If you are using the FilterMixin or the FilterView, your FilterSet instance will be available as filter to the template as above.
I like this simple solution, however when I attempt to use it the current filter parameters are some how getting appended to the url even though it's the base url in the anchor.
So hovering over the button my link (determined using {% url 'app:view' %} shows
localhost/app/view correctly
However when clicking the button the url in browser has the parameters appended
localhost/app/view/?filter1=val1&filter2=val2 etc.
Is django caching something? Is browser (Chrome) caching? Can I force something in the anchor to not use them?
Answered my own, but for anyone else passing by:
I had the anchor on a button within a the filter form, although it was not a submit button moving it outside the form gave the desired result.

Add dynamic content

After a new article is posted (in a form via Ajax) on my Django news site, I want to return a link to the article.
To do so, I'm using a Template object that says:
if form.is_valid():
form.instance.user = request.user
new_article = form.save()
success = Template('<div id="panel_input" class="col-lg-4"> <h2 class="text-center"> Success </h2> <p class="text-center"> Your Article has been posted. You can see and edit details by clicking here. </p></div>')
context = Context({"article_id" : new_article.pk})
return HttpResponse(success.render(context))
The urlsConf for this looks like:
...
url(r'^article/(?P<article_id>\d+)/$', views.article, name='article'),
...
The problem is that I get an error because of {% url "article_manager:article" %}/{{ article_id }}. Apparently, I must pass the article_id inside the previous tag, since the urlsConf requires the id parameter.
But I also get an error when I put the second tag inside the first, like this:
{% url "article_manager:article" {{ article_id }} %}
I'm not sure how to accomplish this task, it doesn't seem to work with the tools I have. Does anyone have any suggestions?
Try {% url "article_manager:article" article_id=article_id %}
Maybe a little more explanation is needed: You were calling the template tag right {% url "namespace:name" %}. Remember that some templatetags can take arguments, in the *args, **kwargs form. The args can be any simple expression understood by the template language, including a context variable (no need to add double-braces). The kwargs follow the same rule, and have the form argument=expression. Thus, you can call some template tags with the form {% tag "exp" 1 request number=5 username=user.name %}

How to store the result of a templatetag using Django templates?

Working with Django 1.5.5 I need to call a custom templatetag and somehow store the result in a variable, to check if it contains a non empty empty string. I need something that should look like:
{% load smart_html %}
{% render_html widget.content as widget_content %}
{% if widget_content %}
Do stuff here...
{% endif %}
This is inspired on the {% url %} built-in templatetag that allows calling it storing the result in a variable using the syntax:
{% url 'named_url' as my_named_url %}
My templatetag looks like:
#register.simple_tag(takes_context=True)
def render_html(context, html):
"""Allows executing 'Django code' within the HTML"""
return Template(html).render(context)
I also thought about adding the returned value from the custom templatetag to the context. What do you think about this? Would this be dangerous? This would look like:
#register.simple_tag(takes_context=True)
def render_html(context, html, var_name=None):
"""Allows executing 'Django code' within the HTML"""
html = Template(html).render(context)
if var_name:
context[var_name] = html
html = ''
return html
If the tag is something you control, then perhaps consider using an assignment tag. If the tag isn't something you control, then you might have to wrap it with an assignment tag of your own.
#register.assignment_tag(takes_context=True)
def render_html(context, content):
return Template(content).render(context)
But I don't know what you are trying to achieve? Isn't it better to do this kind of stuff in your view function and based on the result call different templates with TemplateResponse?

Django functions issue and urlconf confusion

I couldn't place my question into one sentence.
I was following a tutorial to make a blog from scratch. But the tutorial predicted having all the categories, tags, months and years listings in separate templates.
I want to add a list of categories, a list of months and years on the main blog page.
So here is what I got. With this code, the list of categories is shown in the main page but only if you go to the blog/category url, and not in just blog/ where I want it to be.
**(r'^$', list),**
(r'^archive/(\d{1,2})/$', list),
(r'^\d{2}/d{4}/$', month),
(r'^([0-9]{4}/\d{1,2})/(?P<slug>.*)/$', detail),
(r'^(?P<year>\d{4})/$', year),
**(r'^category/$', category),**
I also tried:
(r'^$', category),
but no luck.
This is the same code from the template in category.html and in list.html:
{% if categories %}
{% for category in categories %}
<li class="cat-item"><a href="category/{{ category.name.lower }}/"
title="{{ category.name.capitalize }}">
{{ category.name.capitalize }}</a>
</li>
{% endfor %}
{% endif %}
Views.py:
def category(request):
return render_to_response('blog/list.html', {'categories':Category.objects.all(),},)
It was like this. I tried this, but no luck in def list:
return render_to_response('blog/list.html',{'posts':posts,
'next':next,
'previous':previous,
'categories':Category.objects.all(),
},)
How can I get what shows on blog/category to show on blog/ also?
Thanks.
When you type an url in your browser a request is sent to your server. Django then takes the url and matches it against it url patterns to determine the proper view. As soon as a view is found django stops matching and executes this view which in turn returns a response.
If you want to use your categories in different views you either have to to make sure that in every view the same categories context variable is provided to the template or, which is usually much better, write a simple custom template tag. For your categories this could look like this:
#register.inclusion_tag('blog/category_list.html')
def categories_list():
return { 'categories': Category.objects.all() }
In the file 'blog/categoy_list.html' would then be the code that is currently both in 'categories.html' and 'list.html'. In these files replace it with.
{% load your_blog_tags %}
{% categories_list %}
You can use that then wherever you need a category list. Of course the same applies for years and month lists.

Django custom tag 'takes_context'

I`m new with django (came from Grails), especially with all those custom tags that you have to deal with, instead of writing your variables directly inside the templates.
Well, what I need to do was something really simple, but for some reason is taking me a long time to finish. What I wish to do was make a tag that checks for me if the given path is equals my current url, and then returns the class if true.
<li class="{% check_url '/login/' 'current_page_item' %}">
login
</li>
But, the problem came when I tried to register the tag with takes_context :
Caught TypeError while rendering: simple_tag() got an unexpected keyword argument 'takes_context'
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def check_url(context, path, attr):
if context['request'].environ.get('PATH_INFO') == path:
return attr
else:
return ''
How can I fix it? Also, is there a better way to do it?
That's because takes_context is only available since django 1.3.
Another approach to do it (and to avoid hardcoded urls):
{% url social_login as the_url %}
{% ifequal the_url request.path %}
....
{% endif %}
Or check out something like this!