Download - Django 1.1 Tour
Idan Gazit
PyWeb-IL, August 31st 2009
Django 1.1a tour of (some) new features
bit.ly/django11release notes are your friend
omg i haz to rewrite mai code?
No!
code you wrote for 1.0 should
“Just Work”
http://bit.ly/djangoapi
With a few exceptions…
With a few exceptions…
constraint names on 64-bit platforms
With a few exceptions…
constraint names on 64-bit platforms
transactions in tests
With a few exceptions…
constraint names on 64-bit platforms
transactions in tests
SetRemoteAddrFromForwardedFor middleware
With a few exceptions…
constraint names on 64-bit platforms
transactions in tests
SetRemoteAddrFromForwardedFor middleware
saving of model formsets
With a few exceptions…
constraint names on 64-bit platforms
transactions in tests
SetRemoteAddrFromForwardedFor middleware
saving of model formsets
names of uploaded files
Also…
changed admin autodiscovery
if your urlconf contains:(r'^admin/(.*)', admin.site.root),
change it to be:(r'^admin/', include(admin.site.urls)),
bit.ly/django11release notes are your friend
1.1 new features
1290 tasty commits, 1200 bugs squashed
1.1 new features
10k new lines of documentation
That’s a lot of new code.(at least there’s a lot of new docs)
New Features!not an exhaustive list.
• Aggregates
• F() Expressions
• Model improvements
• Admin improvements
• Comment Moderation
• Aggregates
• F() Expressions
• Model improvements
• Admin improvements
• Comment Moderation
OMGWTFBBQ!!
Aggregates
What are aggregates?
• aggregate()summary for entire query
• annotate()summary per row returned
Aggregate Examples
What is the average age for all people?
>>> Person.objects.aggregate(Avg(‘age’)){‘age__avg’: 23.65}
IntFieldage
first_name CharField
CharFieldlast_name
Person
person FK
thing CharField
DecimalFieldprice
Purchases
Aggregate Examples
What is the average age for all people?
>>> Person.objects.aggregate(Avg(‘age’), Min(‘age’), Max(‘age’)){‘age__avg’: 23.65, ‘age__min’: 14, ‘age__max’:87}
IntFieldage
first_name CharField
CharFieldlast_name
Person
person FK
thing CharField
DecimalFieldprice
Purchases
Aggregate Examples
What is the average age for all people?
>>> Person.objects.aggregate(Avg(‘age’), Min(‘age’), Max(‘age’)){‘age__avg’: 23.65, ‘age__min’: 14, ‘age__max’:87}
IntFieldage
first_name CharField
CharFieldlast_name
Person
person FK
thing CharField
DecimalFieldprice
Purchases
No Chaining!
Aggregation functions
• Avg(field): average of field
• Min(field): min of field
• Max(field): max of field
• Sum(field): sum of all values in the field
• Count(field, distinct): num of related objects via field
• StdDev(field, sample)
• Variance(field, sample)
Annotation
People ordered by number of purchases made
q = Person.objects.annotate(Count(‘purchases’)).order_by(‘-purchases__count’)
>>> q[0]<Person: Material Girl>>>> q[0].purchases__count4592810191
IntFieldage
first_name CharField
CharFieldlast_name
Person
person FK
thing CharField
DecimalFieldprice
Purchases
Annotation
People ordered by number of purchases made
q = Person.objects.annotate(num_purchases=Count(‘purchases’)).order_by(‘-num_purchases’)
>>> q[0]<Person: Material Girl>>>> q[0].num_purchases4592810191
IntFieldage
first_name CharField
CharFieldlast_name
Person
person FK
thing CharField
DecimalFieldprice
Purchases
Annotation
Which people have <=10 purchases?
q = Person.objects.annotate( num_purchases=Count(‘purchases’)).filter(num_purchases__lte=10)
IntFieldage
first_name CharField
CharFieldlast_name
Person
person FK
thing CharField
DecimalFieldprice
Purchases
Annotation
Annotations work across JOINs:
What is the average purchase price for purchases made by each person?
Person.objects.annotate(min_price=Min(‘purchases__price’))
IntFieldage
first_name CharField
CharFieldlast_name
Person
person FK
thing CharField
DecimalFieldprice
Purchases
Dizzy Yet?many possibilities
Annotation
Order is important!
foo.filter().annotate() != foo.annotate().filter()
F( ) expressions
F( ) Expressions
class Stock(models.Model): symbol = models.CharField()
class TradingDay(models.Model): stock = models.ForeignKey(Stock, related_name="days") opening = models.DecimalField() closing = models.DecimalField() high = models.DecimalField() low = models.DecimalField()
F( ) Expressions
class Stock(models.Model): symbol = models.CharField()
class TradingDay(models.Model): stock = models.ForeignKey(Stock, related_name="days") opening = models.DecimalField() closing = models.DecimalField() high = models.DecimalField() low = models.DecimalField()
from django.db.models import F# Closed at least 50% up from openTradingDay.objects.filter(closing__gte=F('opening') * 1.5)# All downhillTradingDay.objects.filter(opening=F('high'))# Works across JOINs# Stocks that have days with <10pt loss in valueStock.objects.filter(days__closing__gte=F('days__opening')-10.0)
F( ) Expressions
Atomic DB Increment Operations!
class MyModel(): ... counter = models.IntegerField()
MyModel.objects.filter(id=someid).update(counter=F('counter')+1)
Model Improvements
Unmanaged Models
• Useful for tables or DB views which aren’t under Django’s control
• Work like regular models
• No table creation during syncdb, tests, etc.
Unmanaged Models
class MyModel(): ... # some fields which match your existing table's column types class Meta: managed = False
Proxy Models
I already have a model, and want to change its python behavior without changing the underlying table structure.
Proxy Models
class MyProxy(MyModel): # no new fields! # but you can define new managers objects = SupaDupaProxyManager() proxy_specific_manager = PonyManager() class Meta: proxy = True ordering = ['not_the_original_ordering_field',] def my_proxy_method(self): # ... do something
admin improvements
Admin Actions
Custom Admin Actions
def add_cowbell(modeladmin, request, queryset): queryset.update(myfield='cowbell!')
Custom Admin Actions
def add_cowbell(modeladmin, request, queryset): queryset.update(myfield='cowbell!')
add_cowbell.short_description = "More Cowbell!"
Custom Admin Actions
def add_cowbell(modeladmin, request, queryset): for obj in queryset: obj.myfield += 'cowbell!' obj.save()
add_cowbell.short_description = "More Cowbell!"
Custom Admin Actions
def add_cowbell(modeladmin, request, queryset): for obj in queryset: obj.myfield += 'cowbell!' obj.save()
add_cowbell.short_description = "More Cowbell!"
class MusicAdmin(admin.ModelAdmin): list_display = ['artist', 'song'] ordering = ['artist',] actions = [add_cowbell]
list_editable
Generic Comment Moderation
django.contrib.comments.moderation
class BlogPost(models.Model): title = models.CharField() body = models.TextField() posted = models.DateTimeField() enable_comments = models.BooleanField() is_public = models.BooleanField()
django.contrib.comments.moderation
class BlogPost(models.Model): title = models.CharField() body = models.TextField() posted = models.DateTimeField() enable_comments = models.BooleanField() is_public = models.BooleanField() class BlogPostModerator(moderation.Moderator): email_notification = True enable_field = 'enable_comments' auto_close_field = 'posted' close_after = 14
moderation.moderator.register(BlogPost, BlogPostModerator)
django.contrib.comments.moderation
class BlogPostModerator(moderation.Moderator): email_notification = True enable_field = 'enable_comments' auto_close_field = 'posted' close_after = 14 def allow(comment, content_object, request): # return False to delete comment def moderate(comment, content_object, request): # return True to moderate comment
moderation.moderator.register(BlogPost, BlogPostModerator)
Random
(r'^myapp/', include('myapp.urls', namespace='foo', app_name='bar'))
reverse(‘bar:mynamedurl’, args=[‘xyzzy’], current_app=‘foo’)
URL Namespaces
{% for foo in bars %} <p>{{ foo.name }}</p>{% empty %} <p class="empty">No foos in bars!</p>{% endfor %}
For/Empty template tag
• forms: hidden_fields() / visible_fields()
• auth using REMOTE_USER: IIS/mod_auth_sspi, mod_authnz_ldap, etc
• safeseq template filterlike safe but for lists
• django.shortcuts.redirect() view:def my_view(request): ... return redirect('some-view-name', foo='bar')
More Random!
Many More!
Photo Credits
• http://www.flickr.com/photos/josstyk/248920216/• http://www.flickr.com/photos/mar00ned/3274556235/• http://www.flickr.com/photos/ilumb/361819506/• http://www.flickr.com/photos/womanofscorn/9163061/• http://www.flickr.com/photos/ginnerobot/2549674296/• http://www.flickr.com/photos/squaregraph/24869936• http://www.flickr.com/photos/aresauburnphotos/3381681226• http://www.flickr.com/photos/lwr/105783846/• http://www.flickr.com/photos/jurvetson/447302275• http://www.flickr.com/photos/leecullivan/240389468• http://www.flickr.com/photos/pulpolux/3698819113