2009-12-11 26 views
9

Tôi có một chức năng mà trông như thế này:Lọc Uẩn trong Django ORM

def post_count(self): 
     return self.thread_set.aggregate(num_posts=Count('post'))['num_posts'] 

Tôi chỉ muốn đếm bài viết mà có tư cách của họ được đánh dấu là 'hoạt động'. Có cách nào dễ dàng để thêm bộ lọc trước hàm Count không?

Mẫu Định nghĩa:

class Category(models.Model): 
    name = models.CharField(max_length=100) 
    slug = models.SlugField(max_length=100, blank=True, primary_key=True) 
    ordering = models.IntegerField(max_length=3, default=0) 

    @property 
    def thread_count(self): 
     return self.thread_set.all().count() 

    @property 
    def post_count(self): 
     return self.thread_set.aggregate(num_posts=Count('post'))['num_posts'] 

class Thread(models.Model): 
    user = models.ForeignKey(User) 
    category = models.ForeignKey(Category) 
    title = models.CharField(max_length=100) 
    slug = models.SlugField(max_length=100) 
    content = models.TextField() 
    created = models.DateTimeField(auto_now_add=True) 
    latest_activity = models.DateTimeField(auto_now_add=True) 

class Post(models.Model): 
    thread = models.ForeignKey(Thread) 
    parent = models.ForeignKey('Post', null=True, blank=True) 
    display_name = models.CharField(max_length=100) 
    email = models.EmailField(db_index=True) 
    ip_address = models.IPAddressField(null=True, blank=True) 
    content = models.TextField() 
    status = models.CharField(choices=STATUS_CHOICES, max_length=25, db_index=True, default='approved') 
    created = models.DateTimeField() 
+2

Mô hình của bạn trông như thế nào? – Mayuresh

Trả lời

9

OK, bây giờ câu hỏi bao gồm các định nghĩa mô hình, tôi gửi cho bạn rằng điều này sẽ hoạt động , trừ khi các phiên bản của Django không hỗ trợ một số tính năng tôi sử dụng ở đây (trong trường hợp này, xin vui lòng cho tôi biết!):

Post.objects.filter(thread__in=thread_set, status='active').aggregate(num_posts=Count('id')) 

Django phép __in bộ lọc để có một QuerySet để quyết định những gì các khoản IN nên nhìn như trong SQL, vì vậy nếu bạn vượt qua thread__in=thread_set, Django sẽ lọc các bài đăng để chỉ những người có trường thread trỏ đến một trong các số id s trong số các chủ đề trong số thread_set của bạn để lại cho cuộc gọi aggregate để xem.

Điều này sẽ lọc các bài đăng với chỉ một truy vấn db với thứ gì đó như WHERE thread_id IN ... bên trong, thay vì chỉ với một truy vấn cho mỗi chuỗi, điều này thực sự sẽ rất kinh khủng. Nếu bất cứ điều gì khác xảy ra, đây sẽ là một lỗi trong Django ...

Kết quả sẽ được nhiều nhất là hai truy vấn để thiết lập một Category 's postcount - một để có được thread_set và một số khác thực sự để đếm số bài viết . Cách khác là để có một chủ đề/bài đăng tham gia được lọc dựa trên trường 's category và trường' status ', mà tôi không nhất thiết phải mong đợi nhanh hơn nhiều. (Tôi nói 'nhiều nhất', vì tôi đoán chúng có thể được hợp nhất tự động ...Mặc dù tôi không nghĩ rằng điều này sẽ xảy ra với Django hiện tại. Không thể kiểm tra máy ATM, xin lỗi)

EDIT:.Django's QuerySet API reference nói này trên __in lọc:


TRÊN

Trong một danh sách nhất định.

Ví dụ:

Entry.objects.filter(id__in=[1, 3, 4]) 

SQL tương đương:

SELECT ... WHERE id IN (1, 3, 4); 

Bạn cũng có thể sử dụng một queryset để tự động đánh giá danh sách các giá trị thay vì cung cấp một danh sách các giá trị văn chương:

inner_qs = Blog.objects.filter(name__contains='Cheddar') 
entries = Entry.objects.filter(blog__in=inner_qs) 

Bộ truy vấn này sẽ được đánh giá là tuyên bố chọn lựa:

SELECT ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Cheddar%') 

Đoạn mã trên cũng có thể được viết như sau:

inner_q = Blog.objects.filter(name__contains='Cheddar').values('pk').query 
entries = Entry.objects.filter(blog__in=inner_q) 

Thay đổi trong Django 1.1: Trong Django 1.0, chỉ có mảnh thứ hai của mã là hợp lệ.

Biểu mẫu thứ hai này ít có thể đọc được và không tự nhiên để viết, vì nó truy cập thuộc tính truy vấn nội bộ và yêu cầu một ValuesQuerySet. Nếu mã của bạn không yêu cầu khả năng tương thích với Django 1.0, hãy sử dụng biểu mẫu đầu tiên, chuyển trực tiếp trong một queryset.


Vì vậy, tôi đoán Django có khả năng truyền một truy vấn đơn đến db trong trường hợp trong ấn bản này. Nếu trình phân tích truy vấn của db thực hiện một công việc tốt, hiệu ứng có thể rất gần như tối ưu. :-)

+0

Đây là giải pháp tốt nhất cho đến nay. Nó cũng có thể là tốt nhất bạn có thể làm với orm django. Tôi chỉ hy vọng có một cách dễ dàng để thêm bộ lọc vào self.thread_set.aggregate ban đầu (num_posts = Count ('post')) ['num_posts']. –

+0

Vâng, cuối cùng tôi đã tìm kiếm các tài liệu trên bộ lọc __in với QuerySets bên trong ... Có vẻ như chúng sẽ hoạt động khá tốt, phải không? :-) Đáng tiếc, tôi không thể tìm cách để cắm bộ lọc thẳng vào mã của bạn, vì tôi có thể thấy nó có thể phù hợp hơn với cách suy nghĩ cụ thể về mã được nói ... (Mặc dù, mặt khác, bạn đang đếm bài viết, vì vậy qua bài viết có vẻ ok với tôi.) Hy vọng nó hoạt động cho bạn hiệu suất khôn ngoan, ở mức nào. Tất cả tốt nhất! –

0

Yes. Cứ làm đi. Điều này sẽ làm việc như mong đợi:

self.thread_set.filter(active_status=1).aggregate(num_posts=Count('post'))['num_posts'] 

Bất kỳ truy vấn ban đầu trả về một QuerySet, vì vậy any available methods that return QuerySets có thể có thể được khá nhiều vô hạn định xích lại với nhau cho tiêu chí phức tạp phù hợp. Kể từ aggregate()does not return a QuerySet, bạn muốn đảm bảo rằng nó là cuối cùng trong chuỗi.

+0

Tôi khá chắc chắn rằng sẽ lọc các bộ chủ đề và không phải là bài viết trong mỗi chủ đề. –

+0

Rất tiếc, bạn đã đúng. Bạn sẽ cần có danh sách đầy đủ các bài đăng cho một chuỗi, sau đó lọc ra các bài đăng đang hoạt động, sau đó tổng hợp các bài đăng đó. Nhưng những thứ khác về việc 'aggregate()' cuối cùng vẫn đứng vững. Tôi sẽ poke xung quanh và sau đó chỉnh sửa câu trả lời của tôi. – jathanism

+0

Tôi chưa có một ví dụ mã, nhưng có vẻ như những gì bạn có thể tìm kiếm là: http://docs.djangoproject.com/en/dev/topics/db/aggregation/#joins-and-aggregates (Tham gia và Tổng hợp) – jathanism

0

Tôi đã xem xét một thứ tương tự và chưa tìm thấy giải pháp tuyệt vời nào. Tôi đang sử dụng một cái gì đó như thế này:

def post_count(self): 
     return len(Post.objects.filter(someModel = self).filter(active_status = 1)) 

Đó không phải là tuyệt vời, nhưng tôi không nghĩ rằng Django cho phép bạn lọc dựa trên các quy tụ mô hình phổ thông và chú thích. Tôi sẽ kiểm tra xem liệu có ai đưa ra giải pháp tốt hơn không.

+1

Tại sao không Post.objects.filter (someModel = self, active_status = 1) .count()? –

+0

Ngoài ra tôi đang sử dụng tổng hợp ở đây vì tôi muốn nhiều hơn chỉ số lượng bài viết trên một chủ đề cụ thể (được đại diện bởi someModel trong ví dụ của bạn), tôi cần số bài cho tất cả các chủ đề trong một thể loại, đó là lý do tại sao tôi đang sử dụng thread_set. –

+0

Vâng ví dụ tôi cho là khá cẩu thả. Mã của bạn chắc chắn là sạch hơn. Điểm mà tôi đã cố gắng thực hiện mặc dù là tôi khá chắc chắn rằng bạn cần phải làm việc thông qua các mô hình bài viết mặc dù, và không phải là mô hình chủ đề. – Zach

0

Bạn có thể muốn nhìn vào văn bản một đối tượng tùy chỉnh quản lý:

http://docs.djangoproject.com/en/1.1/topics/db/managers/

tôi đã không sử dụng aggregate(), nhưng điều đó có thể cho phép bạn viết một trình quản lý tùy chỉnh để cung cấp một lọc active_thread_set và sau đó làm self.active_thread_set.aggregate(...) . Nếu không, nó sẽ cho phép bạn thực hiện SQL tùy chỉnh và thêm một thuộc tính num_posts vào các đối tượng Thread (xem ví dụ PollManager.with_counts().)

0

Bạn có thể thay đổi mọi thứ một chút được không?

Như minh họa bên dưới, bạn có thể thêm thuộc tính post_count vào lớp Thread, tính số lượng bài đăng đang hoạt động trong một Chủ đề.

Bài đăng này sau đó có thể được sử dụng để tính các bài đăng đang hoạt động trong một danh mục bằng cách thêm tất cả các bài đăng đang hoạt động trong tất cả chuỗi trong một danh mục.

class Category(models.Model): 
    name = models.CharField(max_length=100) 
    slug = models.SlugField(max_length=100, blank=True, primary_key=True) 
    ordering = models.IntegerField(max_length=3, default=0) 

    @property 
    def thread_count(self): 
     return self.thread_set.all().count() 

    @property 
    def post_count(self): # <-- Changed 
     return reduce(lambda x,y: x + y, [x.post_count for x in self.thread_set.all()]) 

class Thread(models.Model): 
    user = models.ForeignKey(User) 
    category = models.ForeignKey(Category) 
    title = models.CharField(max_length=100) 
    slug = models.SlugField(max_length=100) 
    content = models.TextField() 
    created = models.DateTimeField(auto_now_add=True) 
    latest_activity = models.DateTimeField(auto_now_add=True) 

    @property 
    def post_count(self): # <---- Newly added 
     return self.post_set.filter(status = 'ACTIVE').count() 

class Post(models.Model): 
    thread = models.ForeignKey(Thread) 
    parent = models.ForeignKey('Post', null=True, blank=True) 
    display_name = models.CharField(max_length=100) 
    email = models.EmailField(db_index=True) 
    ip_address = models.IPAddressField(null=True, blank=True) 
    content = models.TextField() 
    status = models.CharField(choices=STATUS_CHOICES, max_length=25, db_index=True, default='approved') 
    created = models.DateTimeField() 
Các vấn đề liên quan