Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a django manager with a parameter

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:

  1. Django 1.9.5
  2. test framework py.test 2.9.1

Thanks

like image 567
Mr T. Avatar asked Aug 25 '16 13:08

Mr T.


People also ask

How do I create a custom manager in Django?

Custom managers. You can use a custom Manager in a particular model by extending the base Manager class and instantiating your custom Manager in your model. There are two reasons you might want to customize a Manager : to add extra Manager methods, and/or to modify the initial QuerySet the Manager returns.

What is custom model manager in Django?

It is used to get queryset of objects based on the properties it takes as arguments. If no argument is provided then it will return all the objects. In this example, we create a custom django manager by inheriting Manager class and overriding only the get_queryset method . So we have defined a custom manager .

What is QuerySet in Django?

A QuerySet is a collection of data from a database. A QuerySet is built up as a list of objects. QuerySets makes it easier to get the data you actually need, by allowing you to filter and order the data.

What is base manager in Django?

A Manager is a Django class that provides the interface between database query operations and a Django model. In other words, in a Django model, the manager is the interface that interacts with the database.


2 Answers

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.

like image 153
odedfos Avatar answered Oct 03 '22 08:10

odedfos


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()
like image 37
Daniel Roseman Avatar answered Oct 03 '22 06:10

Daniel Roseman