Is it possible to define Django urlpattern, which would get any number of same type parameters and pass them to a view?
Lets say I want to create a page which gets a list of numbers from url and sums them. So these would be valid urls:
/sum/10/99/12/
/sum/1/2/3/4/5/6/7/8/9/
/sum/3/
I guess that view could look like this:
def sum_view(request, *args):
return render(request, 'sum.html', {'result': sum(args)})
Question is how should urlpattern look like? Maybe something like this:
url(r'^sum/((\d+)/)+$', views.sum_view)
But this way view gets only last number repeated twice: args == ('1/', '1'). What is the correct regular expression to get all the numbers passed ot the view?
You could capture a single argument, e.g. '1/2/3/4/5/6/7/8/9/'
url(r'^sum/(?P<numbers>[\d/]+)$', views.sum_view)
The split the list in the view.
def sum_view(request, numbers):
processed_numbers = [int(x) for x in number.split('/') if x]
...
Django's urlpatterns are based on python's regexps so there is no way of catching all data from repeating group. Check this question for explanation.
You will have to split and process them by yourself (like #Alasdair suggests).
Related
I'm trying to append a parameter no_rep at the end of my question url as a signal to show different views. I'm using Django 1.8 and following the url pattern from askbot.
This is the url.py:
url(
(r'^%s(?P<id>\d+)/' % QUESTION_PAGE_BASE_URL.strip('/') +
r'(%s)?' % r'/no-rep:(?P<no_rep>\w+)'),
views.readers.question,
name='question'
),
I'm trying to show different displays depending on the value of no_rep in my url.
This is the view:
def question(request, id, no_rep):
if no_rep == '1':
request.session['no_rep'] = True
else:
request.session['no_rep'] = False
I couldn't find information on what the +,%,? do, which is probably where the problem is. Could someone explain how the regex work with the base url? When I enter the url http://localhost:8000/question6/test-question/no_rep:1, request.session['no_rep'] should be set to true, but it's not. What am I missing?
Your url looks like this after all substitutions (%): r'^question(?P<id>\d+)/(/no-rep:(?P<no_rep>\w+))?', so:
1. Missing part for test-question
2. You have odd grouping around named group no_rep (which is legal, but not recommended)
So for url like http://localhost:8000/question6/test-question/no_rep:1 your url pattern should look like this:
r'^%s(?P<id>\d+)/test-question/(?:no_rep:(?P<no_rep>\d+))?' % QUESTION_PAGE_BASE_URL.strip('/') (you can change \d+ in the last group for \w+ if you want letters to match too).
About non-capturing groups (?:...) and meaning of + and ? you can read in the documentation of Python re module.
I'm writing tests for my Django app using the built-in testing tools. Right now I'm trying to write a test for a page that displays a list of a user's followers. When a user has no followers the page displays a message randomly picked from a list of strings. As an example:
NO_FOLLOWERS_MESSAGES = [
"You don't have any followers.",
"Sargent Dan, you ain't got no followers!"
]
So now I want to write a test that asserts that the response contains one of those strings. If I was only using one string, I could just use self.assertContains(request, "You don't have any followers.") but I'm stuck on how to write the test with multiple possible outcomes. Any help would be appreciated.
Try this:
if not any([x in response.content for x in NO_FOLLOWERS_MESSAGES]):
raise AssertionError("Did not match any of the messages in the request")
About any(): https://docs.python.org/2/library/functions.html#any
Would something like this work?
found_quip = [quip in response.content for quip in NO_FOLLOWERS_MESSAGES]
self.assertTrue(any(found_quip))
Internally assertContains(), uses the count from _assert_contains()
So if you want to preserve exactly the same behavior as assertContains(), and given that the implementation of _assert_contains() isn't a trivial one, you can get inspiration from the source code above and adapt one for your needs
Our assertContainsAny() inspired by assertContains()
def assertContainsAny(self, response, texts, status_code=200,
msg_prefix='', html=False):
total_count = 0
for text in texts:
text_repr, real_count, msg_prefix = self._assert_contains(response, text, status_code, msg_prefix, html)
total_count += real_count
self.assertTrue(total_count != 0, "None of the text options were found in the response")
Use by passing the argument texts as a list, e.g.
self.assertContainsAny(response, NO_FOLLOWERS_MESSAGES)
In django doc proposed instead GET method use urlpatterns, and made ​​convenient way to handle these variables. But if at least one of the variables is not necessary I'll have to write more lines in url.py. I like that I can avoid this?
Example:
If I want to take a sample of posts in a given year, in urlpatterns I should add something like this:
url(r'^articles/(?P<year>\d{4})/$', 'news.views.show_archive'),
url: .../articles/1994/
If I want to make the sample positions for a particular month a specific year, in urlpatterns I should add something like this:
url(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', 'news.views.show_archive'),
url: .../articles/2003/03/
But if I want to see the records of all the years created particular month of year I have to add also this line:
url(r'^articles/(?P<month>\d{2})/$', 'news.views.show_archive'),
url: .../articles/03/
But I would like to do only one line that specifies the maximum set of variables, but that would process any of these URL.
To be honest I'm not sure that this is possible.
regexps can have optional parts, and view functions can have optional arguments. Also, you can still use querystrings (through request.GET) for what has no business being part of the URL (like query terms for a "search" view, ordering and filtering for a listing view, etc).
The point of using urlpatterns instead of querystrings is to build clean "semantic" urls, ie /blog/posts/<post_id>/ instead of /blog/posts/?post_id=<post_id>.
you could try like this
url(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', 'news.views.show_archive'),
def show_archive(request,year=None,month=None):
if year and month:
.....................
elif year:
.....................
elif month:
....................
I was going through the django tutorial and though now almost everything there seems pretty clear, I am having trouble understanding the regex while matching the urls :
r'^(?P<poll_id>\d+)/$
what does (?P<poll_id>\d+) do ?
I am understanding that after stripping off the "34/" from "polls/34/", The polls.url is being called and there the keyword urlpatterns is being looked for , but how does poll_id get this value 34 ?
I know only a bit of regex, so thats why it might be hard for me to read.
Also, here is the reference that I am using for this question :Tutorial Part3
It's a regex that takes the poll_id (a number) as a variable.
The corresponding view is:
def detail(request, poll_id):
return HttpResponse("You're looking at poll %s." % poll_id)
Now when you go to example.com/polls/34/, it knows you are looking for poll number 34, and it brings that in to the view as the poll_id.
So in your view, poll_id = 34. This allows you to display or manipulate this specific poll.
Essentially the point of the regex in this case is to allow you to view a large number of specific polls without having to create an explicit url for each one.
To clarify, this regex is saying take any number \d+, save it as poll_id, and proceed to this view with that poll_id.
To support for Dan Hoerst 's answer: yup, django uses regular expression to extract the poll_id for the view. The underneath of this is:
import re
r = re.compile(r'^poll/(?P<poll_id>\d+)/$')
r.match('poll/132/').groups() # ('132',)
Currently I have three URL paths that map to ServiceHandler. How do I combine the three into one neat regex that can pass n number of arguments to ServiceHandler?
(r'/s/([^/]*)', ServiceHandler),
(r'/s/([^/]*)/([^/]*)', ServiceHandler),
(r'/s/([^/]*)/([^/]*)/([^/]*)', ServiceHandler)
(r'^/s/(([^/]*)((/[^/]+)*))$', ServiceHandler)
Should do the trick to match any amount of
/s/foo/bar/baz/to/infinity/and/beyond/
You can also limit it to a range by doing something like
^/s/(([^/]*)((/[^/]+){0,2}))$
Which would only match things like
/s/foo/bar/baz
/s/foo/bar
/s/foo
but not
/s/foo/bar/baz/pirate
/s
You can try something like
(r'/s/([^/]*)/?([^/]*)/?([^/]*)', ServiceHandler)
I think you will always get 3 parameters to ServiceHandler but the ones that aren't used will be empty strings
This should work for any number
(r'(?<!^)/([^/]+)', ServiceHandler)
Since I've looked in urlresolvers.py, I see this won't work although you could patch the correct behaviour into urlresolvers.py using regex.findall instead of re.search.