pass a multi-value as parameters in pyramid URL Dispatch (add_route) - python

how to configure and use multidict in pyramid.
config.add_route('show_choosed_categories', '/categories/[list]')
and generate the urls like
${request.route_url('show_choosed_categories', categories=[1, 2] )}
in view i would use
request.GET.getall('categories')
pyramid seems to support it by webob.multidict – multi-value dictionary object https://docs.pylonsproject.org/projects/webob/en/stable/api/multidict.html
but how to use it with URL Dispatch.

The route you have configured would match only on a string literal [list]. Routes cannot match on Python objects, only strings and replacement markers. From Route Pattern Syntax under URL Dispatch:
A pattern segment (an individual item between / characters in the pattern) may either be a literal string (e.g., foo) or it may be a replacement marker (e.g., {foo}), or a certain combination of both.
Nonetheless you can extract a multidict from the request object.
# Conjugation of English verbs is horrible
config.add_route('show_chosen_categories', '/categories/')
Assuming you have a list of checkboxes named the same or a select multiple input in a form, with either input being named category, then your request parameters would be generated to look like this:
category=1&category=2
Then any URL that begins with categories would be matched, and request parameters would be usable in your view, depending on your form action:
# form action="POST"
request.POST.getall('category')
# form action="GET"
request.GET.getall('category')
>>> [1, 2]
See Multidict under Request and Response Objects for further information.

You are probably looking for config.add_route('foo', '/categories/*subpath') and request.route_url('foo', subpath=(1, 2, 3). Support for this is limited but it does work if it fits your use cases. Note that an empty list is valid here so you need to handle that.

Related

Tornado - What is the difference between RequestHandler's get_argument(), get_query_argument() and get_body_argument()?

When to use RequestHandler.get_argument(), RequestHandler.get_query_argument() and RequestHandler.get_body_argument()?
What is the use-case for each of them?
Also what does the request.body and request.argument do in these cases? Which are to be used in which scenarios?
And, is there a request.query or something similar too?
Most HTTP requests store extra parameters (say, form values) in one of two places: the URL (in the form of a ?foo=bar&spam=eggs query string), or in the request body (when using a POST request and either the application/x-www-form-urlencoded or multipart/form-data mime type).
The Request.get_query_argument() looks for URL parameters, the RequestHandler.get_body_argument() lets you retrieve parameters set in the POST body. The RequestHandler.get_argument() method retrieves either a body or a URL parameter (in that order).
You use Request.get_argument() when you explicitly don't care where the parameter comes from and your endpoint supports both GET and POST parameters. Otherwise, use one of the other methods, to keep it explicit where your parameters come from.
The Request.get_*_argument methods use the request.body_arguments and request.query_arguments values (with request.arguments being their aggregate), decoded to Unicode. request.body is the undecoded, unparsed raw request body; and yes, there is an equivalent self.query containing the query string from the URL.

How to deal with params in URL schema in Django application

As we know most URL schemes base their URL syntax on this nine-part general format:
<scheme>://<user>:<password>#<host>:<port>/<path>;<params>?<query>#<frag>
I know I can get the query strings in Django request.GET dict. But how to get parameters? It seems HttpRequest in Django can not deal with params defined in URL.
For example: http://www.example.com/hello;param1=aaaa;param2=bbbb
Is there any way to extract params like this in Django without writing own regex in url pattern.
Any help will be appreciated.
The path segment parameters you're talking about were defined in RFC 2396. This RFC has been obsolete since 2005. The new standard, RFC 3986, does not define a specific way to define path segment parameters. The relevant section states:
Aside from dot-segments in hierarchical paths, a path segment is
considered opaque by the generic syntax. [...] For
example, the semicolon (";") and equals ("=") reserved characters are
often used to delimit parameters and parameter values applicable to
that segment. [...] Parameter types may be defined by scheme-specific
semantics, but in most cases the syntax of a parameter is specific to
the implementation of the URI's dereferencing algorithm.
Django uses the new standard and does not define any specific way to parse path segment parameters. However, Python defines the urlparse() function, which parses the url according to RFC2396. You can use this to extract the path parameters:
from urllib.parse import urlparse # urlparse.urlparse on Python 2
params = urlparse(request.path).params
to capture params you've to define url patterns as per that.
lets assume your url is http://myurl.com/mydashboard/250?sort=-1&q=this so here 250 is param which you want.
To capture the same you've to define url like below
url(r'^mydashboard/(?P<weeknum>\d+)/$',
mydashboard, name='mydashboard'),
and now the view definition of mydashboard will have another input param like below
def nagdashboard(request, weeknum):
And to get GET params or POST params you can use.
request.GET.get('sort')
For further reading on patterns refer to here

Testing request parameters in Django ("+" behaves differently)

I have a Django View that uses a query parameter to do some content filtering. Something like this:
/page/?filter=one+and+two
/page/?filter=one,or,two
I have noticed that Django converts the + to a space (request.GET.get('filter') returns one and two), and I´m OK with that. I just need to adjust the split() function I use in the View accordingly.
But...
When I try to test this View, and I call:
from django.test import Client
client = Client()
client.get('/page/', {'filter': 'one+and+two'})
request.GET.get('filter') returns one+and+two: with plus signs and no spaces. Why is this?
I would like to think that Client().get() mimics the browser behaviour, so what I would like to understand is why calling client.get('/page/', {'filter': 'one+and+two'}) is not like browsing to /page/?filter=one+and+two. For testing purposes it should be the same in my opinion, and in both cases the view should receive a consistent value for filter: be it with + or with spaces.
What I don´t get is why there are two different behaviours.
The plusses in a query string are the normal and correct encoding for spaces. This is a historical artifact; the form value encoding for URLs differs ever so slightly from encoding other elements in the URL.
Django is responsible for decoding the query string back to key-value pairs; that decoding includes decoding the URL percent encoding, where a + is decoded to a space.
When using the test client, you pass in unencoded data, so you'd use:
client.get('/page/', {'filter': 'one and two'})
This is then encoded to a query string for you, and subsequently decoded again when you try and access the parameters.
This is because the test client (actually, RequestFactory) runs django.utils.http.urlencode on your data, resulting in filter=one%2Band%2Btwo. Similarly, if you were to use {'filter': 'one and two'}, it would be converted to filter=one%20and%20two, and would come into your view with spaces.
If you really absolutely must have the pluses in your query string, I believe it may be possible to manually override the query string with something like: client.get('/page/', QUERY_STRING='filter=one+and+two'), but that just seems unnecessary and ugly in my opinion.

Putting together Haystack records without templates

The haystack documentation (link below) makes this statement:
Additionally, we're providing use_template=True on the text field.
This allows us to use a data template (rather than error prone
concatenation) to build the document the search engine will use in
searching.
How would one go about using concatenation to build the document? I couldn't find an example.
It may have something to do with overriding the prepare method (second link). But in the example given in the documentation the prepare method is used together with a template, so the two might also be orthogonal.
https://github.com/toastdriven/django-haystack/blob/master/docs/tutorial.rst
http://django-haystack.readthedocs.org/en/latest/searchindex_api.html#advanced-data-preparation
You can see how it works in the Haystack source. Basically, the default implementation of the prepare method on SearchField (the base class for Haystack's fields) calls prepare_template if use_template is True.
If you don't want to use a template, you can indeed use concatenation - it's as simple as just joining the data you want together, separated by something (here I've used a newline):
def prepare_myfield(self, obj):
return self.cleaned_data['field1'] + '\n' + self.cleaned_data['field2']
etc.

django - regex for optional url parameters

I have a view in django that can accept a number of different filter parameters, but they are all optional. If I have 6 optional filters, do I really have to write urls for every combination of the 6 or is there a way to define what parts of the url are optional?
To give you an example with just 2 filters, I could have all of these url possibilities:
/<city>/<state>/
/<city>/<state>/radius/<miles>/
/<city>/<state>/company/<company-name>/
/<city>/<state>/radius/<miles>/company/<company-name>/
/<city>/<state>/company/<company-name>/radius/<miles>/
All of these url's are pointing to the same view and the only required params are city and state. With 6 filters, this becomes unmanageable.
What's the best way to go about doing what I want to achieve?
One method would be to make the regular expression read all the given filters as a single string, and then split them up into individual values in the view.
I came up with the following URL:
(r'^(?P<city>[^/]+)/(?P<state>[^/]+)(?P<filters>(?:/[^/]+/[^/]+)*)/?$',
'views.my_view'),
Matching the required city and state is easy. The filters part is a bit more complicated. The inner part - (?:/[^/]+/[^/]+)* - matches filters given in the form /name/value. However, the * quantifier (like all Python regular expression quantifiers) only returns the last match found - so if the url was /radius/80/company/mycompany/ only company/mycompany would be stored. Instead, we tell it not to capture the individual values (the ?: at the start), and put it inside a capturing block which will store all filter values as a single string.
The view logic is fairly straightforward. Note that the regular expression will only match pairs of filters - so /company/mycompany/radius/ will not be matched. This means we can safely assume we have pairs of values. The view I tested this with is as follows:
def my_view(request, city, state, filters):
# Split into a list ['name', 'value', 'name', 'value']. Note we remove the
# first character of the string as it will be a slash.
split = filters[1:].split('/')
# Map into a dictionary {'name': 'value', 'name': 'value'}.
filters = dict(zip(split[::2], split[1::2]))
# Get the values you want - the second parameter is the default if none was
# given in the URL. Note all entries in the dictionary are strings at this
# point, so you will have to convert to the appropriate types if desired.
radius = filters.get('radius', None)
company = filters.get('company', None)
# Then use the values as desired in your view.
context = {
'city': city,
'state': state,
'radius': radius,
'company': company,
}
return render_to_response('my_view.html', context)
Two things to note about this. First, it allows unknown filter entries into your view. For example, /fakefilter/somevalue is valid. The view code above ignores these, but you probably want to report an error to the user. If so, alter the code getting the values to
radius = filters.pop('radius', None)
company = filters.pop('company', None)
Any entries remaining in the filters dictionary are unknown values about which you can complain.
Second, if the user repeats a filter, the last value will be used. For example, /radius/80/radius/50 will set the radius to 50. If you want to detect this, you will need to scan the list of values before it is converted to a dictionary:
given = set()
for name in split[::2]:
if name in given:
# Repeated entry, complain to user or something.
else:
given.add(name)
This is absolutely the use-case for GET parameters. Your urlconf should just be /city/state/, then the various filters go on the end as GET variables:
/city/state/?radius=5&company=google
Now, in your view, you accept city and state as normal parameters, but everything else is stored in the request.GET QueryDict.
You could also make just one url (that only checks the start of the path, that should be the same) pointing to your view and then parse request.path in your view.
On the other hand, if you have really many optional filter parameters in various combinations the best solution is very often to do th filtering via GET-parameters, especially if the urls used for filtering don't need to be optimized for any search engine...
Try use something like that in your urls.py:
url(r'^(?P<city>[^/]+)/(?P<state>[^/]+)/(radius/(?P<miles>[^/]+)/|company/(?P<company_name>[^/]+)/)*$', 'view')

Categories

Resources