I need to have a dynamic URL prefix for all URLs in my app.
I'm familiar with doing a static prefix, such as url(r'^myprefix/app', include('app.urls')).
Instead, I need the myprefixto be dynamic, such as url(r'^(?P<prefix>\w+)/app', include('app.urls')).
That works, but here's the kicker. I don't want that prefix to be sent as a keyword argument to all of the views. I want to be able to capture it and use it in Middleware or something similar.
To give my specific use case, we have software (this Django project) used to manage various testing labs. The app needs knowledge of which lab it is operating on.
Currently I'm doing this with the following:
class LabMiddleware(object):
def process_request(self, request):
request.current_lab = 'lab1' # Note that this is currently hard coded
The requirement states that the users be able to go to a URL such as http://host/<lab_name>/app where the lab_name would then get used in my LabMiddleware. Because of this, I obviously don't want to have to accept the lab_name in every single one of my views as it's cumbersome and overkill.
UPDATE:
Building on what Sohan gave in his answer, I ended up using a custom middleware class:
urls.py
url(r'^(?P<lab_name>\w+)/', include('apps.urls')),
apps/urls.py
url(r'^app1/', include('apps.app1.urls', namespace='app1')),
middleware.py
class LabMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
lab_name = view_kwargs.get('lab_name')
if lab_name:
request.current_lab = Lab.objects.get(name=lab_name)
# Remove the lab name from the kwargs before calling the view
view_kwargs.pop('lab_name')
return view_func(request, *view_args, **view_kwargs)
settings.py
MIDDLEWARE_CLASSES = (
# Default Django middleware classes here...
'raamp.middleware.LabMiddleware',
)
This allowed me to have the lab name in the URL and to add it to the request. Then by removing it from view_kwargs, it doesn't get passed on to the view function and everything works as I intended it.
Also note that the code I have above isn't the most optimized (e.g. I'm querying the database for every request). I stripped out the code I have for caching this as it's not important to showing how this problem was solved, but is worth mentioning that some improvements should be made to this code if you are using it in a production system.
You can create a decorator that wraps each view function. The decorator can take care of any processing work you have on the lab name parameter, and every view won't need to see the lab_name param.
def process_lab_name(request, lab_name):
request.current_lab = lab_name
def lab(view_func):
def _decorator(request, lab_name, *args, **kwargs):
# process the lab_name argument
process_lab_name(request, lab_name)
# when calling the view function, exclude the lab_name argument
response = view_func(request, *args, **kwargs)
return response
return wraps(view_func)(_decorator)
#lab
def some_view(request):
return render(...)
And your route will look like url(r'^(?P<lab_name>\w+)/app'
Related
The application I am working has an overwritten endpoint for the Python Social Auth /complete/<backend>/ endpoint.
within our urls.py:
urlspatterns = [
...
# Override of social_auth
url(r'^api/v1/auth/oauth/complete/(?P<backend>[^/]+)/$',
social_auth_complete,
name='social_complete'),
...
]
within views.py:
from social_django.views import complete
def social_auth_complete(request, backend, *args, **kwargs):
"""Overwritten social_auth_complete."""
# some custom logic getting variables from session (Unrelated).
response = complete(request, backend, *args, **kwargs)
# Some custom logic adding args to the redirect (Unrelated).
We are attempting to implement a partial pipeline method. The first time the endpoint is called everything works as expected.
#partial
def required_info(strategy, details, user=None, is_new=False, *args, **kwargs):
"""Verify the user has all the required information before proceeding."""
if not is_new:
return
for field in settings.SOCIAL_USER_REQUIRED_DATA:
if not details.get(field):
data = strategy.request_data().get(field)
if not data:
current_partial = kwargs.get('current_partial')
social_provider = kwargs.get('backend')
return strategy.redirect(f'.../?partial_token={partial_token}&provider={social_provider}'
else:
details[field] = data
This redirects the user to the front end in which they fill out a form which calls a POST request to orginal API api/v1/auth/oauth/complete/(?P<backend>[^/]+)/ with the following in the data:
{
'required_fieldX': 'data',
...
'partial_token': '',
}
Key Issues
Two things go wrong; When I pdb into required_info there is never any data within strategy.request_data(). There is still data within the kwargs['request'].body and I can take the data out there.
However
But I am afraid that the second time around we never get into this block of code from social-core:
partial = partial_pipeline_data(backend, user, *args, **kwargs)
if partial:
user = backend.continue_pipeline(partial)
# clean partial data after usage
backend.strategy.clean_partial_pipeline(partial.token)
else:
user = backend.complete(user=user, *args, **kwargs)
I know this to be true because when I interrogate the database the original Partial object still exists as if backend.strategy.clean_partial_pipeline(partial.token) was never called.
Final Questions
Why is the social_django.views.complete not processing the POST request as expected and as it appears to be in all the example applications. Is there an issue from our overwriting it? Should I just create a separate endpoint to handle the POST request and if so how do mimic all that goes on within #psa such that I can call backend.continue_pipeline(partial)?
I think there's only one issue here, and that's that the Django strategy doesn't look into request.body when loading the request data, you can see the method in charge here. There you can see that it looks for request.GET and/or request.POST, but not body.
You can easily overcome this by defining your custom strategy that extends from the built-in one, and override the request_data method to look for the values in request.body. Then define the SOCIAL_AUTH_STRATEGY to point to your class.
I have a decorator which allows requests to get through only from a whitelisted set of IPs. How to I plug it in for all admin views with minimal change in code.
Here's the decorator.
def allow_by_ip(view_func):
def authorize(request, *args, **kwargs):
user_ip = request.META['REMOTE_ADDR']
if user_ip in WHITELIST_IP_INTERNAL_ADMIN:
return view_func(request, *args, **kwargs)
return Response({'error': 'Not allowed!'}, status.HTTP_403_FORBIDDEN)
return authorize
It is better and clearer to create custom middleware for django, where you will check requested path and, if it is an admin path your code will check the IP against of whitelist.
You can make a file like whitelist_middleware.py:
class WhitelistIPMiddleware(object):
def process_request(self, request):
request_ip = request.META.get('REMOTE_ADDR', None)
if request_ip not in WHITELIST_IP_INTERNAL_ADMIN:
raise Http403 # or 404, if you want
return None
That you place that file into the same directory with settings.py (by default, if you have no special directory for middlewares). And after that add it into MIDDLEWARE_CLASSES tuple in the settings.py, of course.
Another way is to user class-based views. But it may require some refactoring, which could be painful. So I think, it's better to user middleware. It is a clean and simple way to add some special handling for a bunch of views.
I'm trying to make middleware which alters some fields for the user based on subdomain, etc...
The only problem is the request.user always comes in as AnonymousUser within the middleware, but is then the correct user within the views. I've left the default authentication and session middleware django uses within the settings.
There is a similar question here: Django, request.user is always Anonymous User
But doesn't overly answer the total question because I'm not using different authentication methods, and djangos authentication is running before I invoke my own middleware.
Is there a way, while using DRF, to get the request.user within the middleware? I'll show some sample code here:
class SampleMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
#This will be AnonymousUser. I need it to be the actual user making the request.
print (request.user)
def process_response(self, request, response):
return response
with process_request:
class SampleMiddleware(object):
def process_request(self, request):
#This will be AnonymousUser. I need it to be the actual user making the request.
print (request.user)
def process_response(self, request, response):
return response
I've solved this problem by getting DRF token from the requests and loading request.user to the user associated to that model.
I had the default django authentication and session middleware, but it seems DRF was using it's token auth after middleware to resolve the user (All requests were CORS requests, this might have been why). Here's my updated middleware class:
from re import sub
from rest_framework.authtoken.models import Token
from core.models import OrganizationRole, Organization, User
class OrganizationMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
header_token = request.META.get('HTTP_AUTHORIZATION', None)
if header_token is not None:
try:
token = sub('Token ', '', header_token)
token_obj = Token.objects.get(key = token)
request.user = token_obj.user
except Token.DoesNotExist:
pass
#This is now the correct user
print (request.user)
This can be used on process_view or process_request as well.
Hopefully this can help someone out in the future.
Came across this today while having the same problem.
TL;DR;
Skip below for code example
Explanation
Thing is DRF have their own flow of things, right in the middle of the django request life-cycle.
So if the normal middleware flow is :
request_middleware (before starting to work on the request)
view_middleware (before calling the view)
template_middleware (before render)
response_middleware (before final response)
DRF code, overrides the default django view code, and executes their own code.
In the above link, you can see that they wrap the original request with their own methods, where one of those methods is DRF authentication.
So back to your question, this is the reason using request.user in a middleware is premature, as it only gets it's value after view_middleware** executes.
The solution I went with, is having my middleware set a LazyObject.
This helps, because my code (the actual DRF ApiVIew) executes when the actual user is already set by DRF's authentication.
This solution was proposed here together with a discussion.
Might have been better if DRF had a better way to extend their functionality, but as things are, this seems better than the provided solution (both performance and readability wise).
Code Example
from django.utils.functional import SimpleLazyObject
def get_actual_value(request):
if request.user is None:
return None
return request.user #here should have value, so any code using request.user will work
class MyCustomMiddleware(object):
def process_request(self, request):
request.custom_prop = SimpleLazyObject(lambda: get_actual_value(request))
The accepted answer only takes TokenAuthentication in consideration - in my case, there are more authentication methods configured. I thus went with initializing the DRF's Request directly which invokes DRF's authentication machinery and loops through all configured authentication methods.
Unfortunately, it still introduces an additional load on the database since the Token object must be queried (the accepted answer has this problem as well). The trick with SimpleLazyObject in this answer is a much better solution, but it didn't work for my use case because I need the user info in the middleware directly - I'm extending the metrics in django_prometheus and it processes the request before get_response is called.
from rest_framework.request import Request as RestFrameworkRequest
from rest_framework.views import APIView
class MyMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
drf_request: RestFrameworkRequest = APIView().initialize_request(request)
user = drf_request.user
...
return self.get_response(request)
Based on Daniel Dubovski's very elegant solution above, here's an example of middleware for Django 1.11:
from django.utils.functional import SimpleLazyObject
from organization.models import OrganizationMember
from django.core.exceptions import ObjectDoesNotExist
def get_active_member(request):
try:
active_member = OrganizationMember.objects.get(user=request.user)
except (ObjectDoesNotExist, TypeError):
active_member = None
return active_member
class OrganizationMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
request.active_member = SimpleLazyObject(lambda: get_active_member(request))
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
Daniel Dubovski's solution is probably the best in most cases.
The problem with the lazy object approach is if you need to rely on the side effects. In my case, I need something to happen for each request, no matter what.
If I'd use a special value like request.custom_prop, it has to be evaluated for each request for the side effects to happen. I noticed that other people are setting request.user, but it doesn't work for me since some middleware or authentication class overwrites this property.
What if DRF supported its own middleware? Where could I plug it in? The easiest way in my case (I don't need to access the request object, only the authenticated user) seems to be to hook into the authentication class itself:
from rest_framework.authentication import TokenAuthentication
class TokenAuthenticationWithSideffects(TokenAuthentication):
def authenticate(self, request):
user_auth_tuple = super().authenticate(request)
if user_auth_tuple is None:
return
(user, token) = user_auth_tuple
# Do stuff with the user here!
return (user, token)
Then I could just replace this line in my settings:
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
#"rest_framework.authentication.TokenAuthentication",
"my_project.authentication.TokenAuthenticationWithSideffects",
),
# ...
}
I'm not promoting this solution, but maybe it will help someone else.
Pros:
It to solves this specific problem
There's no double authentication
Easy to maintain
Cons:
Not tested in production
Things happen in an unexpected place
Side effects...
I know it's not exactly answering the 'can we access that from the middleware' question, but I think it's a more elegant solution VS doing the same work in the middleware VS what DRJ does in its base view class. At least for what I needed, it made more sense to add here.
Basically, I'm just overriding the method 'perform_authentication()' from DRF's code, since I needed to add more things related to the current user in the request. The method just originally call 'request.user'.
class MyGenericViewset(viewsets.GenericViewSet):
def perform_authentication(self, request):
request.user
if request.user and request.user.is_authenticated():
request.my_param1 = 'whatever'
After that in your own views, instead of settings APIView from DRF as a parent class, simply set that class as a parent.
I wasn't quite happy with the solutions out there. Here's a solution that uses some DRF internals to make sure that the correct authentication is applied in the middleware, even if the view has specific permissions classes. It uses the middleware hook process_view which gives us access to the view we're about to hit:
class CustomTenantMiddleware():
def process_view(self, request, view_func, view_args, view_kwargs):
# DRF saves the class of the view function as the .cls property
view_class = view_func.cls
try:
# We need to instantiate the class
view = view_class()
# And give it an action_map. It's not relevant for us, but otherwise it errors.
view.action_map = {}
# Here's our fully formed and authenticated (or not, depending on credentials) request
request = view.initialize_request(request)
except (AttributeError, TypeError):
# Can't initialize the request from this view. Fallback to using default permission classes
request = APIView().initialize_request(request)
# Here the request is fully formed, with the correct permissions depending on the view.
Note that this doesn't avoid having to authenticate twice. DRF will still happily authenticate right afterwards.
I had the same issue and decided to change my design. Instead of using a Middleware I simply monkey-patch rest_framework.views.APIView.
In my case I needed to patch check_permissions but you can patch whatever fits your problem. Have a look at the the source code.
settings.py
INSTALLED_APPS = [
..
'myapp',
]
myapp/patching.py
import sys
from rest_framework.views import APIView as OriginalAPIView
class PatchedAPIView(OriginalAPIView):
def check_permissions(self, request):
print(f"We should do something with user {request.user}"
return OriginalAPIView.check_permissions(self, request)
# We replace the Django REST view with our patched one
sys.modules['rest_framework'].views.APIView = PatchedAPIView
myapp/__init__.py
from .patching import *
We use class based views for most of our project. We have run into an issue when we try and create a CSV Mixin that will allow the user to export the information from pretty much any page as a CSV file. Our particular problem deals with CSV files, but I believe my question is generic enough to relate to any file type.
The problem we are having is that the response from the view is trying to go to the template (say like from django.views.generic import TemplateView). We specify the template in the urls.py file.
url(r'^$', MyClassBasedView.as_view(template_name='my_template.html'))
How can you force the response to bypass the template and just return a standard HttpResponse? I'm guessing you'll need to override a method but I'm not sure which one.
Any suggestions?
EDIT1: It appears that I was unclear as to what we are trying to do. I have rendered a page (via a class based view) and the user will see reports of information. I need to put in a button "Export to CSV" for the user to press and it will export the information on their page and download a CSV on to their machine.
It is not an option to rewrite our views as method based views. We deal with almost all class based view types (DetailView, ListView, TemplateView, View, RedirectView, etc.)
This is a generic problem when you need to provide different responses for the same data. The point at which you would want to interject is when the context data has already been resolved but the response hasn't been constructed yet.
Class based views that resolve to the TemplateResponseMixin have several attributes and class methods that control how the response object is constructed. Do not be boxed into thinking that the name implies that only HTML responses or those that need template processing can only be facilitated by this design. Solutions can range from creating custom, reusable response classes which are based on the behavior of the TemplateResponse class or creating a reusable mixin that provides custom behavior for the render_to_response method.
In lieu of writing a custom response class, developers more often provide a custom render_to_response method on the view class or separately in a mixin, as it's pretty simple and straightforward to figure out what's going on. You'll sniff the request data to see if some different kind of response has to be constructed and, if not, you'll simply delegate to the default implementation to render a template response.
Here's how one such implementation might look like:
import csv
from django.http import HttpResponse
from django.utils.text import slugify
from django.views.generic import TemplateView
class CSVResponseMixin(object):
"""
A generic mixin that constructs a CSV response from the context data if
the CSV export option was provided in the request.
"""
def render_to_response(self, context, **response_kwargs):
"""
Creates a CSV response if requested, otherwise returns the default
template response.
"""
# Sniff if we need to return a CSV export
if 'csv' in self.request.GET.get('export', ''):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="%s.csv"' % slugify(context['title'])
writer = csv.writer(response)
# Write the data from the context somehow
for item in context['items']:
writer.writerow(item)
return response
# Business as usual otherwise
else:
return super(CSVResponseMixin, self).render_to_response(context, **response_kwargs):
Here's where you can also see when a more elaborate design with custom response classes might be needed. While this works perfectly for adding ad-hoc support for a custom response type, it doesn't scale well if you wanted to support, say, five different response types.
In that case, you'd create and test separate response classes and write a single CustomResponsesMixin class which would know about all the response classes and provide a custom render_to_response method that only configures self.response_class and delegates everything else to the response classes.
How can you force the response to bypass the template and just return
a standard HttpResponse?
This kinda defeats the point of using a TemplateView. If the thing you're trying to return isn't a templated response, then it should be a different view.
However...
I'm guessing you'll need to override a method but I'm not sure which one.
...if you prefer to hack it into an existing TemplateView, note from the source code...
class TemplateView(TemplateResponseMixin, ContextMixin, View):
"""
A view that renders a template. This view will also pass into the context
any keyword arguments passed by the url conf.
"""
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
return self.render_to_response(context)
...so you'd have to override the get() method so it doesn't call render_to_response() when returning your CSV. For example...
class MyClassBasedView(TemplateView):
def get(self, request, *args, **kwargs):
if request.GET['csv'].lower() == 'true':
# Build custom HTTP response
return my_custom_response
else:
return TemplateView.get(request, *args, **kwargs)
If you need a generic mixin for all subclasses of View, I guess you could do something like...
class MyMixin(object):
def dispatch(self, request, *args, **kwargs):
if request.GET['csv'].lower() == 'true':
# Build custom HTTP response
return my_custom_response
else:
return super(MyMixin, self).dispatch(request, *args, **kwargs)
class MyClassBasedView(MyMixin, TemplateView):
pass
It's not the same to POST to an URL than to GET it, DELETE it or PUT it. These actions are fundamentally different. However, Django seems to ignore them in its dispatch mechanism. Basically, one is forced to either ignore HTTP verbs completely or do this on every view:
def my_view(request, arg1, arg2):
if request.method == 'GET':
return get_view(request, arg1, arg2)
if request.method == 'POST':
return post_view(request, arg1, arg2)
return http.HttpResponseNotAllowed(['GET', 'POST'])
The few solutions I have found for this in the web (this snippet for verb-based dispatch, or this decorator for verb requirement) are not very elegant as they are clearly just workarounds.
The situation with CherryPy seems to be the same. The only frameworks I know of that get this right are web.py and Google App Engine's.
I see this as a serious design flaw for a web framework. Does anyone agree? Or is it a deliberate decision based on reasons/requirements I ignore?
I can't speak for Django, but in CherryPy, you can have one function per HTTP verb with a single config entry:
request.dispatch = cherrypy.dispatch.MethodDispatcher()
However, I have seen some situations where that's not desirable.
One example would be a hard redirect regardless of verb.
Another case is when the majority of your handlers only handle GET. It's especially annoying in that case to have a thousand page handlers all named 'GET'. It's prettier to express that in a decorator than in a function name:
def allow(*methods):
methods = list(methods)
if not methods:
methods = ['GET', 'HEAD']
elif 'GET' in methods and 'HEAD' not in methods:
methods.append('HEAD')
def wrap(f):
def inner(*args, **kwargs):
cherrypy.response.headers['Allow'] = ', '.join(methods)
if cherrypy.request.method not in methods:
raise cherrypy.HTTPError(405)
return f(*args, **kwargs):
inner.exposed = True
return inner
return wrap
class Root:
#allow()
def index(self):
return "Hello"
cowboy_greeting = "Howdy"
#allow()
def cowboy(self):
return self.cowboy_greeting
#allow('PUT')
def cowboyup(self, new_greeting=None):
self.cowboy_greeting = new_greeting
Another common one I see is looking up data corresponding to the resource in a database, which should happen regardless of verb:
def default(self, id, **kwargs):
# 404 if no such beast
thing = Things.get(id=id)
if thing is None:
raise cherrypy.NotFound()
# ...and now switch on method
if cherrypy.request.method == 'GET': ...
CherryPy tries to not make the decision for you, yet makes it easy (a one-liner) if that's what you want.
Came across this from Google, and thought of updating.
Django
Just FYI, This is now supported in Django as class based views. You can extend the generic class View and add methods like get(), post(), put() etc. E.g. -
from django.http import HttpResponse
from django.views.generic import View
class MyView(View):
def get(self, request, *args, **kwargs):
return HttpResponse('Hello, World!')
The dispatch() part handles this-
dispatch(request, *args, **kwargs)
The view part of the view – the
method that accepts a request argument plus arguments, and returns a
HTTP response.
The default implementation will inspect the HTTP method and attempt to
delegate to a method that matches the HTTP method; a GET will be
delegated to get(), a POST to post(), and so on.
By default, a HEAD request will be delegated to get(). If you need to
handle HEAD requests in a different way than GET, you can override the
head() method. See Supporting other HTTP methods for an example.
The default implementation also sets request, args and kwargs as
instance variables, so any method on the view can know the full
details of the request that was made to invoke the view.
Then you can use it in urls.py -
from django.conf.urls import patterns, url
from myapp.views import MyView
urlpatterns = patterns('',
url(r'^mine/$', MyView.as_view(), name='my-view'),
)
More details.
CherryPy
CherryPy now also supports this. They have a full page on this.
I believe the decision for django was made because usually just GET and POST is enough, and that keeps the framework simpler for its requirements. It is very convenient to just "not care" about which verb was used.
However, there are plenty other frameworks that can do dispatch based on verb. I like werkzeug, it makes easy to define your own dispatch code, so you can dispatch based on whatever you want, to whatever you want.
Because this is not hard to DIY. Just have a dictionary of accepted verbs to functions in each class.
def dispatcher(someObject, request):
try:
return someObject.acceptedVerbs[request.method]()
except:
return http.HttpResponseNotAllowed(someObject.acceptedVerbs.keys())