Django pass known exact string as a url parameter - django

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

Related

Creating a big url

There are references:
sort1, sort2
filter1, filter2
We click on a sort1 and have a new url like: www.example.com?sort=1
Then we click on filter1 and we must have: www.example.com?sort=1&filter=1
Then we click on filter2 and we must have: www.example.com?sort=1&filter=2
and etc...
how can i do this, add more parametrs? my parameters change each other and always only one.
Query parameters like your sort and filter will be passed to your / route through GET variable. So your URLconf looks like:
urls.py
...
url(r'^/$',
sorter
),
Note that you don't put your GET parameters in your URLconf. Instead, they are parsed in key-value fashion and put in an HTTPRequst object, which gets passed to your view. Your view looks like:
views.py
def sorter(request):
...
and in this view, you can access your GET parameters through request.GET. For example, you might use request.GET['sort'] to get the value of the sort parameter. Because parameters are key/value pairs, you can have essentially as many as you want in any order, and it's up to the logic of the view to put them into use. You might want to start at Part 4 of the tutorial for an example of request processing, noting that URL query parameters are passed in request.GET instead of request.POST.
try some of the examples in the docs, replacing POST with GET in templates and views and see what happens

What is reverse()?

When I read Django code sometimes, I see in some templates reverse(). I am not quite sure what this is but it is used together with HttpResponseRedirect. How and when is this reverse() supposed to be used?
reverse() | Django documentation
Let's suppose that in your urls.py you have defined this:
url(r'^foo$', some_view, name='url_name'),
In a template you can then refer to this url as:
<!-- django <= 1.4 -->
link which calls some_view
<!-- django >= 1.5 or with {% load url from future %} in your template -->
link which calls some_view
This will be rendered as:
link which calls some_view
Now say you want to do something similar in your views.py - e.g. you are handling some other URL (not /foo/) in some other view (not some_view) and you want to redirect the user to /foo/ (often the case on successful form submission).
You could just do:
return HttpResponseRedirect('/foo/')
But what if you want to change the URL in the future? You'd have to update your urls.py and all references to it in your code. This violates the DRY (Don't Repeat Yourself) principle and the whole idea of editing in one place only - which is something to strive for.
Instead, you can say:
from django.http import HttpResponseRedirect
from django.urls import reverse
return HttpResponseRedirect(reverse('url_name'))
This looks through all URLs defined in your project for the URL defined with the name url_name and returns the actual URL /foo/.
This means that you refer to the URL only by its name attribute - if you want to change the URL itself or the view it refers to you can do this by editing one place only - urls.py.
The existing answers are quite clear. Just in case you do not know why it is called reverse: It takes an input of a url name and gives the actual url, which is reverse to having a url first and then give it a name.
Existing answers did a great job at explaining the what of this reverse() function in Django.
However, I'd hoped that my answer shed a different light at the why: why use reverse() in place of other more straightforward, arguably more pythonic approaches in template-view binding, and what are some legitimate reasons for the popularity of this "redirect via reverse() pattern" in Django routing logic.
One key benefit is the reverse construction of a url, as others have mentioned. Just like how you would use {% url "profile" profile.id %} to generate the url from your app's url configuration file: e.g. path('<int:profile.id>/profile', views.profile, name="profile").
But as the OP have noted, the use of reverse() is also commonly combined with the use of HttpResponseRedirect. But why?
I am not quite sure what this is but it is used together with HttpResponseRedirect. How and when is this reverse() supposed to be used?
Consider the following views.py:
from django.http import HttpResponseRedirect
from django.urls import reverse
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected = question.choice_set.get(pk=request.POST['choice'])
except KeyError:
# handle exception
pass
else:
selected.votes += 1
selected.save()
return HttpResponseRedirect(reverse('polls:polls-results',
args=(question.id)
))
And our minimal urls.py:
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('<int:question_id>/results/', views.results, name='polls-results'),
path('<int:question_id>/vote/', views.vote, name='polls-vote')
]
In the vote() function, the code in our else block uses reverse along with HttpResponseRedirect in the following pattern:
HttpResponseRedirect(reverse('polls:polls-results',
args=(question.id)
This first and foremost, means we don't have to hardcode the URL (consistent with the DRY principle) but more crucially, reverse() provides an elegant way to construct URL strings by handling values unpacked from the arguments (args=(question.id) is handled by URLConfig). Supposed question has an attribute id which contains the value 5, the URL constructed from the reverse() would then be:
'/polls/5/results/'
In normal template-view binding code, we use HttpResponse() or render() as they typically involve less abstraction: one view function returning one template:
def index(request):
return render(request, 'polls/index.html')
But in many legitimate cases of redirection, we typically care about constructing the URL from a list of parameters. These include cases such as:
HTML form submission through POST request
User login post-validation
Reset password through JSON web tokens
Most of these involve some form of redirection, and a URL constructed through a set of parameters. Hope this adds to the already helpful thread of answers!
This is an old question, but here is something that might help someone.
From the official docs:
Django provides tools for performing URL reversing that match the
different layers where URLs are needed: In templates: Using the url
template tag. In Python code: Using the reverse() function. In higher
level code related to handling of URLs of Django model instances: The
get_absolute_url() method.
Eg. in templates (url tag)
2012 Archive
Eg. in python code (using the reverse function)
return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))
The function supports the dry principle - ensuring that you don't hard code urls throughout your app. A url should be defined in one place, and only one place - your url conf. After that you're really just referencing that info.
Use reverse() to give you the url of a page, given either the path to the view, or the page_name parameter from your url conf. You would use it in cases where it doesn't make sense to do it in the template with {% url 'my-page' %}.
There are lots of possible places you might use this functionality. One place I've found I use it is when redirecting users in a view (often after the successful processing of a form)-
return HttpResponseRedirect(reverse('thanks-we-got-your-form-page'))
You might also use it when writing template tags.
Another time I used reverse() was with model inheritance. I had a ListView on a parent model, but wanted to get from any one of those parent objects to the DetailView of it's associated child object. I attached a get__child_url() function to the parent which identified the existence of a child and returned the url of it's DetailView using reverse().
There is a doc for that
https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-resolution-of-urls
it can be used to generate an URL for a given view
main advantage is that you do not hard code routes in your code.
The reverse() is used to adhere the django DRY principle i.e if you change the url in future then you can reference that url using reverse(urlname).

How to organize URLs in django for views handling GET data and parsing URL?

I have a view that displays some movie data. I thought that it might be a good idea to have a view handle a an URL like movie-id/1234 to search for movie id 1234. Furthermore I would like to be able to enter the ID into a form and send that to a server and search for it. To do that I created a second entry in the urls.py file shown below.
urlpatterns = patterns('',
url(r'movie-id/(?P<movie_id>.+?)/$', 'movieMan.views.detailMovie'),
url(r'movie-id/$', 'movieMan.views.detailMovie', name='movieMan.detailMovie.post'),
)
So if I want to pass data to my view either via a URL or a GET or POST request I have to enter two urls or is there a more elegant way? In the view's code I am then checking if there is any GET data in the incoming request.
To make the second url usable with the template engine, where I wanted to specify the view's url using the {% url movieMan.detailMovie.post %} syntax I had to introduce a name attribute on this url to distinguish between these two.
I am not sure if I am thinking too complicated here. I am now asking myself what is the first url entry good for? Is there a way to get the URL of a movie directly? When do these kinds of URLs come into play and how would they be generated in the template ?
Furthermore I would like to be able to enter the ID into a form and
send that to a server and search for it.
Is this actually a search? Because if you know the ID, and the ID is a part of the URL, you could just have a textbox where the user can write in the ID, and you do a redirect with javascript to the 'correct' URL. If the ID doesn't exist, the view should return a Http404.
If you mean an actual search, i.e. the user submitting a query string, you'll need some kind of list/result view, in which case you'll be generating all the links to the specific results, which you will be sure are correct.
I don't think there is a more elegant way.
I did almost the same thing:
url( r'^movies/search/((?P<query_string>[^/]+)/)?$', 'mediadb.views.search_movies' ),
The url pattern matches urls with or without a search parameter.
In the view-function, you will have to check whether the parameter was defined in the url or in the query string.

Use url args in views, is it possible?

I'd like to use url arguments in views (not templates, I know how to do that).
So is it possible to use them like:
def item_link(self, item):
return mainpage_url_name + "%s/%i" % (item.slug, item.cid)
mainpage_url_name - is of course defined in url patterns (as name variable)
I'm a total newb in Django...
Thanks
First you should use names for your url patterns as documented here.
Then you can use reverse() to use these names in your views or methods.
Following your comments you are using the syndication framework.
Therefore you should make sure that you define get_absolute_url() for you models, ideally using the permalink decorator (for a clean reversing of your urls).
Looking at the example from Django's docs that should be all that's necessary.
To specify the contents of <link>, you
have two options. For each item in
items(), Django first tries calling
the item_link() method on the Feed
class. In a similar way to the title
and description, it is passed it a
single parameter, item. If that method
doesn't exist, Django tries executing
a get_absolute_url() method on that
object.

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