How to use a related manager and reverse lookups to clean up this Django queryset

一世执手 提交于 2020-01-16 20:19:12

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!