问题
I have some working code, but have recently learned about Related Managers and reverse lookups and would like to know how to apply them to this code:
The hacky method I would like to utilize a Related Manager/reverse lookup is get_by_type_for_user(self, user)
:
class BadgeAssertionQuerySet(models.query.QuerySet):
def get_user(self, user):
return self.filter(user = user)
def get_type(self, badge_type):
return self.filter(badge__badge_type = badge_type)
...
class BadgeAssertionManager(models.Manager):
def get_queryset(self):
return BadgeAssertionQuerySet(self.model, using=self._db)
...
def get_by_type_for_user(self, user):
types = BadgeType.objects.all()
qs = self.get_queryset().get_user(user)
by_type = [
{
'badge_type': t,
'list': qs.get_type(t)
} for t in types
]
return by_type
Where:
class BadgeAssertion(models.Model):
badge = models.ForeignKey(Badge)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
...
objects = BadgeAssertionManager()
and:
class Badge(models.Model):
name = models.CharField(max_length=50, unique=True)
badge_type = models.ForeignKey(BadgeType)
....
and:
class BadgeType(models.Model):
name = models.CharField(max_length=50, unique=True)
...
What I'm using this for is to output, for a specific user, the badges they have earned, organized by badge type:
from view:
context['badge_assertions_by_type'] = BadgeAssertion.objects.get_by_type_for_user(profile_user)
snippet from template (this is the user's profile page):
{% for badges in badge_assertions_by_type %}
{{badges.badge_type.name}}
{% for assertion in badges.list %}
{{assertion.badge.name}}
{{assertion.time_issued}}
{% endfor %}
{% endfor %}
回答1:
You can simplify your method like this:
def get_badges_by_type_for_user(self, user):
qs = self.get_queryset()
.select_related('badge', 'badge__badge_type')
.filter(user=user)
badges = defaultdict(list)
for badge_assertion in qs:
badges[badge_assertion.badge.badge_type].append(badge_assertion.badge)
return badges
You don't need to iterate over all the BadgeTypes available, but just group the assertions you get, in buckets of badge types. So, not filtration on badge_type
is required. Notice the use of defaultdict() here. You can try this out, and see if it works. Haven't tested it myself.
The result structure is different here now. It's:
{
badge_type1: [badge1, badge2, ...],
badge_type2: [badge3, badge4, ...]
}
instead of:
[
{'badge_type' : badge_type1, 'list': [badge1, badge2]},
{'badge_type' : badge_type2, 'list': [badge3, badge4]}
]
The first one makes more sense here.
来源:https://stackoverflow.com/questions/32420417/how-to-use-a-related-manager-and-reverse-lookups-to-clean-up-this-django-queryse