Changing class attributes in decorator - python

I'm using django as my framework for some web application.
I implemented a modelview of my own because I have a few querysets and seriazliers in the same view.
For this use, I needed to implement all of the CRUD functions myself:
class Models1AndModel2View(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
model1 = Model1.object.all()
model1_serializer_class = Model1Seriazlizer
model2 = Model2.object.all()
model2_serializer_class = Model2Seriazlizer
def refresh_querysets(func):
def inner(self, *args, **kwargs):
value = func(self, *args, **kwargs)
self.model1 = Model1.object.all()
self.model2 = Model2.object.all()
return value
return inner
#refresh_querysets
def list(self, request, *args, **kwargs):
...
#refresh_querysets
def retrieve(self, pk, request, *args, **kwargs):
...
#refresh_querysets
def update(self, pk, request, *args, **kwargs):
...
#refresh_querysets
def delete(self, pk, request, *args, **kwargs):
...
#refresh_querysets
def create(self, request, *args, **kwargs):
...
Notice that I'm calling the decorator's function before the objects refresh.
I noticed that every attribute set after the function calls is not actually set.
For example - some test of mine:
list all the models - 2 models instances
delete one of them
list them again - still 2 models instances (in model1 + model2)
if you query the model1 and model2 you can see that one of the instances is deleted as expected, but the model1 was not refreshed.
I changed the order on the inner function of the decorator, and it worked as expected.
def refresh_querysets(func):
def inner(self, *args, **kwargs):
self.model1 = Model1.object.all()
self.model2 = Model2.object.all()
return func(self, *args, **kwargs)
return inner

I believe your issue is because the view is temporary.
You save self.model1 on the view which is destroyed and recreated per request.
There is no point updating the self if the object will be destroyed.
Also, you should remember that model1 as set in the class variable is initialized on class creation only, and not on instance creation, meaning it will always contain the original data from when you imported that respective module.
There are 3 options to solve it:
Change the order of the function decorator as you've done, setting self.model1 before running the function, or pass it as a parameter.
Change model1 on __init__, and so the changes will apply on instance creation.
While frowned upon, the class attribute model1 is right now equivalent to a "class global" (and has almost all of a global downsides). You can simply change it like so:
self.__class__.model1 = Model1.object.all()
Keep in mind I do not believe this class attribute should even exist. Should it be immutable, setting it on the instance creation makes more sense.
Either case, I'm not sure if you wish to dump the entire object / DB at every request. I do not know if django smartly caches object.all() (and updates the cache upon modification), but if it doesn't it'll be a major slowdown.

Related

Python mixin/decorator/__metaclass__ for base class enhancement

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.

Inherit Variables from Parent Class

I am trying to work out how to inherit variables from a parent class.
I have two classes (simplified but same principle):
class Database(object):
def __init__(self, post, *args, **kwargs):
self.post = post
self.report()
def report(self):
#... obtain variables from post ...
self.database_id = self.post['id']
#... save data to database
class PDF(Database):
def __init__(self, post, *args, **kwargs):
Database.__init__(self, post, *args, **kwargs)
#... if i try to access self.database_id now, it returns an error ...
print(self.database_id)
instantiating script:
Database(request.POST)
PDF(request.POST)
I have tried just instantiating CreatePDF, as i thought the Database.__init__(self, post, *args, **kwargs) line would the Database class, but this does not work either.
I am trying to find the most pythonic way to do inherit. I can obviously obtain self.database_id from the post dict passed to PDF(), however I do not see the point in doing this twice, if I can use inheritance.
Thanks
Use:
class PDF(Database):
def __init__(self, post, *args, **kwargs):
# Stuff
super().__init__(post, *args, **kwargs)
The correct approach to instantiated an inherited class is to call super().init(args), which in this case calls Database.init because of method resolution order.
See http://amyboyle.ninja/Python-Inheritance

Use #staticmethod in Django Rest Framework endpoint method?

Should I be defining post, get, etc methods of a Django Rest Framework APIView as static?
class HomeView(APIView):
def get(request):
etc...
or
class HomeView(APIView):
#staticmethod
def get(request):
etc...
What are the pros/cons of each way?
Thank you
DRF does not declare get and post as static methods and neither should you. Here's how the defaults are configured in DRF generics.
It is common in DRF to reference instance methods such as self.get_object and self.get_serializer from within get and post.
class CreateAPIView(mixins.CreateModelMixin,
GenericAPIView):
"""
Concrete view for creating a model instance.
"""
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class ListAPIView(mixins.ListModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
class RetrieveAPIView(mixins.RetrieveModelMixin,
GenericAPIView):
"""
Concrete view for retrieving a model instance.
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
class DestroyAPIView(mixins.DestroyModelMixin,
GenericAPIView):
"""
Concrete view for deleting a model instance.
"""
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
class UpdateAPIView(mixins.UpdateModelMixin,
GenericAPIView):
"""
Concrete view for updating a model instance.
"""
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs)
As DRF document, You seems do not need to add #staticmethod decorator
DRF won't function correctly without passing ' self '.
PyCharm does mark these methods as 'maybe static', this is false!
Making them static breaks the calling of the method, since the caller method provides the 'self', and your method does not except it. Essentially your still get the self aliased as request, but your other args are ignored. It is possible that this happens silently, that makes it a fairly nasty bug to find :(.
In terms of performance a 'static' or regular function based view should be slightly faster, since you avoid the creation of the instance, but the difference is probably so small that it is hardly noticeable.

Django class view: __init__

I want to get <Model> value from a URL, and use it as an __init__ parameter in my class.
urls.py
url(r'^(?P<Model>\w+)/foo/$', views.foo.as_view(), name='foo_class'),
views.py
class foo(CreateView):
def __init__(self, **kwargs):
text = kwargs['Model'] # This is not working
text = kwargs.get('Model') # Neither this
Bar(text)
...
Clearly, I'm missing something, or my understanding of URL <> class view is wrong.
You should override dispatch method for such use cases.
class Foo(CreateView):
def dispatch(self, request, *args, **kwargs):
# do something extra here ...
return super(Foo, self).dispatch(request, *args, **kwargs)
For your specific scenario, however, you can directly access self.kwargs as generic views automatically assign them as an instance variable on the view instance.

Use a method from other classes w/o inheritance

This question is about Python inheritance but is explained with a Django example, this should't hurt though.
I have this Django model, with Page and RichText models as well:
class Gallery(Page, RichText):
def save(self, *args, **kwargs):
# lot of code to unzip, check and create image instances.
return "something"
I'm only interested in using the save method in another class.
A solution could be:
class MyGallery(models.Model):
def save(self, *args, **kwargs):
# here goes the code duplicated from Gallery, the same.
return "something"
I'd like to avoid the code duplication and also I'm not interested in inheriting members from Page and RichText (so I don't want to do class MyGallery(Gallery):. If it would be legal I'd write something like this:
class MyGallery(models.Model):
# custom fields specific for MyGallery
# name = models.CharField(max_length=50)
# etc
def save(self, *args, **kwargs):
return Gallery.save(self, *args, **kwargs)
But it won't work because the save() in Gallery expects an instance of Gallery, not MyGallery.
Any way to "detach" the save() method from Gallery and use it in MyGallery as it were defined there?
EDIT:
I forgot to say that Gallery is given and can't be changed.
You can access the __func__ attribute of the save method:
class Gallery(object):
def save(self, *args, **kwargs):
return self, args, kwargs
class MyGallery(object):
def save(self, *args, **kwargs):
return Gallery.save.__func__(self, *args, **kwargs)
# or
# save = Gallery.save.__func__
mg = MyGallery()
print mg.save('arg', kwarg='kwarg')
# (<__main__.MyGallery object at 0x04DAD070>, ('arg',), {'kwarg': 'kwarg'})
but you're better off refactoring if possible:
class SaveMixin(object):
def save(self, *args, **kwargs):
return self, args, kwargs
class Gallery(SaveMixin, object):
pass
class MyGallery(SaveMixin, object):
pass
or
def gallery_save(self, *args, **kwargs):
return self, args, kwargs
class Gallery(object):
save = gallery_save
class MyGallery(object):
save = gallery_save
I'm not sure why you are against inheritance, particularly with regard to methods. I regularly create a MixIn class that is inherited by all of my Django models.Model, and it contains all manner of useful methods for URL creation, dumps, etc., etc. I do make the methods defensive in that they use hasattr() to make sure they apply to a particular class, but doing this is a real time saver.

Categories