I have code like below
url(r'login$', views.LoginView.as_view(), name='login'),
and view as follows
class LoginView(TemplateView):
def __init__(self, *args, **kwargs):
#How to operate on request Object's type and its params.
I have mentioned my question as comment in code.
As mentioned by #karthikr you shouldn't be overriding __init__(). The request object is first available in the dispatch() method, which is called immediately after __init__(), but you shouldn't need to override that method either. Its primary purpose is to call the get(), post(), or other relevant method handlers. Generally speaking, however, it's not necessary to override those either.
If you really absolutely must catch the request at the earliest point possible though, then the dispatch method is your best bet.
class LoginView(TemplateView):
def dispatch(self, request, *args, **kwargs):
print self.request # Works!
return super(LoginView, self).dispatch(request, *args, **kwargs) # Don't forget this
If you want to initialize variables, the best place is the setup function:
class SomeBaseView(View):
var1 = None
def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs)
# do the vars initialization here
self.var1 = kwargs.get('param') # e.g.
By the way, setup function is called before dispatch and you dont need to return anything unlike dispatch.
When the view is called during the request/response cycle, the setup() method assigns the HttpRequest to the view’s request attribute, and any positional and/or keyword arguments captured from the URL pattern to the args and kwargs attributes, respectively. Then dispatch() is called.
https://docs.djangoproject.com/en/4.1/ref/class-based-views/base/#django.views.generic.base.View.as_view
Using dispatch function on the other hand is NOT recommended since you need to return the super call which can make things complicated.
Related
I am implementing a content-aware caching system for a Django REST API. I would like to develop a component which can be added to existing views that would modify the behavior of the base class by checking the cache and falling back to the base class behavior on a miss.
basically, I have something like this:
class Base:
def get(self, request, *args, **kwargs):
....
return Response
class AnotherBase:
def get(self, request, *args, **kwargs):
....
return Response
class Derived(Base):
pass
class OtherDerived(AnotherBase):
pass
and my initial thought was to do something along the lines of
class Cacheable:
def get(self, request, *args, **kwargs):
cache_key = self.get_cache_key(request)
base_get = #.... and this is the problem
return cache.get(cache_key, base_get(request, *args, **kwargs))
def get_cache_key(self, request):
# .... do stuff
class Derived(Cacheable, Base):
pass
class AnotherDerived(Cacheable, AnotherBase):
pass
So clearly this doesn't work, as I don't know how, or if it's possible, or if it's advisable to access the sibling superclass(es) from a mixin.
My goal is an implementation that allows me to add caching behavior to existing views without touching the internals of the existing classes.
Given a view class, C, s.t. C.get(request, *args, **kwargs) -> Response, is there a function, F, s.t. F(C).get(... does the cache check before falling back to C.get? And in this quasi-formal notation, we'll say that adding a mixin to the leftmost parent class in the class definition counts as a function.
Is it more appropriate to use method decorators? or how would a class decorator work?
And then I've seen references to __metaclass__ in researching this, but I'm not clear on what that approach looks like.
This is Python 3.6
Simple example:
def Base:
def _get_data(self):
# get the data eg from database
return self._get_data_native()
def get(self, request, *args, **kwargs):
return Response(self._get_data())
def Cacheable(Base):
def _get_data(self):
# get from cache ...
result = ...
if result is None:
# or from base ...
result = ...
return result
def Derived(Cacheable):
def _get_data_native(self):
# get the data eg from database
...
By inheriting from Cacheable, you include the caching here, because _get_data is overwritten there.
For this problem, you don't need metaclasses or decorators, if you want to just add caching at one place.
Of course, a decorator could be used for including caching in an even more generic way.
See for example this answer: Is there a decorator to simply cache function return values?
The answer was a decorator and some Django-specific libraries.
from django.utils.decorators import method_decorator
from django.core.cache import cache
def cached_get(cache_key_func=None):
"""
Decorator to be applied via django.utils.decorators.method_decorator
Implements content-aware cache fetching by decorating the "get" method
on a django View
:param cache_key_func: a function of fn(request, *args, **kwargs) --> String
which determines the cache key for the request
"""
def decorator(func):
def cached_func(request, *args, **kwargs):
assert cache_key_func is not None, "cache_key_function is required"
key = cache_key_func(request, *args, **kwargs)
result = cache.get(key)
if result is None:
return func(request, *args, **kwargs)
return Response(result)
return cached_func
return decorator
#method_decorator(cached_get(cache_key_func=get_cache_key), name="get")
class SomeView(BaseView):
...
def get_cache_key(request):
# do arbitrary processing on request, the following is the naïve melody
key = urllib.urlencode(request.query_params)
return key
So the solution is to use Django's built-in method_decorator which applies its first argument, a decorator, to the decorated class's method, named by the second argument, name, to method_decorator. I define a higher-order function, cached_get, which takes another function as its argument, and returns a curried function (closure, so called). By calling this, with the function get_cache_key (and not, mind you, invoking that function) I have a decorator that will be applied to the 'get' method on SomeView.
The decorator itself is a straightforward Python decorator -- in this application, it is cached_func and the original, undecorated get method is func. Thus, cached_func replaces SomeView.get, so when SomeView.get is called, it first checks the cache, but falls back to the undecorated method on a miss.
I'm hopeful this approach provides a balance of generic applicability with content-aware key derivation.
My two cents:
You're walking into obscure territory here. Get familiar with all the related concepts, try a few, then decide.
Here is a good tutorial about metaclasses.
Here there's one about decorators.
I'm in no way affiliated to that site.
In a class based view, HTTP methods map to class method names. Below, defined a handler for GET requests with the get method and url called get method. My question is how did the url map to the get method?
url(r'^hello-world/$', MyView.as_view(), name='hello_world'),
class MyView(View):
def get(self, request, *args, **kwargs):
return HttpResponse("Hello, World")
The url doesn't map to the get method, it maps to the view. Its up to the request method to guide django in the right way.
If you're talking in terms of actual code, its the dispatch method on the view.
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
Why not have a look at the code.
http://ccbv.co.uk/projects/Django/1.10/django.views.generic.base/View/
You will see the as_view() method (which gets called in your urls.py) has at line 67:
return self.dispatch(request, *args, **kwargs)
The dispatch() method in turn calls get in line 85 (assuming it is a GET request):
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
In my urls I want to pass additional args like this
MyListView.as_view(extra="test")
But when I do that then I get error that I can only pass those attributes which are defined in class.
I tried this
class MyListView(APIView):
def as_view(self, extra=None, **kwargs):
self.extra=kwargs.pop('extra', None)
super(MyListView, self).as_view(**kwargs)
Then I get
unbound method as_view() must be called with MyListView instance as first argument (got nothing instead)
The keyword arguments to the MyListView.as_view() call are passed to the __init__ method each time a view instance is needed (e.g. when handling a request); you can override that method to capture the extra keyword:
class MyListView(APIView):
def __init__(self, extra=None, **kwargs):
self.extra = extra
super(MyListView, self).__init__(**kwargs)
The as_view() method must be a classmethod; it is not called on an instance of the view:
class MyListView(APIView):
#classmethod
def as_view(cls, extra=None, **kwargs):
cls.extra = extra
return super(MyListView, cls).as_view(**kwargs)
The extra keyword argument is explicitly named so it'll never be found in the kwargs catch-all. You also want to return the result of the super() call.
Note that the extra attribute is then also shared between all instances of the view! You may as well set it directly on the view class:
class MyListView(APIView):
extra = 'test'
Since as_view() must produce an instance, you can add the attribute on the return value of the super() call before passing it on:
class MyListView(APIView):
#classmethod
def as_view(cls, extra=None, **kwargs):
view = super(MyListView, cls).as_view(**kwargs)
view.extra = extra
return view
but then overriding the __init__ is achieving the same result and easier to follow for future maintainers.
I'm building a rate-limiting decorator in flask using redis stores that will recognize different limits on different endpoints. (I realize there are a number of rate-limiting decorators out there, but my use case is different enough that it made sense to roll my own.)
Basically the issue I'm having is ensuring that the keys I store in redis are class-specific. I'm using the blueprint pattern in flask, which basically works like this:
class SomeEndpoint(MethodView):
def get(self):
# Respond to get request
def post(self):
# Respond to post request
The issue here is that I want to be able to rate limit the post method of these classes without adding any additional naming conventions. In my mind the best way to do this would be something like this:
class SomeEndpoint(MethodView):
#RateLimit # Access SomeEndpoint class name
def post(self):
# Some response
but within the decorator, only the post function is in scope. How would I get back to the SomeEndpoint class given the post function? This is the basic layout of the decorator. That might be confusing, so here's a more concrete example of the decorator.
class RateLimit(object):
"""
The base decorator for app-specific rate-limiting.
"""
def __call__(self, f):
def endpoint(*args, **kwargs):
print class_backtrack(f) # Should print SomeEnpoint
return f(*args, **kwargs)
return endpoint
basically looking for what that class_backtrack function looks like. I've looked through the inspect module, but I haven't found anything that seems to accomplish this.
You can decorate the entire class instead of just the methods:
def wrap(Class, method):
def wrapper(self, *args, **kwargs):
print Class
return method(self, *args, **kwargs)
return method.__class__(wrapper, None, Class)
def rate_limit(*methods):
def decorator(Class):
for method_name in methods:
method = getattr(Class, method_name)
setattr(Class, method_name, wrap(Class, method))
return Class
return decorator
#rate_limit('post')
class SomeEndpoint(object):
def post(self):
pass
class Subclass(SomeEndpoint):
pass
a = Subclass()
a.post()
# prints <class 'SomeEndpoint'>
Is there a way to internally pass on the handling of a request from one RequestHandler subclass to another? Basically, what I would like to do is, from the get method of a RequestHandler (well, a subclass of RequestHandler), dispatch the task of handling the request to another handler (subclass of RequestHandler), whose name is determined by a value fetched from the datastore (I'm using GAE, but that is irrelevant to this problem). The code would look something like this:
class Dispatcher(RequestHandler):
def get_handler(some_id):
handler_name = get_handler_name(some_id) # fetches from datastore/etc.
return getattr(my_module, handler_name)
def get(self, some_id, *args, **kwargs):
handler = get_handler(some_id) # e.g., handler could be a HandlerA
# Not a real function, just to describe what to do:
# invokes get method of HandlerA (if handler == HandlerA)
dispatch_to_handler(handler, self, *args, **kwargs)
def post(self, some_id):
handler = get_handler(some_id)
dispatch_to_handler(....) # dispatches to post method of the handler
class HandlerA(RequestHandler):
def get(self, *args, **kwargs):
do_stuff()
def post(...):
do_post_stuff()
The big issue is that I need to somehow pass self and the positional and keyword arguments on to the other handler (HandlerA in this example), as self contains the request, response, session, authentication, and other data, which HandlerA (or whatever the handler may be) needs in order to process the request.
Try it this way:
def get(self, some_id, *args, **kwargs)
handler_cls = get_handler(some_id)
handler = handler_cls(self.request, self.response)
return handler.dispatch()