identical views different URLs - django

I have following routs:
url(r'^future/programs/$', main.programs, {'period': 'future'}),
url(r'^past/programs/$', main.programs, {'period': 'past'}),
When I try to display link in template, using template tag url like this
{% url main.views.main.programs %}
I always get link /past/programs/. When I try like this
{% url main.views.main.programs period="future" %}
I get an error:
Caught NoReverseMatch while rendering: Reverse for
'main.views.main.programs' with arguments '()' and keyword arguments
'{'period': u'future'}' not found.
How i can display link to /future/programs/?

I think you might want to approach it with one single url pattern:
url(r'^(?P(<period>[\w]+)/programs/$', main.views.programs),
and in your view:
def programs(request, period):
if period == 'future':
...
elif period == 'past':
...
and in templates:
{% url main.views.main.programs period="future" %}
In your approach, you are mistaking the forward flow with the reverse flow, i.e. the extra keyword arguments of the url conf with the keyword arguments that are passed to match a pattern.
The former is extra data you are allowed to pass to a view when it is matched (i.e. when a user goes to /future/programs/, the pattern is matched and period=future is passed to the view), the latter is the actual data used to match the url (i.e. the period=future is passed to the reverse() function which tries to match a pattern that excepts those keyword arguments - which you haven't outlined)
Edit:
A more appropriate pattern to use in your url would be something like:
url(r'^(?P(<period>past|future)/programs/$', main.views.programs),
where the selection could only be 'past' or 'future'. This is fine for incoming urls, but django's reverse() function (which is used in the url template tag) can't handle alternative choices:
https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse
The main restriction at the moment is that the pattern cannot contain alternative choices using the vertical bar ("|") character.

I would rather assign each url a name:
url(r'^future/programs/$', main.programs,
{'period': 'future'},
name='future_programs'),
url(r'^past/programs/$', main.programs,
{'period': 'past'},
name='past_programs'),
And to display the link in your template:
Past programs: {% url past_programs %}
Future programs: {% url future_programs %}
I think this solution is better because if you just have two options for that view, you can forget about passing parameters and validating them.
Now, if those two options (future, past) can grow into several more, the other solution would be better, but I think this is not the case.

Related

Django - reverse not found for included URL pattern

I've got the following in an old Django 1.8 (sorry...) site:
url(r"^racing/(?P<type>(dog|horse|pigeon))/", include("racing.urls")),
which pulls in the racing URLs:
urlpatterns = patterns("racing.views",
url(r"^(?P<season>\d+)/$", "season_view", name="season_home"),
}
The view function defines a default for the type argument:
def season_view(request, season, type="horse")
Which, to my mind, means I should be able to (in a template) do:
{% url "season_home" season=nav.season.year %}
But it's throwing an error:
Reverse for 'season_home' with arguments '()' and keyword arguments '{u'season': 2019L}' not found. 1 pattern(s) tried: [u'racing/(?P<type>[-\\w]+)/(?P<season>\\d+)/$']
What am I missing?
Your url pattern need 2 variables to be passed: type and season:
So you are passing only season and nothing for type.
Try to add type like:
{% url "season_home" type=my_type season=nav.season.year %}
where my_type is your type variable.
I can see that you are using default parameters in your views. So, you are expecting that if you are not passing a type parameter through your template, the type is set to horse as default.
Everything is okay but in your main url patterns, you need to be able to match the rules where no parameter is passed. You can try something like:
url(r"^/", include("racing.urls")),
url(r"^racing/(?P<type>(dog|horse|pigeon))/", include("racing.urls")),
This configuration helps you to capture both the cases when you supply a type parameter or not.

Django template 'url' filter encodes a substitution string

Using django1.9
my urls would look something like example.com/my_url/cachebuster
in my email.html template I have something like
{% url my_url cachebuster %}
Now cachebuster is actually "%%cachebuster%%"
This is a substitution string which my email api is parsing for, and whenever it finds "%%cachebuster%%" it replaces it with the specific users cachebuster.
My issue is that the django {% url %} function automatically encodes that string to "%25%25cachebuster%25%25"
Which means the api never finds "%%cachebuster%%" and never replaces it
So instead of a dynamic url, its always example.com/my_url/%25%25cachebuster%25%25
How can I fix this, without changing the substitution string? I've tried using mark_safe but haven't got it to work
The only way I can think of is to hardcode /my_url/{{cachebuster}} without using the django url tag, but this is bad practice because the url templatetag was created for a reason

Django NoReverseMatch error on URL

I'm trying to use a named regular-expression group to pass a keyword arguments to a view.
I'm very new to using regular-expressions (and Django) and keep getting a NoReverseMatch error.
In my template has a table with the following:
<td><i class="icon-forward"></i></td>
The URL that corresponds to "{% url profile player.user %}" is:
url(r'^profile/(?P<players_username>\w)/$', profile_page, name='profile'),
Also I'm not sure if "\w" was correct for this regex. Ideally I it could receive any character.
And my view "profile_page" is:
#login_required
def profile_page(request,players_username):
return render(request, 'portal/portal_partial_profile.html')
I'm not sure why I'm getting this error? I appreciate any feedback and expertise.
Since you are assigning the match to a variable (its a keyword), you need to pass it in, like this:
{% url profile players_username=player.user %}
Another minor correction, your regular expression should be \w+. The + means "one or more, but at least one", otherwise your regular expression will not work as you expect (unless all your players have one character usernames).

Django Generic object_list pagination. Adding instead of replacing arguments

I'm having some trouble with the django generic object_list function's pagination not really being "smart" enough to compensate my daftness.
I'm trying to do a url for listing with optional arguments for page number and category.
The url in urls.py looks like this:
url(r'^all/(?:(?P<category>[-\w]+)/page-(?P<urlpage>\d+))?/$',
views.listing,
),
The category and urlpage arguments are optional beacuse of the extra "(?: )?" around them and that works nicely.
views.listing is a wrapper function looking like this( i don't think this is where my problem occurs):
def listing(request,category="a-z",urlpage="1"):
extra_context_dict={}
if category=="a-z":
catqueryset=models.UserProfile.objects.all().order_by('user__username')
elif category=="z-a":
catqueryset=models.UserProfile.objects.all().order_by(-'user__username')
else:
extra_context_dict['error_message']='Unfortunately a sorting error occurred, content is listed in alphabetical order'
catqueryset=models.UserProfile.objects.all().order_by('user__username')
return object_list(
request,
queryset=catqueryset,
template_name='userlist.html',
page=urlpage,
paginate_by=10,
extra_context=extra_context_dict,
)
In my template userlist.html I have links looking like this (This is where I think the real problem lies):
{%if has_next%}
<a href=page-{{next}}>Next Page> ({{next}})</a>
{%else%}
Instead of replacing the page argument in my url the link adds another page argument to the url. The urls ends up looking like this "/all/a-z/page-1/page-2/
It's not really surprising that this is what happens, but not having page as an optional argument actually works and Django replaces the old page-part of the url.
I would prefer this DRYer (atleast I think so) solution, but can't seem to get it working.
Any tips how this could be solved with better urls.py or template tags would be very appreciated.
(also please excuse non-native english and on the fly translated code. Any feedback as to if this is a good or unwarranted Stack-overflow question is also gladly taken)
You're using relative URLs here - so it's not really anything to do with Django. You could replace your link with:
Next Page> ({{ next }})
and all would be well, except for the fact that you'd have a brittle link in your template, which would break as soon as you changed your urls.py, and it wouldn't work unless category happened to be a-z.
Instead, use Django's built-in url tag.
Next Page> ({{ next }})
To make that work, you'll have to pass your category into the extra_context_dict, which you create on the first line of your view code:
extra_context_dict = { 'category': category }
Is /all/a-z/page-1/page-2/ what appears in the source or where the link takes you to? My guess is that the string "page-2" is appended by the browser to the current URL. You should start with a URL with / in order to state a full path.
You should probably add the category into the extra_context and do:
next page ({{next}})
"Instead of replacing the page argument in my url the link adds another page argument to the url. The urls ends up looking like this "/all/a-z/page-1/page-2/"
that is because
'<a href=page-{{next}}>Next Page> ({{next}})</a>'
links to the page relative to the current url and the current url is already having /page-1/ in it.
i'm not sure how, not having page as an optional argument actually works and Django replaces the old page-part of the url
one thing i suggest is instead of defining relative url define absolute url
'Next Page> ({{ next }})'

Django - How to pass several arguments to the url template tag

In my urls.py I have:
(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/section/(?P<slug>[-\w]+)/$',
'paper.views.issue_section_detail',
{},
'paper_issue_section_detail'
),
and I'm trying to do this in a template:
{% url paper_issue_section_detail issue.pub_date.year,issue.pub_date.month,issue.pub_date.day,section_li.slug %}
but I get this error:
TemplateSyntaxError
Caught an exception while rendering: Reverse for 'paper_issue_section_detail' with arguments '(2010, 1, 22, u'business')' and keyword arguments '{}' not found.
However, if I change the URL pattern to only require a single argument it works fine. ie:
(r'^(?P<year>\d{4})/$',
'paper.views.issue_section_detail',
{},
'paper_issue_section_detail'
),
and:
{% url paper_issue_section_detail issue.pub_date.year %}
So it seems to complain when I pass more than a single argument using the 'url' template tag - I get the same error with two arguments. Is there a different way to pass several arguments? I've tried passing in named keyword arguments and that generates a similar error.
For what it's worth, the related view starts like this:
def issue_section_detail(request, year, month, day, slug):
How do I pass more than a single argument to the url template tag?
I had the same issue (I'm using Django 1.3.1) and tried Gregor Müllegger's suggestion, but that didn't work for two reasons:
there should be no commas between year, month and day values
my class-based generic view seems to take only keyword arguments
Thus the only working solution was:
{% url news_detail slug=object.slug year=object.date|date:"Y" month=object.date|date:"m" day=object.date|date:"d" %}
The problem lives in the /(?P<month>\d{2})/ part of your url configuration. It only allows exactly two digits (\d{2}) while issue.pub_date.month is only one digit.
You can do either allow also one digit in the URL (but this will violate the principle of unique URLs, /2010/1/... would be the same as /2010/01/...) or pass two digits to the month argument in your url templatetag.
You can use the date filter to achieve a consistent formating of date objects. Use the url tag like this:
{% url paper_issue_section_detail issue.pub_date|date:"Y",issue.pub_date|date:"m",issue.pub_date|date:"d",section_li.slug %}
Look at the month and day argument: It will be always displayed as two digits (with a leading zero if necessary). Have a look at the documentation of the now tag to see which options are possible for the date filter.
Your month expression is (?P<month>\d{2}), but you're sending it the argument 1. The 1 doesn't match \d{2}, so the url resolver isn't finding your view.
Try changing the month expression to \d{1,2} (or something to that effect).