2010-09-16 11 views
2

J'ai installé comme si (changé pour la simplicité)Handling modèles associés à Django pour une utilisation dans Django-piston

class Author(models.Model) 
    name = models.CharField(max_length=100) 
    ... 

class Document(models.Model): 
    title = models.CharField(max_length=200) 
    content - models.TextField() 
    author = models.ForeignKey("Author", related_name="documents") 
    date_published = models.DateTimeField() 
    categories = models.ManyToManyField("Category") 

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

Je tire dans les dossiers de l'auteur, mais je veux seulement tirer dans les dossiers de documents connexes pour chaque auteur qui correspond à des critères spécifiques - disons, date_published et category.

Je sais que le moyen facile de le faire serait de tirer dans les dossiers comme une liste de dictionnaires en utilisant Author.objects.values(), une boucle à travers chaque enregistrement et le fonctionnement:

author['documents']=Document.objects.filter(categories__in=[category_list], date_published__year=year)` 

Cependant, cela est généré pour django- piston, et il semble être le plus heureux (surtout si vous définissez vos propres champs!) si vous renvoyez un objet QuerySet.

Une partie de ceci peut être parce que j'ai fait des changements au code de base django-piston. Fondamentalement, le current version of the code écrase ici la valeur fields. J'ai changé ce code afin que je puisse changer la valeur fields pour un gestionnaire basé sur la demande (ainsi je pourrais fournir plus de détails si la demande était pour une ressource spécifique).

donc je suppose que ma question est triple:

  1. Y at-il un moyen de filtrer ou d'une certaine façon de limiter les sous-enregistrements d'un enregistrement (ie filtre documents pour chaque author.documents)
  2. Dans le cas contraire, ce qui est une façon fonctionnelle de faire cela qui fonctionne également avec django-piston?
  3. Existe-t-il une façon plus simple et plus efficace de faire ce que j'essaie de faire (afficher tous les auteurs sans leurs documents si un identifiant n'est pas donné, mais afficher les sous-enregistrements si filtrage vers un seul auteur)?

Précision

D'accord, juste pour être clair, voici le pseudocode que je veux:

def perhaps_impossible_view(request, categories=None, year=None): 
    authors = Author.objects.all() 
    authors.something_magical_happens_to_documents(category__in=[categories], date_published__year=year) 
    return render_to_response('blar.html', locals(), RequestContext(request)) 

Alors que si je étaient à mettre dans un modèle, cela fonctionnerait sans aucune modification:

{% for a in authors %} 
    {% for d in authors.documents.all %} 
     {{ d.title }} is almost certainly in one of these categories: {{ categories }} and was absolutely published in {{ year }}. If not, .something_magical_happens_to_documents didn't work. 
    {% endfor %} 
{% endfor %} 

something_magical_happens_to_documents doit courir et réellement changer le contenu de documents pour chaque enregistrement d'auteur. Il semble que cela devrait être possible, mais peut-être pas?

Répondre

0

Édité ... ou mieux ... remplacé! :)

C'est vrai que les auteurs sans appariement de documents ne seront pas dans le jeu de requête, donc vous devrez les rajouter après (je ne pourrais pas trouver un meilleur moyen mais quelqu'un sait comment ne pas les supprimer sql).

Vous obtenez le nombre complet de documents des auteurs parce que vous n'utilisez pas le queryset pour obtenir les comptes de document:

qryset = Author.objects.all() 
qryset = qryset.filter(documents__categories__name__in = category_list).distinct() 
qryset = qryset.filter(documents__date_published__year=year) 

print(qryset) donne [<Author: Author object>] (si seulement 1 auteur correspondait toutes les catégories) et qryset[0].documents.count() retournera seul le nombre de documents correspondait (pas tous les documents de l'auteur - 2 dans le cas que j'ai testé et l'auteur avait 4 mais seulement 2 correspondant à toutes les conditions).

Si vous utilisez dict (.values ​​()) au lieu de QuerySets dans les étapes ci-dessus, vous ne pouvez pas le faire (je pense) parce que dict ne sera pas un champ de documents afin:

qryset_dict = Author.objects.values() 
qryset_dict = qryset_dict.filter(documents__categories__name__in = category_list).distinct().values() 
qryset_dict = qryset_dict.filter(documents__date_published__year=year).values() 

lorsque vous émettez qryset_dict[0].documents.count() vous recevez une erreur:

AttributeError: 'dict' object has no attribute 'documents' 


maintenant ajouter les auteurs filtrée que vous pouvez faire:

res = [] 
for a in Author.objects.all(): 
    if a in qryset: 
     res.append([a,a.documents.count()]) 
    else: 
     res.append([a,0]) 

et res sera une liste avec <authors> dans la 1ère colonne et compte des documents correspondants dans la 2ème colonne.

Je sais que cela est loin d'être parfait ... mais si vous êtes intéressé seulement dans le count() de faire correspondre les documents par auteur, je pense que vous pourriez trouver une meilleure façon en utilisant django aggregation and annotation ou peut-être le faire avec l'aide d'un sql cru left join de auteurs aux documents.




EDIT après la clarification à la question:

def possible_view(request, categories=None, year=None): 
    # you will pass these as parmeters of course 
    category_list = ['c2', 'c3'] 
    year = 2010 

    qryset = Document.objects.filter(categories__name__in = category_list).distinct() 
    qryset = qryset.filter(date_published__year=year) 
    authors = Author.objects.all() 
    return render_to_response('blar.html', { 'result': qryset, 'authors': authors, 'categories': category_list, 'year': year }, RequestContext(request)) 

Modèle blar.html:

{% for a in authors %} 
    <b>{{a.name}}</b><br /> 
    {% for d in result %} 
     {% if d.author.name == a.name %} 
      {{ d.title }} is almost certainly in one of these categories: {{ categories }} and was absolutely published in {{ year }}. If not, .something_magical_happens_to_documents didn't work.<br /> 
     {% endif %} 
    {% endfor %}<br /> 
{% endfor %} 

Cela vous donnera quelque chose de pas très joli mais avec tous les auteurs et au-dessous chacun, la liste de leurs documents qui tombent dans l'un des category_list (OU condition, pour ET, vous devez filtrer la requête pour chaque catégorie i au lieu d'utiliser __in).

Si l'auteur n'a pas de document dans le category_list, il sera répertorié sans documents au-dessous de lui.


Quelque chose comme:

aut1 
tit2 is almost certainly in one of these categories: ['c2', 'c3'] and was absolutely published in 2010. If not, .something_magical_happens_to_documents didn't work. 
tit1 is almost certainly in one of these categories: ['c2', 'c3'] and was absolutely published in 2010. If not, .something_magical_happens_to_documents didn't work. 

aut2 
tit3 is almost certainly in one of these categories: ['c2', 'c3'] and was absolutely published in 2010. If not, .something_magical_happens_to_documents didn't work. 

aut3 
+0

Ce ne filtre que les enregistrements de l'auteur, qui n'est pas l'effet désiré. John, Sally et Maria écrivent chacun 100 docs, John a 10 documents correspondants, Sally 8, Maria 0. Avec votre requête, seulement John et Sally viendraient, et pour chaque 'author.documents.count()' serait encore 100. Je veux que John, Sally et Maria viennent, et 'author.documents.count()' devrait être 10, 8 et 0, respectivement. Je soupçonne qu'il y a un hack qui vous permet de définir ceci si vous êtes prêt à jouer avec les internes d'un objet QuerySet. –

+0

@Jordan, True pour les auteurs sans correspondance manquants (c'est un filtre donc ils sont supprimés bien sûr) mais le nombre de documents ne fonctionne pas parce que vous n'utilisez pas le jeu de requête pour trouver le nombre de documents. Comme le commentaire est trop long et nécessite du code, j'ai édité la réponse. – laurent

+0

@Jordan, 'author.documents.count()' retournera toujours 100 sauf si vous modifiez les données car il s'agit d'une nouvelle requête sans filtrage – laurent