4👍
You need to add methods to the QuerySet
which you eventually end up with. So you need to create and use a QuerySet
subclass which has the methods you define wherever you want this functionality.
I found this tutorial which explains how to do it and reasons why you might want to:
46👍
As of django 1.7, the ability to use a query set as a manager was added:
class PersonQuerySet(models.QuerySet):
def authors(self):
return self.filter(role='A')
def editors(self):
return self.filter(role='E')
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
role = models.CharField(max_length=1, choices=(('A', _('Author')),
('E', _('Editor'))))
people = PersonQuerySet.as_manager()
Resulting the following:
Person.people.authors(last_name='Dahl')
In addition, the ability to add custom lookups was also added.
- [Django]-How does one make logging color in Django/Google App Engine?
- [Django]-How to return custom JSON in Django REST Framework
- [Django]-Django and VirtualEnv Development/Deployment Best Practices
16👍
This is a complete solution that is known to work in Django 1.3, courtesy of Zach Smith and Ben.
class Entry(models.Model):
objects = EntryManager() # don't forget this
is_public = models.BooleanField()
owner = models.ForeignKey(User)
class EntryManager(models.Manager):
'''Use this class to define methods just on Entry.objects.'''
def get_query_set(self):
return EntryQuerySet(self.model)
def __getattr__(self, name, *args):
if name.startswith("_"):
raise AttributeError
return getattr(self.get_query_set(), name, *args)
def get_stats(self):
'''A sample custom Manager method.'''
return { 'public_count': self.get_query_set().public().count() }
class EntryQuerySet(models.query.QuerySet):
'''Use this class to define methods on queryset itself.'''
def public(self):
return self.filter(is_public=True)
def by(self, owner):
return self.filter(owner=owner)
stats = Entry.objects.get_stats()
my_entries = Entry.objects.by(request.user).public()
Note: the get_query_set()
method is now deprecated in Django 1.6; get_queryset()
should be used instead in this case.
- [Django]-Django: Model Form "object has no attribute 'cleaned_data'"
- [Django]-Django Debug Toolbar: understanding the time panel
- [Django]-Django: Multiple forms possible when using FormView?
4👍
You could modify the get_query_set()
method to return a custom QuerySet, adding the methods you require. In your case, you would use:
class TransactionManager(models.Manager):
def get_query_set(self):
return TransactionQuerySet(self.model)
class TransactionQuerySet(models.query.QuerySet):
def reasonably_complex_filter(self):
return self.filter(...)
I’ve seen examples subclassing the TransactionQuerySet into the Transaction
model, or in the related Manager
, but that is entirely up to you.
edit: I seem to have overlooked the fact that objects
first references to the TransactionManager
and therefore Transaction.objects.reasonably_complex_filter()
isn’t possible in my implementation. This can be fixed in three ways:
- Implement the
reasonably_complex_filter
in both the Manager and the QuerySet; - Use
Transaction.objects.all().reasonably_complex_filter()
when that’s the only filter required; - Refer to Marcus Whybrow’s answer for a solution that will implement the method in both the
QuerySet
and theManager
without code duplication.
It depends on the application which option is the most desireable, although I would strongly recommend against code duplication (although you could use a global method to overcome this). Though, the last option could be too costly in terms of overhead if you only require this kind of practice once, or if you only intend to use the complex filter in combination with another filter.
- [Django]-Do properties work on Django model fields?
- [Django]-Django post_save preventing recursion without overriding model save()
- [Django]-How to log all sql queries in Django?
1👍
If you need both custom Manager methods and custom QuerySet methods, you can use from_queryset
.
class BaseManager(models.Manager):
def manager_only_method(self):
return
class CustomQuerySet(models.QuerySet):
def manager_and_queryset_method(self):
return
class MyModel(models.Model):
objects = BaseManager.from_queryset(CustomQuerySet)()
https://docs.djangoproject.com/en/2.1/topics/db/managers/#from-queryset
- [Django]-Simple Subquery with OuterRef
- [Django]-How to use "AND" in a Django filter?
- [Django]-How to get POST request values in Django?
1👍
Simplest version for Django 2.0+
class TransactionQuerySet(models.QuerySet):
def reasonably_complex_filter(self):
return self.filter(...)
class Transaction(models.Model):
...
objects = models.Manager.from_queryset(TransactionQuerySet)()
- [Django]-How do I get the values of all selected checkboxes in a Django request.POST?
- [Django]-Django default=timezone.now + delta
- [Django]-Django – Render the <label> of a single form field
0👍
I actually ended up going with another method. It turned out I only needed to chain filter
calls onto the end of my custom method, so I amended my method to take arbitrary keyword arguments, and pass them through to a filter()
call on the end of my reasonably complex query:
class TransactionManager(models.Manager):
def reasonably_complex_filter(self, **kwargs):
return self.get_query_set().filter(...).filter(**kwargs)
Seems to work fine for my purposes, and is a bit simpler than subclassing QuerySet
.
- [Django]-In Django, getting a "Error: Unable to serialize database" when trying to dump data?
- [Django]-Django middleware difference between process_request and process_view
- [Django]-Redis Python – how to delete all keys according to a specific pattern In python, without python iterating