Problems with creating custom chainable query set manager - python

In my project I'm using code from http://djangosnippets.org/snippets/562/ and I'm encountering some problems.
When I use my own ChainableQuerySetManager and try to create an object through a related object I get a recursion error.
ChainableQuerySetManager source:
class ChainableQuerySetManager(models.Manager):
def __init__(self, qs_class=models.query.QuerySet):
super(ChainableQuerySetManager,self).__init__()
self.queryset_class = qs_class
def get_query_set(self):
return self.queryset_class(self.model)
def __getattr__(self, attr, *args):
try:
return getattr(self.__class__, attr, *args)
except AttributeError:
return getattr(self.get_query_set(), attr, *args)
Extended query set:
class ExtendedQuerySet(models.query.QuerySet):
def get_or_None(self, *args, **kwargs):
result = None
try:
result = self.get(*args, **kwargs)
except ObjectDoesNotExist:
pass
return result
And test models:
class ParentObject(models.Model):
value = models.IntegerField(default=0)
def count_someobjects(self):
return self.someobjects.count()
def create_someobject_throw_related(self):
return self.someobjects.create()
def create_someobject(self):
return SomeObject.objects.create(parent=self)
class SomeObject(models.Model):
parent = models.ForeignKey(ParentObject, related_name='someobjects')
value = models.IntegerField(default=1)
objects = ChainableQuerySetManager(ExtendedQuerySet)
Test Case looks like:
class ExtendedQuerySetTests(TestCase):
def setUp(self):
self.parent = ParentObject.objects.create()
def test_create_someobject_in_parent(self):
someobject = self.parent.create_someobject_throw_related()
I would appreciate your help.
Full source can be found at https://github.com/RANUX/django-simptools

I recently had a similar issue. Try replacing self.__class__ with ChainableQuerySetManager in your query manager. I never quite sorted out exactly why this was the issue, but it did solve things for me.

Related

How can I auto-complete python code if i use dynamic type

Here is an example below:
class Service:
def __init__(self):
self.data = dict()
def get(self, clazz):
# clazz is a class Type, and always return an instance of clazz
return self.data[clazz]
def register(self, clazz, *args, **kwargs):
self.data[clazz] = clazz(*args, **kwargs)
class A:
def method_of_a(self):
pass
class B:
pass
service = Service()
service.register(A)
service.register(B)
now I complete the code of service, and continue coding like
x = service.get(A)
x.method_of_a()
the code works, but there is a problem, when i was coding, the IDE(pycharm in this case) will not show the function of x when you input
x.
even i modify my code like this, it does'nt work
def get(self, clazz):
result: clazz = self.data[clazz]
return result
I want to know if there any way to implement Service, that IDE can recognise the type of the return value?
Use typing.TypeVar and typing.Type to type-hint your .get() function to say "if called with the type of T, this returns an instance of T":
from typing import TypeVar, Type
T = TypeVar('T')
class Service:
def __init__(self):
self.data = dict()
def get(self, clazz: Type[T]) -> T:
return self.data[clazz]
def register(self, clazz, *args, **kwargs):
self.data[clazz] = clazz(*args, **kwargs)
class A:
def method_of_a(self):
pass
s = Service()
s.register(A)
x = s.get(A)
x.method_of_a()
reveal_type(x)
When run with mypy, the reveal_type() call prints out
so73566368.py:24: note: Revealed type is "so73566368.A"

How to remove a method from an object instance conditioned by __init__ parameters?

I would like to write a class that removes access to or throws an error in case a particular method is accessed and this behavior should be conditioned by a variable set upon class instantiation.
So in code I would like something like this:
class NewClass:
def __init__(self, has_non_available_method):
self.has_non_available_method = has_non_available_method
def some_method(self):
if self.has_non_available_method:
raise SomeError()
This is fine however, if I want to have a couple of methods like this then I have to copy-paste these 2 lines into every method. Which "doesn't seem very sophisticated" and I would like to find out if a "better"/DRY approach exists.
I considered using decorators but decorators execute upon class execution so they do not seem to be suitable for this task as the attribute has_non_available_method (in this example) will not be set at that point.
A decorator is perfect for this.
def checkavail(func):
def wrapper(self, *args, **kwargs):
if self.has_non_available_method:
raise SomeError()
func(self, *args, **kwargs)
return wrapper
class NewClass:
def __init__(self, has_non_available_method):
self.has_non_available_method = has_non_available_method
#checkavail
def some_method(self):
# do stuff
You can use decorator to make this behavior more dynamic.
def check_permission(method):
def call(self, *args, **kwargs):
if getattr(self, 'has_{}_permission'.format(method.__name__), False):
method(self, *args, **kwargs)
else:
raise Exception('{} needs permission'.format(method.__name__))
return call
class PermissionRequiredMethodClass(object):
def __init__(self, has_secret_method_permission: bool = False):
self.has_secret_method_permission = has_secret_method_permission
super().__init__()
def open_method(self):
print('in open method')
#check_permission
def secret_method(self):
# secret stuff
print('in secret')
use decorator
I add decorate_all_functions, so you don't need copy-paste the decorate itself to every class method.
code:
def decorate_all_functions(function_decorator):
def decorator(self):
for name, obj in vars(self).items():
if callable(obj):
try:
obj = obj.__func__
except AttributeError:
pass
setattr(self, name, function_decorator(obj))
return self
return decorator
from functools import wraps
def check_permission(func):
#wraps(func)
def wrapper(self, *args, **kwargs):
if func.__name__ in self.has_non_available_method:
raise Exception("Some Error")
func(self,*args, **kwargs)
return wrapper
#decorate_all_functions(check_permission)
class NewClass:
has_non_available_method = []
def __init__(self, has_non_available_method):
self.has_non_available_method = has_non_available_method
def some_method(self):
print("do something")
def another_method(self):
print("do another thing")
x = NewClass(["another_method"])
x.some_method()
try:
x.another_method()
except Exception as e:
print(e)
result:
do something
Some Error
use del
You can try delattr or del to remove class method dynamically.
code:
class NewClass:
def some_method(self):
print("do something")
def another_method(self):
print("do another thing")
x = NewClass()
x.some_method()
x.another_method()
delattr(type(x),"some_method")
# del NewClass.some_method
# both work
try:
x.some_method()
except Exception as e:
print(e)
x.another_method()
result:
do something
do another thing
'NewClass' object has no attribute 'some_method'
do another thing
But it will remove the class method, not the instance.
y = NewClass()
y.some_method()#raise AttributeError

How to decorate an instance with code-blocks and accessing/using the variables of the instance?

I try to build a decorator for methods of instances (not classes) that flexibly puts code blocks in front and/or behind the method (and not affect other instances). Up to my code below works:
def Decorate(func, before = None, after = None):
def wrap(*args, **kwargs):
if before: before() # code block insert
result = func(*args, **kwargs)
if after: after() # code block insert
return result
return wrap
class Test():
def __init__(self, name):
self.name = name
def put(self, prefix):
print(prefix, self.name)
a = Test('me')
def Before():
print('before')
def After():
print('after')
a.put = Decorate(a.put, Before, After)
a.put('it is')
How can I extend the code blocks accessing/using variables and methods of the instance? A code example for this would look like this:
def Before():
print('before')
print(self.name)
self.any_method(any_argument) # just an example!
I already tried several things without success. And I already struggle to access the instance values directly in the wrapper:
def Decorate(func, before = None, after = None):
def wrap(self, *args, **kwargs):
if before: before() # code block insert
print(self.name) # --> even this DOES NOT WORK!
result = func(self, *args, **kwargs)
if after: after() # code block insert
return result
return wrap
Here print(self.name) throws an error: AttributeError: 'str' object has no attribute 'name'. So it looks like that I am far away in using the same comment in one of the code blocks (Before() & After()) below.
One addition: The approach works when I add a method to the instance:
This method is in the class (so for working with strings and exec, but that enables to deliver the name as string or the function itself):
def addMethod(self, method, givenName = ''):
print('add')
if givenName == '':
N = method.__name__
else:
N = givenName
self._methods.append(N)
exec('self.' + N + ' = ' + method.__name__ + '.__get__(self)')
The code in the main part looks like this:
def x(self):
print('hello,', self.name)
a.addMethod(x)
a.x()
Any solution is appreciated and many thanks in advance!
from functools import wraps
def Decorate(func, before = None, after = None):
#wraps(func)
def wrap(*args, **kwargs):
if before: before() # code block insert
result = func(*args, **kwargs)
if after: after() # code block insert
return result
return wrap
def Before():
print('before')
def After():
print('after')
class Test():
def __init__(self, name):
self.name = name
def put(self, prefix):
print(prefix, self.name)
put = Decorate(put, Before, After)
a = Test('me')
a.put("pre")
You could execute your decorator inside your class. In your wrap, pass whatever you get to func by (*args, **kwargs). self is still the first argument implicitly in args.
Edit: Code related concerns from comments
from functools import wraps
def Before(t):
print('before')
print(t.name)
def After(t):
print('after')
print(t.name)
def Decorate(func, before = None, after = None):
#wraps(func)
def wrap(*args, **kwargs):
if before: before(args[0]) # code block insert
result = func(*args, **kwargs)
if after: after(args[0]) # code block insert
return result
return wrap
class Test():
def __init__(self, name):
self.name = name
def put(self, prefix):
print(prefix, self.name)
put = Decorate(put, Before, After)
a = Test('me')
a.put("pre")

Django - CustomQuerySet only works in objects

I'm using a CustomQuerySet using Custom QuerySet and Manager without breaking DRY?. I can only access custom functions using objects. Here's my Code:
class CustomQuerySetManager(models.Manager):
"""A re-usable Manager to access a custom QuerySet"""
def __getattr__(self, attr, *args):
print(attr)
try:
return getattr(self.__class__, attr, *args)
except AttributeError:
# don't delegate internal methods to the queryset
if attr.startswith('__') and attr.endswith('__'):
raise
return getattr(self.get_query_set(), attr, *args)
def get_query_set(self):
return self.model.QuerySet(self.model, using=self._db)
class SampleModel(models.Model):
objects = CustomQuerySetManager()
class QuerySet(models.QuerySet):
def test(self):
print("test function was callsed")
With this these happens:
SampleModel.objects.test() # This works
SampleModel.objects.all().test() # This doesnt works...
Why does this happen?

how to access property like dict.get('key') in python Class

class Investor:
def __init__(self, profile):
self.profile = profile
def __getitem__(self, item):
return self.profile[item]
It is ok to access Investor profile by simply Investor['name'],
But it comes to an error when I use get() Investor.get('name')
Raised: AttributeError: 'Investor' object has no attribute 'get'
I know I can fix it by adding a get() method to Investor Class, but is it a right way to do? or are there any other special method __get__ or whatever?
The standard get has a default as well. So this would be the full version:
def get(self, item, default=None):
return self.profile.get(item, default=default)
As for this being proper, as far as I know there isn't any better way so it is by default.
Why don't you just define a get function?
def get(self, item):
return self.profile.get(item)
As mentioned, there isn't a special "get" function which already exists and you can inherit from the object class. To get the functionality you want, you need to implement your own "get" function.
If you actually want to create a lot of similar classes to Investor which all have a get() function, then you should create a superclass for Investor to inherit from.
class Person(object):
def __init__(self, profile):
self.profile = profile
def get(self, item):
if item in self.profile:
return self.profile[item]
class Investor(Person):
def __init__(self, profile):
super().__init__(profile)
How about using #property ?
class Investor:
def __init__(self, profile):
self._profile = profile
#property
def profile(self):
return self._profile
if __name__ == "__main__":
inv = Investor(profile="x")
print(inv.profile)
Gives:
x
The most simple solution that you can have is to use try:#code except: #code block in __getitem__ method.For ex:
class Investor:
def __init__(self, profile):
self.profile = profile
def __getitem__(self, item):
try:
return self.profile[item]
except:
return 0
`
This will help you to get dictionary get() method like features without having to add new get() method.
Assuming you have an investor_object, like:
investor_object = Investor({'name': 'Bob', 'age': 21})
You can do either:
investor_object.profile['name']
or
investor_object.profile.get('name')
Gives:
Bob

Categories