Django: Order a model by a many-to-many field

后端 未结 3 1655
鱼传尺愫
鱼传尺愫 2021-02-07 21:19

I am writing a Django application that has a model for People, and I have hit a snag. I am assigning Role objects to people using a Many-To-Many relationship - where Roles have

相关标签:
3条回答
  • 2021-02-07 21:59

    Something like this in SQL:

    select p.*, max (r.Weight) as HeaviestWeight
    from persons p
    inner join RolePersons rp on p.id = rp.PersonID
    innerjoin Roles r on rp.RoleID = r.id
    group by p.*
    order by HeaviestWeight desc
    

    Note: group by p.* may be disallowed by your dialect of SQL. If so, just list all the columns in table p that you intend to use in the select clause.

    Note: if you just group by p.ID, you won't be able to call for the other columns in p in your select clause.

    I don't know how this interacts with Django.

    0 讨论(0)
  • 2021-02-07 22:03

    Here's a way to do it without an annotation:

    class Role(models.Model):
        pass
    
    class PersonRole(models.Model):
        weight = models.IntegerField()
        person = models.ForeignKey('Person')
        role = models.ForeignKey(Role)
    
        class Meta:
            # if you have an inline configured in the admin, this will 
            # make the roles order properly 
            ordering = ['weight'] 
    
    class Person(models.Model):
        roles = models.ManyToManyField('Role', through='PersonRole')
    
        def ordered_roles(self):
            "Return a properly ordered set of roles"
            return self.roles.all().order_by('personrole__weight')
    

    This lets you say something like:

    >>> person = Person.objects.get(id=1)
    >>> roles = person.ordered_roles()
    
    0 讨论(0)
  • 2021-02-07 22:09

    Django 1.1 (currently beta) adds aggregation support. Your query can be done with something like:

    from django.db.models import Max
    People.objects.annotate(max_weight=Max('roles__weight')).order_by('-max_weight')
    

    This sorts people by their heaviest roles, without returning duplicates.

    The generated query is:

    SELECT people.id, people.name, MAX(role.weight) AS max_weight
    FROM people LEFT OUTER JOIN people_roles ON (people.id = people_roles.people_id)
                LEFT OUTER JOIN role ON (people_roles.role_id = role.id)
    GROUP BY people.id, people.name
    ORDER BY max_weight DESC
    
    0 讨论(0)
提交回复
热议问题