4

Problem: I've implemented a custom manager for a model with just one custom query set named get_by_tag and it's working fine if I use it this way:

ViewStatistic.objects.get_by_tag('some-tag-name').filter(user=user_id)

But when I change the order of queries, in this way:

ViewStatistic.objects.filter(user=user_id).get_by_tag('some-tag-name')

it doesn't work! and raises this error:

AttributeError: 'QuerySet' object has no attribute 'get_by_tag'

Am I missing something?! How can I do this in such a order?

P.S: The custom manager is something like this:

class MyCustomManager(models.Manager):
    def get_by_tag(self, tag_name):
        posts = Post.objects.filter(tags__pk=tag_name)
        return super().get_queryset().filter(post__pk__in=posts)
Hamidreza
  • 1,465
  • 12
  • 31

2 Answers2

10

If you want to use your queryset methods inside of queryset chain and not only directly after the manager, you should define them as methods of custom QuerySet class that you connect to a manager.

Two solutions are described in Django documentation Creating a manager with QuerySet methods.

Common part - a custom QuerySet class with queryset methods

class MyCustomQuerySet(models.QuerySet):
    def get_by_tag(self, tag_name):
        return self.filter(post__pk__in=Post.objects.filter(tags__pk=tag_name))

    # more possible queryset methods ...

A) if your manager has only queryset methods and no other custom methods
    then you can create it simply from the QuerySet.

class MyModel(models.Model):
    objects = MyCustomQuerySet.as_manager()

B) if your manager need also other methods that do not return a queryset:

class MyCustomManager(models.Manager):
    ...  # other methods

class MyModel(models.Model):
    objects = MyCustomManager.from_queryset(MyCustomQuerySet)()
hynekcer
  • 14,942
  • 6
  • 61
  • 99
0

When you say ViewStatistic.objects it returns the object of <django.db.models.manager.Manager>

In your case since it have derived class MyCustomManager having base class models.manager, so it return object of <your_app.models.MyCustomManager> which have get_by_tag function, and you can access get_by_tag.

For second case ViewStatistic.objects.filter return django.db.models.query.QuerySet object and ofcourse it hasn't no method named get_by_tag that's why you get AttributeError.

One more point related to queryset is The result of refining a QuerySet is itself a QuerySet, so it’s possible to chain refinements together.

https://docs.djangoproject.com/en/3.0/topics/db/queries/#chaining-filters

In your case get_by_tag return QuerySet further you performed .filter() operation, which is fine.

The django official documentation link can be followed related to models Manager and query for more details. https://docs.djangoproject.com/en/3.0/topics/db/queries/

Furkan Siddiqui
  • 1,725
  • 12
  • 19
  • Do you mean it's not possible? – Hamidreza Jun 21 '20 at 13:38
  • It is interesting that everything what you explained is correct, the only deficiency is an answer if a solution exists or not. I up-voted you, because it was useful for my answer and you are new and probably OP down-voted, because it was not useful for him. – hynekcer Jun 22 '20 at 08:37