问题
I have the following situation
I have a manager class that filters a queryset according to a field. The problem is that the field name is different according to the class but the value to which it filters comes from the same place (so i thought i don't need several managers). This is what i did so far.
class MyManager(models.Manager):
def __init__(self, field_name):
super(MyManager, self).__init__()
self.field_name = field_name
def get_queryset(self):
# getting some value
kwargs = { self.field_name: some_value }
return super(MyManager, self).get_queryset().filter(**kwargs)
class A:
# some datamembers
@property
def children(self):
return MyUtils.prepare(self.b_set.all())
class B:
objects = MyManager(field_name='my_field_name')
a = models.ForeignKey(A, null=False, blank=False)
When i run tests i that retrieve from the DB a B object, and try to read the children
property i get the following error:
self = <django.db.models.fields.related_descriptors.RelatedManager object at 0x7f384d199290>, instance = <A: A object>
def __init__(self, instance):
> super(RelatedManager, self).__init__()
E TypeError: __init__() takes exactly 2 arguments (1 given)
I know its because of the constructor parameter because when i remove it (or give it a default value) all of the tests work.
How can i overcome this? Is this the right way of achieving this?
Tech stuff:
- Django 1.9.5
- test framework py.test 2.9.1
Thanks
回答1:
Another option would be to generate the Manager class dynamically, such as:
def manager_factory(custom_field):
class MyManager(models.Manager):
my_field = custom_field
def get_queryset(self):
# getting some value
kwargs = {self.my_field: 'some-value'}
return super(MyManager, self).get_queryset().filter(**kwargs)
return MyManager()
class MyModel(models.Model):
objects = manager_factory('custom_field')
This way you can decouple the Manager from the Model class.
回答2:
As you can see, that error is happening because Django instantiates a new Manager whenever you make a related objects call; that instantiation wouldn't get the extra parameter.
Rather than getting the value this way, you could try making it an attribute of the model and then referencing it via self.model
.
class MyManager(models.Manager):
def get_queryset(self):
# getting some value
kwargs = { self.model.manager_field_name: some_value }
class B:
manager_field_name = 'my_field_name'
objects = MyManager()
来源:https://stackoverflow.com/questions/39146075/creating-a-django-manager-with-a-parameter