The task is getting harder and harder, humongous change list.
So we'll discuss some notable changes.
2 new features were crowd funded:
Not official nor stable
for field in self._meta.fields:
if f.name == 'foo':
pass
f = self._meta.get_field('foo')
all_fields_list = self._meta.get_fields()
pip install Jinja2
In settings (new in 1.8):
TEMPLATES = [
{
'BACKEND': 'django.template.backends.jinja2.Jinja2',
'APP_DIRS': True,
},
]
Above APP_DIRS
means Jinja2 engines look for templates in
the jinja2
subdirectory of installed applications.
autoescape
, correct template loader for APP_DIRS
,
auto_reload
and undefined
.
environment
option to add Django specific api. From the docs,
assuming we've created myproject/jinja2.py:
from __future__ import absolute_import # Python 2 only
from django.contrib.staticfiles.storage import staticfiles_storage
from django.core.urlresolvers import reverse
from jinja2 import Environment
def environment(**options):
env = Environment(**options)
env.globals.update({
'static': staticfiles_storage.url,
'url': reverse,
})
return env
TEMPLATE
options:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.jinja2.Jinja2',
'APP_DIRS': True,
'OPTIONS': {
'environment': 'myproject.jinja2.environment',
}
},
]
Usage in the template:
Administration
Integrated several features from django-secure
Security Middleware
django.middleware.security.SecurityMiddleware
Provides some security enhancements for request/response. Enable each separately using a setting.
./manage.py check --deploy
New option for check
management command --deploy
,
check settings file to increase site security.
Stored as the native uuid data type on PostgreSQL and as a fixed length character field on other backends.
Has a corresponding
form field
will accept any any string format accepted as the hex
argument to Python's UUID
constructor.
For storing periods of time (timedelta
)
interval
type on PostgreSQL.INTERVAL DAY(9)
TO SECOND(6) on oracle.bigint
of microseconds on other backends.
django.contrib.postgres.fields
has extensions
for PostgreSQL specific features:
ArrayField
, HStoreField
,
Range Fields, and unaccent
lookup.
class Post(models.Model):
name = models.CharField(max_length=200)
tags = ArrayField(models.CharField(max_length=200), blank=True)
>>> Post.objects.create(name='First post', tags=['thoughts', 'django'])
>>> Post.objects.create(name='Second post', tags=['thoughts'])
>>> Post.objects.create(name='Third post', tags=['tutorial', 'django'])
Multitude of query options, index, and slice transforms:
contains
contained_by
overlap
len
See the documentation.
HStoreField
stores key/value mappings of strings to strings.
Need to setup the hstore
extension on your db by adding a migration with
HStoreExtension
operation.
class Dog(models.Model):
name = models.CharField(max_length=200)
data = HStoreField()
>>> Dog.objects.create(name='Rufus', data={'breed': 'labrador'})
>>> Dog.objects.create(name='Meg', data={'breed': 'collie'})
data__breed='collie'
)contains
contained_by
has_key
has_keys
keys
values
See the documentation.
Five range field types, corresponding to the built-in range types in PostgreSQL.
IntegerRangeField
BigIntegerRangeField
FloatRangeField
DateTimeRangeField
DateRangeField
Can define custom range types.
class Event(models.Model):
name = models.CharField(max_length=200)
ages = IntegerRangeField()
>>> Event.objects.create(name='Soft play', ages=(0, 10))
>>> Event.objects.create(name='Pub trip', ages=(21, None))
contains
>>> from psycopg2.extras import NumericRange
>>> Event.objects.filter(ages__contains=NumericRange(4, 5))
contains
contained_by
overlap
fully_lt
fully_gt
not_lt
not_gt
adjacent_to
startswith
endswith
isempty
haccent
or use UnaccentExtension
in a migration.
>>> City.objects.filter(name__unaccent="México")
['<City: Mexico>']
>>> User.objects.filter(first_name__unaccent__startswith="Jerem")
['<User: Jeremy>', '<User: Jérémy>', '<User: Jérémie>', '<User: Jeremie>']
More SQL power for us
annotate()
and aggregate()
by Josh Smeaton.
order_by()
can accept expressions.
# Aggregates can contain complex computations also
Company.objects.annotate(num_offerings=Count(F('products') + F('services')))
# Expressions can also be used in order_by()
Company.objects.order_by(Length('name').asc())
Company.objects.order_by(Length('name').desc())
Let you use if
... elif
... else
logic within filters, annotations, aggregations, and updates
When
Case
>>> # Get the discount for each Client based on the account type
>>> Client.objects.annotate(
... discount=Case(
... When(account_type=Client.GOLD, then=Value('5%')),
... When(account_type=Client.PLATINUM, then=Value('10%')),
... default=Value('0%'),
... output_field=CharField(),
... ),
... ).values_list('name', 'discount')
>>> a_month_ago = date.today() - timedelta(days=30)
>>> a_year_ago = date.today() - timedelta(days=365)
>>> # Update the account_type for each Client from the registration date
>>> Client.objects.update(
... account_type=Case(
... When(registered_on__lte=a_year_ago,
... then=Value(Client.PLATINUM)),
... When(registered_on__lte=a_month_ago,
... then=Value(Client.GOLD)),
... default=Value(Client.REGULAR)
... ),
... )
>>> Client.objects.values_list('name', 'account_type')
[('Jane Doe', 'G'), ('James Smith', 'R'), ('Jack Black', 'P')]
>>> from django.db.models import IntegerField, Sum
>>> Client.objects.aggregate(
... regular=Sum(
... Case(When(account_type=Client.REGULAR, then=1),
... output_field=IntegerField())
... ),
... gold=Sum(
... Case(When(account_type=Client.GOLD, then=1),
... output_field=IntegerField())
... ),
... platinum=Sum(
... Case(When(account_type=Client.PLATINUM, then=1),
... output_field=IntegerField())
... )
... )
{'regular': 2, 'gold': 1, 'platinum': 3}
Use functions provided by the underlying database in annotations, filters, etc.
Coalesce
Concat
Length
Lower
Upper
Substr
atomic
block, class level and for each test.TestCase.setUpTestData()
allows setting initial data at the class level for the whole test.setup()
which runs for each test.TestCase
is now performed once for the whole TestCase
PasswordResetForm
by overriding
send_email()
has_module_permission()
.InlineModelAdmin.show_change_link
.AdminSite.site_url
for the link to "View site", default to "/"
. AdminSite.has_permission()
.Form.has_error(field)
.required_css_class
on labels of required fields.Field.label_suffix
to override label_suffix
per field.runserver
uses daemon threads for faster reloading.dumpdata --output
.loaddata --ignorenonexistent
to ignore data for non existent models.showmigrations
.makemigrations --name
for custom migration name.insepectdb
outputs Meta.uniqute_together
,
introspects AutoField
for PostgreSQL and MySQL, and introspects database views (only tables in < 1.8).RunSQL
can handle parameters.RunSQL.noop
and RunPython.noop
.default_related_name
for model.Model.from_db()
can customize model loading behaviour in the ORM.Model.refresh_from_db()
EmailField.max_length
is 254 instead of 75.GET
and POST
are QueryDict
instead of Dict, FILES
is a MultiValueDict
.HttpResponse.charset
FileResponse
for streaming files.HttpResponse.setdefault()
, allows setting a header unless already been set.select_related()
checks validity of given fields instead of ignoring.add()
, remove()
, clear()
, direct assignment) are run in a transaction to reduce data corruption risk.django.contrib.formtools
moved to separate packages.django.contrib.comments
(after the deprecation in 1.6).TransactionMiddleware
, decorators and context managers defined in django.db.transaction (autocommit
, commit_on_success
, and commit_manually
).patterns()
and string view referencing, instead use:
from django.conf.urls import url
from myapp import views
urlpatterns = [
url('^$', views.myview),
url('^other/$', views.otherview),
]
django.core.context_processors
moved to django.template.context_processors
.NoArgsCommand
, use BaseCommand
which takes no arguments by default.django.contrib.webdesign
, the lorem
tag is now built in.After preparing this talk: