2009-06-01 33 views
12

Tôi đang viết một ứng dụng Django có mô hình dành cho Mọi người và tôi đã bị đánh lừa. Tôi đang gán các đối tượng Role cho những người sử dụng mối quan hệ Nhiều-Nhiều, trong đó Roles có tên và trọng lượng. Tôi muốn đặt hàng danh sách người của mình theo trọng lượng nặng nhất của họ. Nếu tôi làm People.objects.order_by ('- roles__weight'), thì tôi sẽ nhận được bản sao khi mọi người có nhiều vai trò được gán cho họ.Django: Đặt hàng một mô hình theo trường nhiều người đến nhiều người

Ý tưởng ban đầu của tôi là thêm trường không chuẩn hóa được gọi là nặng nhất vai trò - và sắp xếp theo đó. Điều này sau đó có thể được cập nhật mỗi lần một vai trò mới được thêm hoặc xóa khỏi người dùng. Tuy nhiên, nó chỉ ra rằng không có cách nào để thực hiện một hành động tùy chỉnh mỗi khi một ManyToManyField được cập nhật trong Django (yet, anyway). Vì vậy, tôi nghĩ rằng tôi có thể sau đó đi hoàn toàn overboard và viết một lĩnh vực tùy chỉnh, mô tả và quản lý để xử lý này - nhưng điều đó có vẻ vô cùng khó khăn khi ManyRelatedManager được tạo động cho một ManyToManyField.

Tôi đã cố gắng tìm ra một số SQL thông minh có thể làm điều này cho tôi - tôi chắc chắn rằng có thể có một truy vấn phụ (hoặc một vài), nhưng tôi lo lắng về nó sẽ không tương thích cơ sở dữ liệu backends Django hỗ trợ.

Có ai đã làm điều này trước đây - hoặc có bất kỳ ý tưởng nào để đạt được điều đó không?

Trả lời

11

Django 1.1 (hiện đang thử nghiệm beta) thêm hỗ trợ aggregation. Truy vấn của bạn có thể được thực hiện với một cái gì đó như:

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

Điều này sắp xếp mọi người theo vai trò nặng nhất của họ mà không trả lại bản sao.

Truy vấn được tạo ra là:

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

Chỉ cần những gì tôi đã tìm kiếm. Tôi không chạy Django 1.1, nhưng tôi có thể sử dụng SQL đó trong trình quản lý tùy chỉnh của mình. Cảm ơn :) –

1

Something như thế này trong 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 

Lưu ý:. Nhóm bởi p * có thể được khấu trừ bằng phương ngữ của bạn của SQL. Nếu vậy, chỉ cần liệt kê tất cả các cột trong bảng p mà bạn định sử dụng trong mệnh đề select.

Lưu ý: nếu bạn chỉ nhóm theo p.ID, bạn sẽ không thể gọi cho các cột khác trong p trong mệnh đề chọn của bạn.

Tôi không biết điều này tương tác với Django như thế nào.

6

Đây là một cách để làm điều đó mà không có một chú thích:

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') 

này cho phép bạn nói điều gì đó như:

>>> person = Person.objects.get(id=1) 
>>> roles = person.ordered_roles() 
Các vấn đề liên quan