Django

2009-09-08 12 views
14

Django bir gıda günlük veritabanı inşa ediyorum ilgili bir modelde açıklamalı Kont() göre sıralamak ve ben bir sorgu ile ilgili sorun var nasıl.Django

I (diğer şeylerin yanı sıra) dahil etmek Tüketim modeli üzerinden bir M2M-alanı "tüketici" aracılığıyla kullanıcı modeli bağlı bir Gıda modeli Modellerimi kurdum. Gıda modeli, yiyecek tabaklarını tanımlar ve Tüketim modeli, kullanıcının Gıda tüketimini (tarih, miktar, vb.) Açıklar.

class Food(models.Model): 
    food_name = models.CharField(max_length=30) 
    consumer = models.ManyToManyField("User", through=Consumption) 

class Consumption(models.Model): 
    food = models.ForeignKey("Food") 
    user = models.ForeignKey("User") 

Ben Gıda nesne o kullanıcı (kullanıcı gıda tükettiği sayısı) için tüketimle tabloda görünme sayısına göre sipariş edilen tüm gıda nesnelerini döndüren bir sorgu oluşturmak istiyorum.

ben hattında şey çalışıyorum:

Food.objects.all().annotate(consumption_times = Count(consumer)).order_by('consumption_times')` 

Ama tabii bu irade kullanıcıyla ilişkili değil, sadece olanları, Gıda nesnesine ilgili tüm Tüketim nesneleri sayar. Modellerimi değiştirmem mi gerekiyor yoksa sadece sorgularda belirgin bir şey eksik miyim?

Bu oldukça zaman açısından kritik bir işlemdir (diğer şeylerin yanı sıra, Frontend'deki Otomatik Tamamlama alanını doldurmak için kullanılır) ve Yemek tablosunda birkaç bin giriş vardır, bu nedenle veritabanında sıralama yapmayı tercih ederim sonunda, kaba kuvvet yöntemini yapıyor ve yapmaya sonuçları üzerinde yineleme yerine:

Consumption.objects.filter(food=food, user=user).count() 

ve sonra bunları sıralamak piton sıralama kullanarak. Bu yöntemin kullanıcı tabanı arttıkça çok iyi bir şekilde ölçekleneceğini düşünmüyorum ve veritabanını en başından itibaren gelecekteki kanıt olarak tasarlamak istiyorum.

Herhangi bir fikrin var mı?

+0

Olası yinelenen [ForeignKey alanın sayımı ile Order?] (Http://stackoverflow.com/questions/2501149/order-by-count-of-a-foreignkey-field) –

cevap

21

Belki de böyle bir şey?

Food.objects.filter(consumer__user=user)\ 
      .annotate(consumption_times=Count('consumer'))\ 
      .order_by('consumption_times') 
+0

Ama bu olur Bir süredir tüketilmiş olan Gıda nesnelerini geri getirir, değil mi? Tüm Gıda nesnelerini iade etmek istiyorum, ancak en sık tüketilen ilk sırada. Kullanıcıya göre filtrelerim, henüz tüketilmemiş Gıdaları alamayacağım. Bir fikir, belki de en az bir kez tüketilen tüm Gıda maddelerini ve daha sonra da Food.objects.exclude (consumer__user = kullanıcı) satırları boyunca bir şeyler almayı önerdiğiniz gibi iki sorgu yapmak olabilir ve listeyi . Işe yarar mı? –

+0

Evet, nasıl yapacağımı 2 sorgu olurdu. – SmileyChris

19

Çok benzer bir sorun yaşıyorum. ,

SELECT food.*, COUNT(IF(consumption.user_id=123,TRUE,NULL)) AS consumption_times 
     FROM food LEFT JOIN consumption ON (food.id=consumption.food_id) 
     ORDER BY consumption_times; 

Ne diliyorum, toplama işlevlerini ve F ifadesini karıştırın bir toplama işlevi olmadan F ifadeleri açıklama verebilecek olan operasyonlar/fonksiyonları, daha zengin bir için vardır: Temelde, istediğiniz SQL sorgusu olduğunu biliyoruz F ifadeleri ve temel olarak otomatik bir F ifade notu olan sanal alanlara sahipler. Yapabileceğin Böylece:

Food.objects.annotate(consumption_times=Count(If(F('consumer')==user,True,None)))\ 
      .order_by('consumtion_times') 

Ayrıca sadece daha kolay güzel olurdu kendi karmaşık toplama işlevlerini eklemek mümkün edememek, ama bu arada, burada yapmak bir toplama işlevi ekleyen bir beygir.

from django.db.models import aggregates,sql 
class CountIf(sql.aggregates.Count): 
    sql_template = '%(function)s(IF(%(field)s=%(equals)s,TRUE,NULL))' 
sql.aggregates.CountIf = CountIf 

consumption_times = aggregates.Count('consumer',equals=user.id) 
consumption_times.name = 'CountIf' 
rows = Food.objects.annotate(consumption_times=consumption_times)\ 
        .order_by('consumption_times') 
+0

Bu harika! Teşekkürler günümü kurtardın!Biraz daha güzel görünmesini sağlamaya çalışacağım, ama kesinlikle bunu django'nun trac'sine yazmalısın. –