Django Party Dakar - 08 août 2009
Agence Universitaire de la Francophonie
| Auteurs: | Ousmane Wilane <ousmane@wilane.org> |
|---|---|
| Version: | $Id: whats-new-djang-1.1.rst 17 2009-08-08 12:41:54Z wilane $ |
L'utilisation de AdminSite.root() pour exposer les vues de l'admin est veillit
On ne fait plus:
(r'^admin/(.*)', admin.site.root),
On fait plutôt:
(r'^admin/', include(admin.site.urls)),
Deux innovations majeures.
Il est possible maintenant d'exécuter les requêtes d'aggrégation SQL (COUNT(), MAX(), MIN(), etc.). On peut recupérer les résultats des aggrégations directement ou annoter les objets. QuerySet.aggregate():
>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}
all() est redondant dans cet exemple:
>>> Book.objects.aggregate(Avg('price'))
{'price__avg': 34.35}
Pour générer d'autres agrégats, il suffit de les passer en argument à aggregate():
>>> from django.db.models import Avg, Max, Min, Count
>>> Book.objects.aggregate(Avg('price'), Max('price'),
Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'),
'price__min': Decimal('12.99')}
Lorsqu'on annote une QuerySet chaque objet sera annoté avec les valeurs spécifiées. Dans un modèle Book lié à authors par une liaison m2m:
>>> q = Book.objects.annotate(Count('authors'))
>>> q[0]
<Book: The Definitive Guide to Django>
>>> q[0].authors__count
2
# Interrogate the second object in the queryset
>>> q[1]
<Book: Practical Django Projects>
>>> q[1].authors__count
1
Pour utiliser un nom différent du nom auto-génér ci-dessus:
>>> q = Book.objects.annotate(num_authors=
Count('authors'))
>>> q[0].num_authors
2
>>> q[1].num_authors
1
Concrairement à aggregate(), annotate() n'est pas une clause terminale, il génère une QuerySet() qui peut être modifié par toutes les opérations des QuerySet()
Si les champs d'aggrégation sont des champs de modèles liés, on peut encore utiliser la notation __:
>>> Store.objects.annotate(min_price=Min('books__price'),
max_price=Max('books__price'))
Ceci:
aggregate() marche pareil:
>>> Store.objects.aggregate(min_price=Min('books__price'),
max_price=Max('books__price'))
L'age du plus jeune auteur de tous les bouquins vendus dans la boutique:
>>> Store.objects.aggregate(youngest_age=
Min('books__authors__age'))
Le prix moyen des bouquins dont le titre commence par Django:
>>> Book.objects.filter(name__startswith="Django").
annotate(num_authors=Count('authors'))
Les filtres contraignent les objets sur lesquels les aggrégations sont effectuées:
>>> Book.objects.filter(name__startswith="Django").
aggregate(Avg('price'))
Les alias des annotations peuvent être utilisés pour dans les clauses filter() et exclude()
Pour avoir la liste des bouquins écrit par plus d'un auteur:
>>> Book.objects.annotate(num_authors=Count('authors')).
filter(num_authors__gt=1)
Cette requête génère une QuerySet annotée sur lesquels il applique le filtre en utilisant l'alias de l'annotation.
La clause annotate() est appliquée à une QuerySet() en l'état au moment où elle est annotée.
Les deux opération ne sont pas commutatives:
>>> Publisher.objects.annotate(num_books=Count('book')).
filter(book__rating__gt=3.0)
est différent de
>>> Publisher.objects.filter(book__rating__gt=3.0).
annotate(num_books=Count('book'))
>>> Book.objects.annotate(num_authors=Count('authors')).
order_by('num_authors')
Avec cette opération l'annotation est un peu différente. Considérons la requête qui recherche la notation moyenne d'un bouqin:
>>> Author.objects.annotate(average_rating=Avg('book__rating'))
Cette requête va renvoyer un enregistrement pour chaque auteur de la base de donnée. Les resultats vont être un peu différents si on utilise values():
>>> Author.objects.values('name').annotate
(average_rating=Avg('book__rating'))
Cette requête va renvoyer un enregistrement pour chaque auteur de la base de donnée. Les resultats vont être un peu différents si on utilise values():
>>> Author.objects.values('name').annotate
(average_rating=Avg('book__rating'))
Dans ce cas les auteurs vont être regroupés par nom, autrement dis, si vous avez deux auteurs avec le même nom, leurs résultats vont être regroupés dans le même enregitrement.
L'inversion de deux clauses dans le requête précédante:
>>> Author.objects.annotate(average_rating=Avg('book__rating')).
values('name', 'average_rating')
Nous donne un résultat unique pour chaque auteur, mais uniquement le nom de l'auteur et sa note moyenne seront retournés. Vous noterez qu'il est obligatoire d'exopliciter la moyenne dans les champs renvoyés par values() dans ce cas.
CF: docs
Vous pouvez générer une aggrégation de résultats d'annotation:
>>> Book.objects.annotate(num_authors=Count('authors')).
aggregate(Avg('num_authors'))
{'num_authors__avg': 1.66}
Donne le nombre moyen d'auteurs par bouquin.
Les Requêtes peuvent faire réfèrence à d'autres champs et même traverser les relations pour faire réfèrence à des champs sur les modèles liés
>>> from django.db.models import F
>>> Entry.objects.filter(n_pingbacks__lt=F('n_comments'))
Tous les posts du blog ayant plus de commentaires que de pingbacks
Django supporte les opérations d'addition, de soustraction, de multiplication, de division et de modulo avec F():
>>> Entry.objects.filter(n_pingbacks__lt=F('n_comments') * 2)
ou:
>>> Entry.objects.filter(rating__lt=
F('n_comments') + F('n_pingbacks'))
Opérations simple sur un champ:
>>> product = Product.objects.get (name='Venezuelan Beaver Cheese') >>> product.number_sold += 1 >>> product.save()
On peut faire:
>>> from django.db.models import F
>>> product = Product.objects.get
(name='Venezuelan Beaver Cheese')
>>> product.number_sold = F('number_sold') + 1
>>> product.save()
Dans ce cas Django demande à la base de faire la mise à jour en fonction de la valeur actuelle du champs.
Si vous ne souhaitez pas que syncdb et autres gèrent vos schéma vous pouvez utiliser l'option managed:
class MonModel(models.Model):
question = models.ForeignKey(Question)
# ...
class Meta:
managed = False
Si vous voulez utilisez un modèle existant à travers un autre modèle (Proxy):
from django.contrib.auth.models import User
class MonUser(User):
class Meta:
proxy = True
def yada(self):
...
Dans ce cas MonUser travail sur les données de User, ainsi toutes les nouvelles instances de User seront accessibles à travers MonUser et vis-versa.
Si vous désirez exécuter une requête contenant des champs complexes à traduire en type Python, vous pouvez demander que ces champs soient reportés:
Entry.objects.defer("body").filter(headline="Lennon").defer("lede")
Blog.objects.select_related().defer("entry__lede", "entry__body")
Si vous essayez d'accèder aux champs reportés ils seront alors construit à la vollée un à la fois pas pour tous les enregistrements.
only() est le complémentaire de defer().
Vous pouvez spécifier des champs modifiable directement dans les vues de listing de l'admin:
class UserAdmin(admin.ModelAdmin):
list_display = ('username', 'email', 'first_name',
'last_name', 'is_staff')
list_display = ('first_name', 'last_name', 'is_staff')
list_filter = ('is_staff', 'is_superuser')
Exemple:
class ArticleAdmin(admin.ModelAdmin):
list_display = ['title', 'status']
ordering = ['title']
actions = ['make_published']
def make_published(self, request, queryset):
queryset.update(status='p')
make_published.short_description = u"Marquer
comme publiées"
admin.site.register(Article, ArticleAdmin)