Django - get names of parameters needed to reverse url - python

Background
Let's say I have a url pattern with parameters that will link me to a view in django:
url(
r'^things/(?P<thing_name>\w+)/features/(?P<feature_name>\w+)$',
views.thingFeature,
name='thing_feature'
),
And lets say I have a thing and a feature:
thing = Thing.objects.get(.....)
feature = thing.feature_set.first()
t_name = thing.name
f_name = feature.name
Now Django gives me the awesome ability to get a url that brings me to a page dedicated to a specific feature of a specific thing. I can do that like so:
from django.core.urlresolvers import reverse
url = reverse('thing_feature', thing_name=t_name, feature_name=f_name)
# url == '/things/thing2/features/left-arm'
Question
Now I've stumbled into a situation that I need to specifically address. I'm not looking for a workaround - I'm looking to solve the following problem:
Given a url's name, how do I get the list of kwarg argument names needed to reverse that url?
I am looking for the function get_kwarg_names_for_url. It behaves like so:
url_kwarg_names = get_kwarg_names_for_url('thing_feature')
# url_kwarg_names == ['thing_name', 'feature_name']
url_kwarg_names is now the list of every keyword I need to supply to Django's reverse function in order to reverse the url named "thing_feature".
Any help is appreciated!
Solution
Based on knbk's answer I was able to come up with the following solution:
def get_kwarg_names_for_url(url_name):
resolver = get_resolver(get_urlconf())
reverse_data = resolver.reverse_dict[url_name]
pattern_list = reverse_data[0]
'''
Need to specify the 1st pattern because url regexes can
potentially have multiple kwarg arrangments - this function does
not take this possibility into account.
'''
first_pattern = pattern_list[0]
'''
`first_pattern` is now of the form `(url_string, kwarg_list)` -
all we are interested in is the 2nd value.
'''
return first_pattern[1]

I'll start with a fair warning: this isn't possible using the public API. On top of that, I'm actively working to rewrite the URL dispatcher for 1.10, so this method will most likely break by that time.
First, you need to get the right RegexURLResolver. If the view is not in a namespace, you can use the reverse_dict to get a list of possibilities, and extract the kwargs:
def get_kwargs(view_name):
resolver = urlresolvers.get_resolver()
patterns = resolver.reverse_dict.getlist(view_name)
kwargs = []
for possibility, pattern, defaults in patterns:
for result, params in possibility:
kwargs.append(params)
return kwargs
Since a view name can have multiple patterns with different kwargs (though you'd want to avoid that for your own sanity), this will return a list of each set of possible kwargs. Usually the different sets would be the required kwargs on one side and required + optional kwargs on the other side.
I haven't tested this, but if it doesn't work you can dig around in resolver.reverse_dict for a bit to find out the exact specifics. It wasn't exactly designed with usability in mind.

You should be able to do this with a resolve()
From the Docs:
A ResolverMatch object can then be interrogated to provide information about the URL pattern that matches a URL:
func, args, kwargs = resolve('/some/path/')
Specific to your example code:
url = reverse('thing_feature')
func, args, kwargs = resolve(url)
# args == ['thing_name', 'feature_name']

Related

Django: Passing parameters in URL as query arguments

How can I pass parameters via URL as query parameters to avoid multiple and complicated url patterns?
For example, instead of making a complicated url like
example.com/page/12/red/dog/japan/spot
or something like that, and then a corresponding entry in urls.py that will parse that url and direct it to a view, I want to simply get a url where I can freely add or remove parameters as needed similar to the ugly way
example.com/page?id=12&color=red&animal=dog&country=Japan&name=spot
Then in urls.py simply have something like
path('page/<parameter_dictionary>', views.page, name='page' parameters='parameter_dictionary)
If I have to use url patterns, how can I account for urls that have parameters that may or may not fit the pattern, such as sometimes
"/page/12/red/dog/Japan/spot" -> path('page/<int:id>/<str:color>/<str:animal>/<str:country>/<str:name>', views.page, name='page'),
"/page/12/dog/red/Japan/"-> path('page/<int:id>/<str:animal>/<str:color>/<str:country>', views.page, name='page')
"/page/dog/red/Japan/"-> path('page/<str:animal>/<str:color>/<str:country>', views.page, name='page')
I would like to just have anything sent to http://example.com/page/
go to views.page(), and then be accessible by something like
animal = request.GET['animal']
color = request.GET['color']
id = request.GET['id']
etc. so examples below would all work via one entry in urls.py
example.com/page?id=12&animal=dog&country=Japan&name=spot
example.com/page?id=12&color=red&animal=dog&name=spot
example.com/page?id=12&country=Japan&color=red&animal=dog&name=spot
You are looking for queryparameters and you are almost done with it. The following code is untested but should kinda work:
def page(request):
animal = request.GET.get("animal",None) # default None if not present
color = request.GET.get("color",None)
return render(request,'some_html.html')
# urls.py:
path('page/', views.page, name='page')
You access the queryparameters via the passed request object request.GET. This is a dict like object. The main difference is that this object handles multi keys.
For example if you pass the these params ?a=1&a=2 to your url, it converts request.GET.getlist("a") # Returns ["1","2"] to a list.
request.GET.get("a") returns the last passed value "2" as #Kbeen mentioned in comments,. Read more about QueryDict here.
Also be sure to know the difference and best practice for url parameters and queryparameters. Example Stackoverflow post
Edit: Added request.GET.getlist()

Get all parameters and their values in a request in Django [duplicate]

I am currently defining regular expressions in order to capture parameters in a URL, as described in the tutorial. How do I access parameters from the URL as part the HttpRequest object?
My HttpRequest.GET currently returns an empty QueryDict object.
I'd like to learn how to do this without a library, so I can get to know Django better.
When a URL is like domain/search/?q=haha, you would use request.GET.get('q', '').
q is the parameter you want, and '' is the default value if q isn't found.
However, if you are instead just configuring your URLconf**, then your captures from the regex are passed to the function as arguments (or named arguments).
Such as:
(r'^user/(?P<username>\w{0,50})/$', views.profile_page,),
Then in your views.py you would have
def profile_page(request, username):
# Rest of the method
To clarify camflan's explanation, let's suppose you have
the rule url(regex=r'^user/(?P<username>\w{1,50})/$', view='views.profile_page')
an incoming request for http://domain/user/thaiyoshi/?message=Hi
The URL dispatcher rule will catch parts of the URL path (here "user/thaiyoshi/") and pass them to the view function along with the request object.
The query string (here message=Hi) is parsed and parameters are stored as a QueryDict in request.GET. No further matching or processing for HTTP GET parameters is done.
This view function would use both parts extracted from the URL path and a query parameter:
def profile_page(request, username=None):
user = User.objects.get(username=username)
message = request.GET.get('message')
As a side note, you'll find the request method (in this case "GET", and for submitted forms usually "POST") in request.method. In some cases, it's useful to check that it matches what you're expecting.
Update: When deciding whether to use the URL path or the query parameters for passing information, the following may help:
use the URL path for uniquely identifying resources, e.g. /blog/post/15/ (not /blog/posts/?id=15)
use query parameters for changing the way the resource is displayed, e.g. /blog/post/15/?show_comments=1 or /blog/posts/2008/?sort_by=date&direction=desc
to make human-friendly URLs, avoid using ID numbers and use e.g. dates, categories, and/or slugs: /blog/post/2008/09/30/django-urls/
Using GET
request.GET["id"]
Using POST
request.POST["id"]
Someone would wonder how to set path in file urls.py, such as
domain/search/?q=CA
so that we could invoke query.
The fact is that it is not necessary to set such a route in file urls.py. You need to set just the route in urls.py:
urlpatterns = [
path('domain/search/', views.CityListView.as_view()),
]
And when you input http://servername:port/domain/search/?q=CA. The query part '?q=CA' will be automatically reserved in the hash table which you can reference though
request.GET.get('q', None).
Here is an example (file views.py)
class CityListView(generics.ListAPIView):
serializer_class = CityNameSerializer
def get_queryset(self):
if self.request.method == 'GET':
queryset = City.objects.all()
state_name = self.request.GET.get('q', None)
if state_name is not None:
queryset = queryset.filter(state__name=state_name)
return queryset
In addition, when you write query string in the URL:
http://servername:port/domain/search/?q=CA
Do not wrap query string in quotes. For example,
http://servername:port/domain/search/?q="CA"
def some_view(request, *args, **kwargs):
if kwargs.get('q', None):
# Do something here ..
For situations where you only have the request object you can use request.parser_context['kwargs']['your_param']
You have two common ways to do that in case your URL looks like that:
https://domain/method/?a=x&b=y
Version 1:
If a specific key is mandatory you can use:
key_a = request.GET['a']
This will return a value of a if the key exists and an exception if not.
Version 2:
If your keys are optional:
request.GET.get('a')
You can try that without any argument and this will not crash.
So you can wrap it with try: except: and return HttpResponseBadRequest() in example.
This is a simple way to make your code less complex, without using special exceptions handling.
I would like to share a tip that may save you some time.
If you plan to use something like this in your urls.py file:
url(r'^(?P<username>\w+)/$', views.profile_page,),
Which basically means www.example.com/<username>. Be sure to place it at the end of your URL entries, because otherwise, it is prone to cause conflicts with the URL entries that follow below, i.e. accessing one of them will give you the nice error: User matching query does not exist.
I've just experienced it myself; hope it helps!
These queries are currently done in two ways. If you want to access the query parameters (GET) you can query the following:
http://myserver:port/resource/?status=1
request.query_params.get('status', None) => 1
If you want to access the parameters passed by POST, you need to access this way:
request.data.get('role', None)
Accessing the dictionary (QueryDict) with 'get()', you can set a default value. In the cases above, if 'status' or 'role' are not informed, the values ​​are None.
If you don't know the name of params and want to work with them all, you can use request.GET.keys() or dict(request.GET) functions
This is not exactly what you asked for, but this snippet is helpful for managing query_strings in templates.
If you only have access to the view object, then you can get the parameters defined in the URL path this way:
view.kwargs.get('url_param')
If you only have access to the request object, use the following:
request.resolver_match.kwargs.get('url_param')
Tested on Django 3.
views.py
from rest_framework.response import Response
def update_product(request, pk):
return Response({"pk":pk})
pk means primary_key.
urls.py
from products.views import update_product
from django.urls import path
urlpatterns = [
...,
path('update/products/<int:pk>', update_product)
]
You might as well check request.META dictionary to access many useful things like
PATH_INFO, QUERY_STRING
# for example
request.META['QUERY_STRING']
# or to avoid any exceptions provide a fallback
request.META.get('QUERY_STRING', False)
you said that it returns empty query dict
I think you need to tune your url to accept required or optional args or kwargs
Django got you all the power you need with regrex like:
url(r'^project_config/(?P<product>\w+)/$', views.foo),
more about this at django-optional-url-parameters
This is another alternate solution that can be implemented:
In the URL configuration:
urlpatterns = [path('runreport/<str:queryparams>', views.get)]
In the views:
list2 = queryparams.split("&")
url parameters may be captured by request.query_params
It seems more recommended to use request.query_params. For example,
When a URL is like domain/search/?q=haha, you would use request.query_params.get('q', None)
https://www.django-rest-framework.org/api-guide/requests/
"request.query_params is a more correctly named synonym for request.GET.
For clarity inside your code, we recommend using request.query_params instead of the Django's standard request.GET. Doing so will help keep your codebase more correct and obvious - any HTTP method type may include query parameters, not just GET requests."

Using "google-like" colon-search operator with Haystack

I'm developing a Django application with Haystack search engine capability, and am wanting to implement "google-like" colon search operators.
When doing a google search you can use queries like filetype:pdf or site:www.stackoverflow.com to restrict the specific search results, and would like to implement a similar style.
I know Solr, one of the search engines underneath Haystack can do this on specific fields but was curious if there was a generic approach in Haystack.
The current solution I am looking at building would be to take the search input from a form, use a regex search to find terms matching \b[a-z]+:[\w\d]+\b and then checking if they are appropriate fields to search on and using SearchQuerySet.filter to restrict results.
However, I am hoping there is an already in-built way to specify a afield in a SearchIndex can be used in this fashion automatically. Is this possible?
you can take advantage of the the ability to expand dictionaries into keyword arguments
after applying your regex to filter you search field and search value create a dictionary like
my_search = {'search_field':'search_value'}
search_res = SearchQuerySet().filter(**my_search)
just tested it out in my implementation and worked great! Unless you are worried about users searching fields that they should not be searching/viewing there is no need to verify that it is a valid search field, an invalid search filed will just return no results
So this is actually quite easy, as Haystack provides a straight forward way to get all of the fields for the index using:
connections[DEFAULT_ALIAS].get_unified_index()
All that needs to be done is tokenize the search string, remove any that have a token that is a valid field, and run the search on the terms that are left before using the fields in a call to filter.
I've released it as a gist as well, but will probably end up packaging it at a later stage
from haystack.constants import DEFAULT_ALIAS
from haystack import connections
class TokenSearchForm(SearchForm):
def prepare_tokens(self):
try:
query = self.cleaned_data.get('q')
except:
return {}
opts = connections[DEFAULT_ALIAS].get_unified_index().fields.keys()
kwargs = {}
query_text = []
for word in query.split(" "):
if ":" in word:
opt,arg = word.split(":",1)
if opt in opts:
kwargs[str(opt)]=arg
else:
query_text.append(word)
self.query_text = " ".join(query_text)
self.kwargs = kwargs
return kwargs
def search(self):
self.query_text = None
kwargs = self.prepare_tokens()
if not self.is_valid():
return self.no_query_found()
if not self.cleaned_data.get('q'):
return self.no_query_found()
sqs = self.searchqueryset.auto_query(self.query_text)
if kwargs:
sqs = sqs.filter(**kwargs)
if self.load_all:
sqs = sqs.load_all()
return sqs

Is it a way to check whether all the subpaths of the current request.path have a view callable associated with?

I am working on a breadcrumbs generator.
It uses request.path and then, for each subpath, builds a breadcrumb.
Example:
/blog/articles/view/12345
Then for each of the subpaths:
/blog/articles/view
/blog/articles
/blog
True would be returned, if there's a view callable behind this URL ( allowing GET method without arguments ), otherwise False
So that I could make the subpaths in the breadcrumbs clickable to show that there's something served there.
Any idea which would not call any of the subpaths and generate useless code execution?
No, you have to test all path prefixes; routing allows for many, arbitrary URLs to be possible. Moreover, with path predicates in the mix, multiple routes could match the same URL and choosing between them depends on other information from the request.
To prepare your breadcrumbs, instead loop over the sub-paths once and determine for each if there is a matching view; the easiest way to do this is to reuse the code underlying the pviews command; this code needs the current request:
from pyramid.scripts.pviews import PViewsCommand
pvcomm = PViewsCommand()
urlpath = request.environ['PATH_INFO']
parts = urlpath.split('/')
existing_views = {}
for i in range(1, len(parts)):
path = '/'.join(parts[:i])
view = pvcomm._find_view(path, request.registry)
if view is not None:
existing_views[path] = view
You can now look up path prefixes in the existing_views dictionary.

Default parameters to actions with Django

Is there a way to have a default parameter passed to a action in the case where the regex didnt match anything using django?
urlpatterns = patterns('',(r'^test/(?P<name>.*)?$','myview.displayName'))
#myview.py
def displayName(request,name):
# write name to response or something
I have tried setting the third parameter in the urlpatterns to a dictionary containing ' and giving the name parameter a default value on the method, none of which worked. the name parameter always seems to be None. I really dont want to code a check for None if i could set a default value.
Clarification: here is an example of what i was changing it to.
def displayName(request,name='Steve'):
return HttpResponse(name)
#i also tried
urlpatterns = patterns('',
(r'^test/(?P<name>.*)?$',
'myview.displayName',
dict(name='Test')
)
)
when i point my browser at the view it displays the text
'None'
Any ideas?
The problem is that when the pattern is matched against 'test/' the groupdict captured by the regex contains the mapping 'name' => None:
>>> url.match("test/").groupdict()
{'name': None}
This means that when the view is invoked, using something I expect that is similar to below:
view(request, *groups, **groupdict)
which is equivalent to:
view(request, name = None)
for 'test/', meaning that name is assigned None rather than not assigned.
This leaves you with two options. You can:
Explicitly check for None in the view code which is kind of hackish.
Rewrite the url dispatch rule to make the name capture non-optional and introduce a second rule to capture when no name is provided.
For example:
urlpatterns = patterns('',
(r'^test/(?P<name>.+)$','myview.displayName'), # note the '+' instead of the '*'
(r'^test/$','myview.displayName'),
)
When taking the second approach, you can simply call the method without the capture pattern, and let python handle the default parameter or you can call a different view which delegates.
I thought you could def displayName(request, name=defaultObj); that's what I've done in the past, at least. What were you setting the default value to?

Categories