Nouveautés De Django 1.1 (rev 11365)

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 $

Fonctionnalités veillit

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)),

Nouveautés de Django 1.1

ORM

Deux innovations majeures.

Aggrégation

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}

Plusieurs Aggrégation

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')}

Annotation

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

Annotation nommée

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()

Jointures et aggrégation

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:

Jointures et aggrégation (2)

aggregate() marche pareil:

>>> Store.objects.aggregate(min_price=Min('books__price'),
max_price=Max('books__price'))

Jointures et aggrégation (exemple)

L'age du plus jeune auteur de tous les bouquins vendus dans la boutique:

>>> Store.objects.aggregate(youngest_age=
Min('books__authors__age'))

Aggrégation et les autres clauses

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'))

Filtres sur des objets annotés

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.

Ordre de annotate() et filter()

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'))

order_by()

>>> Book.objects.annotate(num_authors=Count('authors')).
order_by('num_authors')

values()

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'))

values() (2)

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.

Ordre de values() et annotate()

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.

Interaction des ordres par défaut d'avec order_by()

CF: docs

Aggregating annotations

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.

Expressions dans les requêtes

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

Opérations

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'))

Mise à jour d'attributs

Opérations simple sur un champ:

>>> product = Product.objects.get
(name='Venezuelan Beaver Cheese')
>>> product.number_sold += 1
>>> product.save()

Mise à jour d'attributs (2)

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.

Améliorations des modèles

Modèles non gérés

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

Modèle Proxy

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.

Champs reportés

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().

Améliorations de l'admin

Champs éditable dans les listings

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')

Actions admin

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)

GeoDjango

Améliorations

Autres domaines

Améliorations

Autres détails

Améliorations