问题
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