Django: Passing model object values through url - django

I'm having problems with passing model object values through a URL pattern. The URL:
url(r'^cities/(?P<city>\w+)/$', 'city_firm', name='city_firm'),
In the template (from the index page) I have:
{{ city }}
This is in a for loop.
The related view is:
def city_firm(request, city):
city1 = Cities.objects.get(city=city)
cityf = city1.Firms.all()
return render_to_response('cityfirm.html', {'cityf': cityf})
The two models (Cities, Firms) are in a many to many relationship.
I keep getting TemplateSyntaxError at index (NoReverseMatch while rendering: Reverse for 'city_firm' with arguments '(<Cities: >,)' and keyword arguments '{}' not found). In the template link tag I tried: {% url city_firm city=city %}, {% url city_firm city=cities.city %}. Nothing changed. The urlconf part seems right. The problem seems to be in the template. Maybe there is an issue with the string values of the object as they aren't in English. But I took several precautions to prevent this. There is maybe something wrong with the view but the error says template. Any ideas?
Solution:
Thanks everyone! Finally I figured it out. The problem was simple: I was trying to send object attribute names through the url, that had non-English characters and spaces. To fix it, I had to edit my models.

The issue is that you can't pass an object in a URL, you can only pass characters. So you need to put the part of the city object that contains the text you want to be in the URL - in your case, it appears to be an attribute also called city, which is what you use to in the lookup to get the object in the view. So it should be:
{{ city }}

I don't think name means what you think it does - remove that and read this: http://docs.djangoproject.com/en/dev/topics/http/urls/#naming-url-patterns
As far as the error... the NoReverseMatch is telling you that it's not seeing any arguments. Remember that nonexisting template variables expand to "". Make sure city is in context when you're running that code - maybe post the for in the template?

Related

Django pass known exact string as a url parameter

I have two urls in dispatcher pointing the same view
path('posts/top/', posts, name='top'),
path('posts/new/', posts, name='new'),
I want view start as follows:
def posts(request, ordering):
...
I guess, to pass top and new as a parameter it should be something like:
path('posts/<ordering:top>/', posts, name='top'),
path('posts/<ordering:new>/', posts, name='new'),
But it gives me:
django.core.exceptions.ImproperlyConfigured: URL route 'posts/<ordering:top>/' uses invalid converter 'ordering'.
So, as a work around I use this, but it looks a little bit dirty:
path('posts/top/', posts, name='top', kwargs={'order': 'top'}),
path('posts/new/', posts, name='new', kwargs={'order': 'top'}),
What is the right way to do it?
You've misunderstood the way path converters work. The first element is the type, which here is str, and the second is the parameter name to use when calling the view. At this point you don't constrain the permissible values themselves. So your path would be:
path('posts/<str:ordering>/', posts, name='posts')
and when you come to reverse you would pass in the relevant parameter:
{% url 'posts' ordering='top' %}
If you really want to ensure that people can only pass those two values, you can either check it programmatically in the view, or you can use re_path to use regex in your path:
re_path(r'^posts/(?P<ordering>top|new)/$', posts, name='posts')

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

How to create a WordPress like URL naming convention in Django?

I'm a newbie in Django and in WordPress if you create a Post called "hello world" then the URL by default will be like
wordpress.com/2012/07/05/hello-world/
and if you create another post with the same name it will be
wordpress.com/2012/07/05/hello-world-2/
I want to achieve the same in Django and I was thinking to create a sample urlconf like this
(r'^articles/(\d{4})/(\d{2})/(?P<name>\w+)', 'article.views.article_detail')
and in the views break down the name and iterate through all the items and match the name.
But the problem with will be that I won't be able to reference posts dynamically. For e.g. if I was to link the a hello world post I would need to find out how many posts with the same name exist already and then append the additional number to it which is inefficient.
So what's the best way to do this in Django?
See the documentation for Django's {{ url }} template tag. It lets you pass it a view name and parameters, and automatically generates the correct URL for you.
You can take care of appending numbers to each post's name in the function that generates its slug - you could have a look at django-autoslug

identical views different URLs

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.

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 }})'