2009-09-15 16 views
2

J'ai une classe Marketorders qui contient des informations sur les ordres uniques du marché et qui sont rassemblées en instantanés du marché (représentés par la classe Snapshot). Chaque commande peut apparaître dans plusieurs instantanés, la dernière ligne étant bien sûr la plus pertinente.Django ORM: Obtenir des rangées en fonction de la valeur maximale d'une colonne

class Marketorders(models.Model): 
    id = models.AutoField(primary_key=True) 
    snapid = models.IntegerField() 
    orderid = models.IntegerField() 
    reportedtime = models.DateTimeField(null=True, blank=True) 
    ... 


class Snapshot(models.Model): 
    id = models.IntegerField(primary_key=True) 
    ... 

Ce que je fais est d'obtenir toutes les commandes à travers plusieurs clichés pour le traitement, mais je veux inclure seulement la ligne la plus récente pour chaque commande. Dans SQL Je voudrais simplement faire:

SELECT m1.* FROM marketorders m1 WHERE reportedtime = (SELECT max(reportedtime) 
FROM marketorders m2 WHERE m2.orderid=m1.orderid); 

ou mieux encore avec une jointure:

SELECT m1.* FROM marketorders m1 LEFT JOIN marketorders m2 ON 
m1.orderid=m2.orderid AND m1.reportedtime < m2.reportedtime 
WHERE m2.orderid IS NULL; 

Cependant, je ne peux pas comprendre comment faire cela avec Django ORM. Y at-il un moyen d'accomplir cela sans SQL brut?

EDIT: Juste pour clarifier le problème. Disons que nous avons les marketorders suivants (en laissant tout ce que peu d'importance et en utilisant seulement orderid, reportedtime):

1, 09:00:00 
1, 10:00:00 
1, 12:00:00 
2, 09:00:00 
2, 10:00:00 

Comment puis-je ce qui suit avec le ORM mis en?

1, 12:00:00 
2, 10:00:00 
+0

Je ne suis pas un expert avec Django, mais il y a peu d'ORM qui peuvent gérer une sous-requête dépendante ou une auto-jointure nulle. On s'attend à ce que parfois vous deviez passer à SQL pour mettre ensemble une requête efficace. – bobince

+0

J'espère que ce n'est pas le cas avec Django. =) J'ai essayé de faire ceci avec les recherches supplémentaires, mais je ne peux pas comprendre comment référencer le orderid original de l'appel supplémentaire (ainsi oui, peut-être que les sous-requêtes dépendantes ne sont pas possibles avec des recherches supplémentaires). Je suis un peu nouveau à Django, donc il se peut aussi que je ne sache pas comment écrire de telles références. –

Répondre

2

Si je comprends bien vous avez besoin d'une liste d'objets Marketorder qui contient chaque Marketorder avec le plus reportedtime par orderid

Quelque chose comme cela devrait fonctionner (disclaimer: n'a pas testé directement):

m_orders = Marketorders.objects.filter(id__in=(
    Marketorders.objects 
     .values('orderid') 
     .annotate(Max('reportedtime')) 
     .values_list('id', flat=True) 
)) 

Pour vérifier la documentation:

http://docs.djangoproject.com/en/dev/topics/db/aggregation/

Edit: Cela devrait obtenir un Marketorder avec le plus reportedtime pour une orderid spécifique

order = (
    Marketorders.objects 
     .filter(orderid=the_orderid) 
     .filter(reportedtime=(
      Marketorders.objects 
       .filter(orderid=the_orderid) 
       .aggregate(Max('reportedtime')) 
       ['reportedtime__max'] 
     )) 
) 
+0

J'ai mal compris. Mettra à jour. –

+0

En fait, votre première hypothèse était correcte. Je suis à la recherche d'un moyen d'obtenir un ensemble de commandes qui ne contient que la dernière ligne pour chaque ordre (c.-à-d. Filtrer les anciennes "instances" des ordres). Votre première solution est assez proche de ce que j'essaie de faire (je ne sais pas pourquoi je n'ai pas pensé à faire des sous-requêtes comme ça), mais cela ne semble retourner le premier ordre du sous-ensemble, pas le plus récent un. –

0

Avez-vous une bonne raison pour laquelle vous n'utilisez pas ForeignKey ou (dans votre cas mieux) ManyToManyField. Ces champs représentent la structure relationnelle de nos modèles.

En outre, il n'est pas nécessaire de déclarer un identifiant de champ de type pk. si aucun pk n'est défini, django ajoute id.

Le code ci-dessous permettent des requêtes ORM-comme ceci:

m1 = Marketorder() 
    m1.save() # important: primary key will be added in db 
    s1 = Snapshot() 
    s2 = Snapshot() 
    s1.save() 
    s2.save() 
    m1.snapshots.add(s1) 
    m1.snapshots.add(s2) 
    m1.snapshots.all() 
    m1.snapshots.order_by("id") # snapshots in relations with m1 
           # ordered by id, that is added automatically 
    s1.marketorder_set.all() # reverse 

donc pour votre requête:

snapshot = Snapshot.objects.order_by('-id')[0] # order desc, pick first 
marketorders = snapshot.marketorder_set.all() # all marketorders in snapshot 

ou compact:

marketorders = Snapshot.objects.order_by('-id')[0].marketorder_set.all() 

Mode ls.py:

class Snapshot(models.Model): 
    name = models.CharField(max_length=100) 

class Marketorder(models.Model): 
    snapshots = models.ManyToManyField(Snapshot) 
    reportedtime = models.DateTimeField(auto_now= True) 

Par convention toutes les classes nom du modèle sont au singulier. Django rend le pluriel à différents endroits automatiquement.

more on queries (filtering, sorting, complex lookups). à lire absolument.

+0

Quant aux touches et conventions, c'est vrai. Ma base de données est une ancienne et le modèle a été créé automatiquement à partir de celui-ci, donc il ne suit pas toutes les conventions de Django. J'ai ajouté les bonnes références au fur et à mesure, mais je n'ai pas vraiment pris la peine de changer de nom. –