Using Django Managers for limiting queries by user
Say I have two types of users: brief and long.
When a brief user logs in, he should only see the most recent 10 Items. When a long user logs in, he should see the most recent 30 Items. Otherwise they're very much alike.
I believe I could use Django Managers to implement something like this:
class Item(models.Model):
name = models.CharField(max_length=200)
date = models.DateField()
class BriefItemManager(models.Manager):
def get_query_set(self):
return super(BriefItemManager, self).get_query_set().order_by('date')[:10]
class LongItemManager(models.Manager):
def get_query_set(self):
return super(LongItemManager, self).get_query_set().order_by('date')[:30]
I'm thinking of monkey patching Item, perhaps using Middleware, so that the rest of the code is oblivious to whether the user is a brief or long . Item's .objects manager would be set to be either BriefItemManager or LongItemManager by the time the main code kicks in.
if user_type(request.user) == 'brief':
Item.objects = BriefItemManager()
else:
Item.objects = LongItemManager()
Questions:
- Are Managers the right way to implement this?
- Is monkeypatching Item's .objects manager the right way to go? Is开发者_StackOverflow中文版 there a cleaner way?
You wouldn't want to monkeypatch that sucker, because once the Item
type gets created, it's state is then shared between threads, which includes the default manager. It's possible then that you retrieve 10 items, even though the user for the current request is of type long
, because you have thread contention on the model manager. (see ModelBase in the package django.db.models.base
)
In your case, you'll want to make it very obvious what is it that your doing. This is my best guess:
class ItemManager(models.Manager):
def latest_for_user_type(user_type):
limit = user_type=='brief' and 10 or user_type=='long' and 30
return self.get_query_set().order_by('date')[:limit]
class Item(models.Model)
# ...
objects = ItemManager()
# views.py
class LatestItemListView(ListView):
def get_query_set(self):
return Item.objects.latest_for_user_type(
user_type=user_type(self.request.user)
)
You might want to implement the user_type()
logic in ItemManager
... you might even want to refactor it to be latest_for_user(user=None, user_type=None)
. This is just the basic idea and there are other ways, such as proxy classes, that are ideal (the only way true way) for overriding model managers.
Managers are the right way to implement anything that operates on collections.
精彩评论