Throwing back a URL in Django - python

This is a relatively straightforward question.
Is if possible for a method in views.py to dynamically throw back a URL that it caught in err and let a later handler process it. For example:
urls.py
urlpatterns = patterns('myapp.views',
url(r'^foo/(?P<fiz>\d+)/?$', too_broad_method,name="foo"),
url(r'^foo/bar/?$', just_right_method,name="foo"),
)
views.py
def too_broad_method(request,fiz=None):
if fiz == some_dynamic_value:
# under some runtime conditions fiz can equal bar
# Throw some exception to give the URL back??
else:
return process_it()
Lets say for example, /foo/bar should be caught and processed by too_broad_method if an item has the name bar but otherwise it should be processed by just_right_method.
For extra context, I am trying to catch urls of the form app_label/model_name, which doesn't follow any pattern. I'd like these to be caught first, before anything else, which means using a very broad regex.

(Edited since the entire premise of the question changed)
If you need to catch app_name/model_name URLs, my suggestion is that you generate your URL patterns dynamically. There's no reason you couldn't iterate through INSTALLED_APPS, get all available classes that inherit from models.Model, and create URL patterns in a list accordingly. Then you can feed that into the patterns function at the end.
Trying to inform the URL dispatcher that it was somehow "wrong" is misguided, as I've already explained, and you're solving the wrong problem. Instead, you should focus on configuring the URL patterns how you actually need them.

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

Django - Testing a view name retrieved from a url parameter ie. ref=home

Simply put, I am trying to test a view name, captured from a URL parameter, within one of my view functions to see if it is valid. If not, to redirect to another page.
For example, take this url...
www.site.com/users/?ref=view_name
Then in my view
ref = request.GET.get('ref', None)
return redirect(ref) if ref else redirect('users')
The problem is, of course, that if a user alters the ref parameter, it will kick back a 404. I was hoping to be able to test if ref=home is a valid view and return it, if not then redirect to another view.
I have been messing with Django's resolve and reverse but I am not getting the results I was looking for.
I also tried to use a try/finally in all sorts of ways mixed in with resolve and reverse. Yeah, dumb...I know.
try:
if ref:
return redirect(resolve(ref))
finally:
return redirect('user')
I have searched for almost two hours to try to fund a succinct way to do this.
Any help would be appreciated!
The problem is that you are passing a view name to resolve() which requires a URL path. To protect against 404, you need to first reverse() the view name to get a path. Then you can use that path with resolve() to check if the path exists.
Working Solution
This was actually very simple after guidance from #Code-Apprentice
A couple Imports first off
from django.urls.resolvers import NoReverseMatch
from django.urls import reverse
Then to get the ref parameter and validate is as a legit view name I did the following
# get ref parameter
ref = request.GET.get('ref', None)
if ref:
try:
# redirect to ref view name if valid
return redirect(reverse(ref))
except NoReverseMatch as e:
print('No reverse match found')
# redirect to specific view if ref is invalid
return redirect('users')

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

How to map separate sets of URL prefixes to Flask blueprints

I have a Flask app that handles a range of URLs. I'm splitting it up into multiple handler modules, where the handler depends on the first element of the path. The mapping between path/URL prefixes and handlers is a bit like this:
/one/... => Handler A
/two/... => Handler A
/three/... => Handler B
/four/... => Handler B
Calling a given URL under /one/... gets you something very similar (though not identical) to the same URL under /two/ - hence the desire to use the same handler for both those sets of URLs. At the same time Handler A does something very different to Handler B, therefore the desire to implement a clear separation, with separate modules for each.
Blueprints seem to be a great way to do this - and for the most part appear to work well. Where I'm struggling is in setting up differentiated behavior for /one/ vs /two/ (and /three/ vs /four/). In other words, exposing the actual URL prefix to the handler.
As an example handler A looks like
# handler_A.py
from flask import Blueprint
handler_A = Blueprint('handler_A', __name__)
#handler_A('/somepage', methods=['GET'])
def get_page():
return "You've reached somepage provided to you by handler A"
And handler B has a similar structure (but does something very different).
Then the app looks like
# app.py
from flask import Flask
from handler_A import handler_A
from handler_B import handler_B
app = Flask(__name__)
app.register_blueprint(handler_A, url_prefix='/one')
app.register_blueprint(handler_A, url_prefix='/two')
app.register_blueprint(handler_B, url_prefix='/three')
app.register_blueprint(handler_B, url_prefix='/four')
The part that I can't seem to do "nicely" is figuring out whether an endpoint within Handler A (for example) was called from a URL prefixed with /one/ or /two/. This is an important distinction for me, though as soon as the handler is called that information is obscured. I've looked through the docs but can't find a clean way to do this.
The following are the options I've thought of/attempted so far:
Grabbing request.path from within the handler and pulling out the prefix from the string. This is simple and it works, but seems awkward
Setting up a separate blueprint for each top-level path, and "merging" the execution flow from four blueprints into the two handlers I have
Making the top-level of the URL be a parameter that's recorded in the context (like in https://flask.palletsprojects.com/en/1.1.x/patterns/urlprocessors/#internationalized-blueprint-urls). However I think this also requires me to write a custom URL processor, if I want "one" and "two" to match one url_prefix, while "two" and "three" match another.
Is there smart way to do this that I'm missing?
To me the request.path is a good solution. It's awkward in that it's an implicit parameter of the function; the only difference between this and the solution you're looking for is this explicit vs. implicit parameters. I think this method albeit imperfect is more readable than the complexities required to make this explicit.
If you want, you could extract the core function and pass it the results of the request.path s.t. it's more explicit.
#handler_A('/somepage', methods=['GET'])
def getPage():
return pageForPath(request.path)
def pageForPath(path):
return '<html> .... path ... </html>

Routes with trailing slashes in Pyramid

Let's say I have a route '/foo/bar/baz'.
I would also like to have another view corresponding to '/foo' or '/foo/'.
But I don't want to systematically append trailing slashes for other routes, only for /foo and a few others (/buz but not /biz)
From what I saw I cannot simply define two routes with the same route_name.
I currently do this:
config.add_route('foo', '/foo')
config.add_route('foo_slash', '/foo/')
config.add_view(lambda _,__: HTTPFound('/foo'), route_name='foo_slash')
Is there something more elegant in Pyramid to do this ?
Pyramid has a way for HTTPNotFound views to automatically append a slash and test the routes again for a match (the way Django's APPEND_SLASH=True works). Take a look at:
http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/urldispatch.html#redirecting-to-slash-appended-routes
As per this example, you can use config.add_notfound_view(notfound, append_slash=True), where notfound is a function that defines your HTTPNotFound view. If a view is not found (because it didn't match due to a missing slash), the HTTPNotFound view will append a slash and try again. The example shown in the link above is pretty informative, but let me know if you have any additional questions.
Also, heed the warning that this should not be used with POST requests.
There are also many ways to skin a cat in Pyramid, so you can play around and achieve this in different ways too, but you have the concept now.
Found this solution when I was looking for the same thing for my project
def add_auto_route(config,name, pattern, **kw):
config.add_route(name, pattern, **kw)
if not pattern.endswith('/'):
config.add_route(name + '_auto', pattern + '/')
def redirector(request):
return HTTPMovedPermanently(request.route_url(name))
config.add_view(redirector, route_name=name + '_auto')
And then during route configuration,
add_auto_route(config,'events','/events')
Rather than doing config.add_route('events','/events')
Basically it is a hybrid of your methods. A new route with name ending in _auto is defined and its view redirects to the original route.
EDIT
The solution does not take into account dynamic URL components and GET parameters. For a URL like /abc/{def}?m=aasa, using add_auto_route() will throw a key error because the redirector function does not take into account request.matchdict. The below code does that. To access GET parameters it also uses _query=request.GET
def add_auto_route(config,name, pattern, **kw):
config.add_route(name, pattern, **kw)
if not pattern.endswith('/'):
config.add_route(name + '_auto', pattern + '/')
def redirector(request):
return HTTPMovedPermanently(request.route_url(name,_query=request.GET,**request.matchdict))
config.add_view(redirector, route_name=name + '_auto')
I found another solution. It looks like we can chain two #view_config. So this solution is possible:
#view_config(route_name='foo_slash', renderer='myproject:templates/foo.mako')
#view_config(route_name='foo', renderer='myproject:templates/foo.mako')
def foo(request):
#do something
Its behavior is also different from the question. The solution from the question performs a redirect, so the url changes in the browser. In the second form both /foo and /foo/ can appear in the browser, depending on what the user entered. I don't really mind, but repeating the renderer path is also awkward.

Categories