• Like
  • Save

Loading…

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

Django In Depth

  • 32,239 views
Uploaded on

A tutorial given at PyCon 2010 in Atlanta

A tutorial given at PyCon 2010 in Atlanta

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
32,239
On Slideshare
0
From Embeds
0
Number of Embeds
20

Actions

Shares
Downloads
0
Comments
8
Likes
162

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. django in Depth James Bennett • PyCon Atlanta • February 18, 2010
  • 2. Let’s talk about you
  • 3. Let’s talk about you You’ve done a Python tutorial
  • 4. Let’s talk about you You’ve done a Python tutorial You’ve done a Django tutorial
  • 5. Let’s talk about you You’ve done a Python tutorial You’ve done a Django tutorial You want to understand how all the parts fit together
  • 6. Let’s talk about me
  • 7. Let’s talk about me Working with Django for 4½ years; last 4 at the Lawrence Journal-World
  • 8. Let’s talk about me Working with Django for 4½ years; last 4 at the Lawrence Journal-World Release manager for the project
  • 9. Let’s talk about me Working with Django for 4½ years; last 4 at the Lawrence Journal-World Release manager for the project Author, “Practical Django Projects” (Apress)
  • 10. Let’s talk about this tutorial
  • 11. Let’s talk about this tutorial “Bottom-up” approach
  • 12. Let’s talk about this tutorial “Bottom-up” approach Lots of gory details
  • 13. Let’s talk about this tutorial “Bottom-up” approach Lots of gory details ORM, forms, templates, requests, admin
  • 14. Let’s talk about this tutorial “Bottom-up” approach Lots of gory details ORM, forms, templates, requests, admin Undocumented APIs
  • 15. Let’s talk about this tutorial “Bottom-up” approach Lots of gory details ORM, forms, templates, requests, admin Undocumented APIs Covers Django 1.2 (soon to be released)
  • 16. The ORM
  • 17. Look, it’s a blog!
  • 18. Look, it’s a blog! from django.db import models class Entry(models.Model): title = models.CharField(max_length=255) pub_date = models.DateTimeField() body = models.TextField() def __unicode__(self): return self.title
  • 19. A magic trick
  • 20. A magic trick >>> from blog.models import Entry >>> Entry.objects.all()
  • 21. A magic trick >>> from blog.models import Entry >>> Entry.objects.all() SELECT "blog_entry"."id", "blog_entry"."title", "blog_entry"."pub_date", "blog_entry"."body" FROM "blog_entry";
  • 22. A magic trick >>> from blog.models import Entry >>> Entry.objects.all() SELECT "blog_entry"."id", "blog_entry"."title", "blog_entry"."pub_date", "blog_entry"."body" FROM "blog_entry"; [<Entry: My first entry>, <Entry: Another entry>]
  • 23. How it works (in excruciating detail)
  • 24. Down the rabbit hole…
  • 25. Down the rabbit hole… Model
  • 26. Down the rabbit hole… Model Manager
  • 27. Down the rabbit hole… Model Manager QuerySet
  • 28. Down the rabbit hole… Model Manager QuerySet Query
  • 29. Down the rabbit hole… Model Manager QuerySet Query SQLCompiler
  • 30. Down the rabbit hole… Model Manager QuerySet Query SQLCompiler Database backend
  • 31. Database backends
  • 32. Database backends At least one backend module for each supported database (in django.db.backends)
  • 33. Database backends At least one backend module for each supported database (in django.db.backends) Goes in the ENGINE portion of the database settings
  • 34. Database backends At least one backend module for each supported database (in django.db.backends) Goes in the ENGINE portion of the database settings Specifies extremely low-level behavior
  • 35. Database backends At least one backend module for each supported database (in django.db.backends) Goes in the ENGINE portion of the database settings Specifies extremely low-level behavior Provides the boundary between Django and the DB driver (psycopg, cx_Oracle, etc.)
  • 36. Database backends At least one backend module for each supported database (in django.db.backends) Goes in the ENGINE portion of the database settings Specifies extremely low-level behavior Provides the boundary between Django and the DB driver (psycopg, cx_Oracle, etc.) Made up of several classes
  • 37. DatabaseWrapper
  • 38. DatabaseWrapper Knows how to connect to the database and return a cursor
  • 39. DatabaseWrapper Knows how to connect to the database and return a cursor Provides the transaction-management hooks
  • 40. DatabaseWrapper Knows how to connect to the database and return a cursor Provides the transaction-management hooks Maps Python-level lookup types to SQL operators in the DB’s dialect
  • 41. DatabaseOperations
  • 42. DatabaseOperations Understands and implements a particular database’s “quirks”
  • 43. DatabaseOperations Understands and implements a particular database’s “quirks” Knows how to generate DB-specific SQL (typecasts, DDL, quoting, transaction begin/end/commit)
  • 44. DatabaseOperations Understands and implements a particular database’s “quirks” Knows how to generate DB-specific SQL (typecasts, DDL, quoting, transaction begin/end/commit) Can specify an alternate query generator for more fine- grained SQL control
  • 45. DatabaseFeatures
  • 46. DatabaseFeatures What does this database support?
  • 47. DatabaseFeatures What does this database support? Savepoints, autocommit modes, certain NULL-handling quirks
  • 48. DatabaseFeatures What does this database support? Savepoints, autocommit modes, certain NULL-handling quirks Type constraints
  • 49. DatabaseCreation
  • 50. DatabaseCreation Knows how to create your database (and test database) and tables in it
  • 51. DatabaseCreation Knows how to create your database (and test database) and tables in it Maps model field types to column types
  • 52. DatabaseIntrospection
  • 53. DatabaseIntrospection Used by inspectdb
  • 54. DatabaseIntrospection Used by inspectdb Knows how to get list of tables
  • 55. DatabaseIntrospection Used by inspectdb Knows how to get list of tables Handles reverse mapping of column types to model field types
  • 56. DatabaseIntrospection Used by inspectdb Knows how to get list of tables Handles reverse mapping of column types to model field types Detects relations on DBs which allow it
  • 57. Other bits
  • 58. Other bits DatabaseClient can fire up an interactive shell (for dbshell)
  • 59. Other bits DatabaseClient can fire up an interactive shell (for dbshell) DatabaseValidation can implement DB-specific validation of model definitions
  • 60. Other bits DatabaseClient can fire up an interactive shell (for dbshell) DatabaseValidation can implement DB-specific validation of model definitions DatabaseError and IntegrityError provide normalized exception classes for errors from the DB
  • 61. Write a new backend if…
  • 62. Write a new backend if… Django doesn’t support your database or driver of choice
  • 63. Write a new backend if… Django doesn’t support your database or driver of choice You want to control the connection mechanism (you can do connection pooling at this level)
  • 64. Write a new backend if… Django doesn’t support your database or driver of choice You want to control the connection mechanism (you can do connection pooling at this level) You need really low-level overriding of operations (like field type mappings or SQL generation)
  • 65. SQLCompiler
  • 66. SQLCompiler Base classes in django.db.models.sql.compiler
  • 67. SQLCompiler Base classes in django.db.models.sql.compiler Compiles a Query to SQL
  • 68. SQLCompiler Base classes in django.db.models.sql.compiler Compiles a Query to SQL Executes the SQL against the correct DB
  • 69. SQLCompiler Base classes in django.db.models.sql.compiler Compiles a Query to SQL Executes the SQL against the correct DB Returns the results
  • 70. Flavors of SQLCompiler
  • 71. Flavors of SQLCompiler SQLCompiler is the base class, and the default for most queries (i.e., SELECT queries)
  • 72. Flavors of SQLCompiler SQLCompiler is the base class, and the default for most queries (i.e., SELECT queries) One subclass for each other query type: SQLInsertCompiler, SQLDeleteCompiler, etc.
  • 73. Flavors of SQLCompiler SQLCompiler is the base class, and the default for most queries (i.e., SELECT queries) One subclass for each other query type: SQLInsertCompiler, SQLDeleteCompiler, etc. Subqueries for aggregates go through SQLAggregateCompiler
  • 74. Flavors of SQLCompiler SQLCompiler is the base class, and the default for most queries (i.e., SELECT queries) One subclass for each other query type: SQLInsertCompiler, SQLDeleteCompiler, etc. Subqueries for aggregates go through SQLAggregateCompiler SQLDateCompiler is a special case used for dates() queries
  • 75. You shouldn’t ever need to work with SQLCompiler directly…
  • 76. …unless you’re writing the Oracle backend…
  • 77. …but just in case:
  • 78. …but just in case: Query.get_compiler() returns a SQLCompiler instance
  • 79. …but just in case: Query.get_compiler() returns a SQLCompiler instance Calls DatabaseOperations.compiler()
  • 80. …but just in case: Query.get_compiler() returns a SQLCompiler instance Calls DatabaseOperations.compiler() DatabaseOperations.compiler_module specifies the module name, Query.compiler specifies the class name
  • 81. …but just in case: Query.get_compiler() returns a SQLCompiler instance Calls DatabaseOperations.compiler() DatabaseOperations.compiler_module specifies the module name, Query.compiler specifies the class name Oracle backend uses this to provide a compiler which understands Oracle’s SQL dialect
  • 82. The Query class
  • 83. The Query class Base class in django.db.models.sql.query
  • 84. The Query class Base class in django.db.models.sql.query A big scary data structure
  • 85. The Query class Base class in django.db.models.sql.query A big scary data structure Tracks all the elements of the eventual query: columns, tables, joins, orderings, etc.
  • 86. The Query class Base class in django.db.models.sql.query A big scary data structure Tracks all the elements of the eventual query: columns, tables, joins, orderings, etc. Here be dragons (db/models/sql/query.py is over 1800 lines of code)
  • 87. What’s in Query
  • 88. What’s in Query Attributes storing building blocks which become SELECT, FROM, WHERE, HAVING, ORDER BY, limit and offset, etc.
  • 89. What’s in Query Attributes storing building blocks which become SELECT, FROM, WHERE, HAVING, ORDER BY, limit and offset, etc. Methods for manipulating these attributes
  • 90. What’s in Query Attributes storing building blocks which become SELECT, FROM, WHERE, HAVING, ORDER BY, limit and offset, etc. Methods for manipulating these attributes Most high-level things you do in queries end up as calls to Query.add_filter()
  • 91. Flavors of Query
  • 92. Flavors of Query Just like SQLCompiler, subclasses exist for different query types: INSERT, DELETE, etc.
  • 93. Flavors of Query Just like SQLCompiler, subclasses exist for different query types: INSERT, DELETE, etc. Specialized subclasses for aggregates and dates() queries
  • 94. Flavors of Query Just like SQLCompiler, subclasses exist for different query types: INSERT, DELETE, etc. Specialized subclasses for aggregates and dates() queries And brand-new in Django 1.2: RawQuery implements raw()
  • 95. Changes in Django 1.2
  • 96. Changes in Django 1.2 Query used to be the bottom of the chain, and generated the SQL with help from the backend
  • 97. Changes in Django 1.2 Query used to be the bottom of the chain, and generated the SQL with help from the backend Database backends would swap out the Query class as needed
  • 98. Changes in Django 1.2 Query used to be the bottom of the chain, and generated the SQL with help from the backend Database backends would swap out the Query class as needed SQLCompiler handles the SQL generation now
  • 99. Playing with Query
  • 100. Playing with Query You probably don’t need to (though prior to 1.2, some DB backends did)
  • 101. Playing with Query You probably don’t need to (though prior to 1.2, some DB backends did) Vast majority of code in Query just does bookkeeping
  • 102. Playing with Query You probably don’t need to (though prior to 1.2, some DB backends did) Vast majority of code in Query just does bookkeeping __str__() is useful: returns the query as a string of SQL (generated by SQLCompiler)
  • 103. QuerySet
  • 104. QuerySet Wraps a Query instance
  • 105. QuerySet Wraps a Query instance Knows which model to query
  • 106. QuerySet Wraps a Query instance Knows which model to query Implements the high-level querying methods you actually use
  • 107. QuerySet Wraps a Query instance Knows which model to query Implements the high-level querying methods you actually use Acts as a container-ish object for accessing query results
  • 108. Laziness
  • 109. Laziness No results until forced to fetch them
  • 110. Laziness No results until forced to fetch them Results may or may not be cached depending on use
  • 111. Methods which force a query
  • 112. Methods which force a query Methods which don’t return a QuerySet
  • 113. Methods which force a query Methods which don’t return a QuerySet Methods which return the number of results or check whether there are results
  • 114. Methods which force a query aggregate() get_or_create() count() in_bulk() create() iterator() delete() latest() exists() update() get()
  • 115. Other ways to force a query
  • 116. Other ways to force a query Iteration (in a for loop or otherwise)
  • 117. Other ways to force a query Iteration (in a for loop or otherwise) Slicing (one item or multiple)
  • 118. Other ways to force a query Iteration (in a for loop or otherwise) Slicing (one item or multiple) Boolean evaluation (in an if statement)
  • 119. Other ways to force a query Iteration (in a for loop or otherwise) Slicing (one item or multiple) Boolean evaluation (in an if statement) len()
  • 120. Other ways to force a query Iteration (in a for loop or repr() otherwise) Slicing (one item or multiple) Boolean evaluation (in an if statement) len()
  • 121. Other ways to force a query Iteration (in a for loop or repr() otherwise) list() Slicing (one item or multiple) Boolean evaluation (in an if statement) len()
  • 122. Other ways to force a query Iteration (in a for loop or repr() otherwise) list() Slicing (one item or Pickling multiple) Boolean evaluation (in an if statement) len()
  • 123. Other ways to force a query Iteration (in a for loop or repr() otherwise) list() Slicing (one item or Pickling multiple) Caching Boolean evaluation (in an if statement) len()
  • 124. __repr__() is special
  • 125. __repr__() is special Will add LIMIT 21 to the query
  • 126. __repr__() is special Will add LIMIT 21 to the query Saves you from yourself: accidentally repr()-ing a QuerySet with a million results can suck
  • 127. __repr__() is special Will add LIMIT 21 to the query Saves you from yourself: accidentally repr()-ing a QuerySet with a million results can suck Also saves you from debug pages which print string representations of variables
  • 128. A QuerySet is a container
  • 129. A QuerySet is a container QuerySet instances have an internal result cache
  • 130. A QuerySet is a container QuerySet instances have an internal result cache Iterating a QuerySet multiple times only does the query once (subsequent iterations use the cache)
  • 131. A container?
  • 132. A container? >>> my_queryset = SomeModel.objects.all()
  • 133. A container? >>> my_queryset = SomeModel.objects.all() >>> my_queryset[5].some_attribute
  • 134. A container? >>> my_queryset = SomeModel.objects.all() >>> my_queryset[5].some_attribute False
  • 135. A container? >>> my_queryset = SomeModel.objects.all() >>> my_queryset[5].some_attribute False >>> my_queryset[5].some_attribute = True
  • 136. A container? >>> my_queryset = SomeModel.objects.all() >>> my_queryset[5].some_attribute False >>> my_queryset[5].some_attribute = True >>> my_queryset[5].save()
  • 137. A container? >>> my_queryset = SomeModel.objects.all() >>> my_queryset[5].some_attribute False >>> my_queryset[5].some_attribute = True >>> my_queryset[5].save() >>> my_queryset[5].some_attribute
  • 138. A container? >>> my_queryset = SomeModel.objects.all() >>> my_queryset[5].some_attribute False >>> my_queryset[5].some_attribute = True >>> my_queryset[5].save() >>> my_queryset[5].some_attribute False
  • 139. A container? >>> my_queryset = SomeModel.objects.all() >>> my_queryset[5].some_attribute False >>> my_queryset[5].some_attribute = True >>> my_queryset[5].save() >>> my_queryset[5].some_attribute False >>> # what?
  • 140. Indexing is special
  • 141. Indexing is special Trade-off: indexing/slicing imply LIMIT/OFFSET and generate the appropriate (new) query
  • 142. Indexing is special Trade-off: indexing/slicing imply LIMIT/OFFSET and generate the appropriate (new) query Each query returns a separate in-memory copy of the model instance(s)
  • 143. Methods you’ve never heard of (but should use)
  • 144. update()
  • 145. update() Issues a bulk update of every record in the QuerySet
  • 146. update() Issues a bulk update of every record in the QuerySet Executes in a single SQL UPDATE statement
  • 147. update() Issues a bulk update of every record in the QuerySet Executes in a single SQL UPDATE statement Doesn’t execute custom save() methods
  • 148. delete()
  • 149. delete() Deletes every record in the QuerySet
  • 150. delete() Deletes every record in the QuerySet May or may not be a single SQL DELETE statement
  • 151. delete() Deletes every record in the QuerySet May or may not be a single SQL DELETE statement May or may not execute custom delete() methods
  • 152. iterator()
  • 153. iterator() Returns an iterator over the results, instantiates only one object at a time
  • 154. iterator() Returns an iterator over the results, instantiates only one object at a time No internal result cache
  • 155. iterator() Returns an iterator over the results, instantiates only one object at a time No internal result cache Big memory saving for large result sets
  • 156. exists()
  • 157. exists() Returns True if the query has results, False otherwise
  • 158. exists() Returns True if the query has results, False otherwise If the QuerySet already evaluated, checks the result cache
  • 159. exists() Returns True if the query has results, False otherwise If the QuerySet already evaluated, checks the result cache If not, does a (fast) query to see if results would exist
  • 160. exists() Returns True if the query has results, False otherwise If the QuerySet already evaluated, checks the result cache If not, does a (fast) query to see if results would exist Usually better than just doing if some_queryset
  • 161. defer() and only()
  • 162. defer() and only() Return “partial” model instances, with only some fields filled in
  • 163. defer() and only() Return “partial” model instances, with only some fields filled in Let you control exactly which fields are selected by the query
  • 164. values() and values_list()
  • 165. values() and values_list() Like defer() and only(), let you control which fields are selected
  • 166. values() and values_list() Like defer() and only(), let you control which fields are selected Don’t return model instances: values() returns dictionaries, values_list() returns lists
  • 167. values() and values_list() Like defer() and only(), let you control which fields are selected Don’t return model instances: values() returns dictionaries, values_list() returns lists values_list(flat=True), with a single field name, returns a single list
  • 168. Write your own!
  • 169. Write your own! Writing a QuerySet subclass with additional query methods is easy
  • 170. Write your own! Writing a QuerySet subclass with additional query methods is easy Example: http://simonwillison.net/2008/May/1/orm/
  • 171. Manager
  • 172. Manager Starting point for nearly all queries
  • 173. Manager Starting point for nearly all queries Attached directly to a model class, accessible as self.model on the manager
  • 174. Manager Starting point for nearly all queries Attached directly to a model class, accessible as self.model on the manager Exposes most of the query methods of QuerySet
  • 175. Manager options
  • 176. Manager options Don’t specify one at all, Django creates one for you and names it objects
  • 177. Manager options Don’t specify one at all, Django creates one for you and names it objects If you specify one, Django doesn’t create the default objects manager
  • 178. Manager options Don’t specify one at all, Django creates one for you and names it objects If you specify one, Django doesn’t create the default objects manager One model can have multiple managers
  • 179. Manager options Don’t specify one at all, Django creates one for you and names it objects If you specify one, Django doesn’t create the default objects manager One model can have multiple managers First manager defined is the “default” manager, and becomes the attribute _default_manager
  • 180. How it works
  • 181. How it works Manager.get_query_set() returns a QuerySet
  • 182. How it works Manager.get_query_set() returns a QuerySet Everything else just calls methods on the returned QuerySet
  • 183. How it works Manager.get_query_set() returns a QuerySet Everything else just calls methods on the returned QuerySet Except raw(), which directly instantiates and returns a RawQuerySet
  • 184. Blog entries with status
  • 185. Blog entries with status class Entry(models.Model): LIVE_STATUS = 1 DRAFT_STATUS = 2 STATUS_CHOICES = ( (LIVE_STATUS, ‘Live’), (DRAFT_STATUS, ‘Draft’), ) status = models.IntegerField(choices=STATUS_CHOICES, default=LIVE_STATUS)
  • 186. Custom manager
  • 187. Custom manager class EntryManager(models.Manager): def live(self): return self.filter(status=self.model.LIVE_STATUS)
  • 188. Put it together
  • 189. Put it together class Entry(models.Model): …fields and such… objects = EntryManager() live_entries = Entry.objects.live()
  • 190. Do it in QuerySet
  • 191. Do it in QuerySet class EntryQuerySet(QuerySet): def live(self): return self.filter(status=self.model.LIVE_STATUS) class EntryManager(models.Manager): def get_query_set(self): return EntryQuerySet(self.model) # Now you can do... may_2009 = Entry.objects.filter(pub_date__year=2009, pub_date__month=5) may_2009_live = may_2009.live()
  • 192. But be careful
  • 193. But be careful Overriding get_query_set() affects all queries for that manager
  • 194. But be careful Overriding get_query_set() affects all queries for that manager Can lead to unpleasant results if you really want to fetch certain objects but your custom QuerySet filters them out
  • 195. The power of managers
  • 196. The power of managers Encapsulate common query patterns
  • 197. The power of managers Encapsulate common query patterns Got status fields on lots of models? Write a manager with query methods for it and re-use
  • 198. The power of managers Encapsulate common query patterns Got status fields on lots of models? Write a manager with query methods for it and re-use More exotic query types, too: want a most_commented() method?
  • 199. Multiple databases
  • 200. Multiple databases New in Django 1.2
  • 201. Multiple databases New in Django 1.2 Mostly low-level API bits for now
  • 202. Database routers
  • 203. Database routers Determine which DBs get reads, writes and syncdb
  • 204. Database routers Determine which DBs get reads, writes and syncdb Can allow/disallow creation of relations
  • 205. Database routers Determine which DBs get reads, writes and syncdb Can allow/disallow creation of relations Specified in the DATABASE_ROUTERS setting
  • 206. Database routers Determine which DBs get reads, writes and syncdb Can allow/disallow creation of relations Specified in the DATABASE_ROUTERS setting Default router saves objects to the DB they were queried from, uses the default DB unless otherwise specified
  • 207. Manual control
  • 208. Manual control QuerySet.using() takes a DB connection alias and passes it down the chain
  • 209. Manual control QuerySet.using() takes a DB connection alias and passes it down the chain Manager.db_manager(name) returns a new Manager instance using that connection
  • 210. Manual control QuerySet.using() takes a DB connection alias and passes it down the chain Manager.db_manager(name) returns a new Manager instance using that connection save() and delete() take a using argument
  • 211. Tracking models
  • 212. Tracking models Each model instance has a new attribute: _state
  • 213. Tracking models Each model instance has a new attribute: _state Instance of django.db.models.base.ModelState
  • 214. Tracking models Each model instance has a new attribute: _state Instance of django.db.models.base.ModelState ModelState.db is the name of a DB connection
  • 215. Tracking models Each model instance has a new attribute: _state Instance of django.db.models.base.ModelState ModelState.db is the name of a DB connection Set automatically by save() and by a QuerySet doing retrieval
  • 216. Use cases
  • 217. Use cases Sharding: write a router which determines where to read/ write data
  • 218. Use cases Sharding: write a router which determines where to read/ write data Master/slave replication: send all writes to the master, read from the slaves
  • 219. Use cases Sharding: write a router which determines where to read/ write data Master/slave replication: send all writes to the master, read from the slaves Disparate data sets: override Manager.get_query_set() to always use a particular DB
  • 220. Limitations
  • 221. Limitations Cross-database relations are not supported by most DBs
  • 222. Limitations Cross-database relations are not supported by most DBs Order of routers is significant: first router to return a DB name wins
  • 223. Models
  • 224. Models Represent data: one model class (usually) maps to one database table
  • 225. Models Represent data: one model class (usually) maps to one database table Fields (usually) map to columns in that table
  • 226. Models Represent data: one model class (usually) maps to one database table Fields (usually) map to columns in that table Specify options: ordering, human-readable name, etc.
  • 227. Models Represent data: one model class (usually) maps to one database table Fields (usually) map to columns in that table Specify options: ordering, human-readable name, etc. Methods for saving/deleting
  • 228. Models Represent data: one model class (usually) maps to one database table Fields (usually) map to columns in that table Specify options: ordering, human-readable name, etc. Methods for saving/deleting Custom methods for business logic
  • 229. Model classes
  • 230. Model classes django.db.models.Model uses a metaclass: django.db.models.base.ModelBase
  • 231. Model classes django.db.models.Model uses a metaclass: django.db.models.base.ModelBase ModelBase sets up some attributes, delegates setup of others
  • 232. ModelBase
  • 233. ModelBase Parses Meta declaration, fields and inheritance, creates the Options instance on the model class
  • 234. ModelBase Parses Meta declaration, fields and inheritance, creates the Options instance on the model class Adds per-model exception classes (DoesNotExist, etc.)
  • 235. ModelBase Parses Meta declaration, fields and inheritance, creates the Options instance on the model class Adds per-model exception classes (DoesNotExist, etc.) Sends signals for model class preparation and registers the model class
  • 236. Model customization hooks
  • 237. Model customization hooks contribute_to_class(cls, name) will be called by ModelBase while the model class is being constructed
  • 238. Model customization hooks contribute_to_class(cls, name) will be called by ModelBase while the model class is being constructed django.db.models.signals.class_prepared will be sent after ModelBase finishes constructing the model class
  • 239. contribute_to_class()
  • 240. contribute_to_class() Any attribute of the model class which has this method will have it called
  • 241. contribute_to_class() Any attribute of the model class which has this method will have it called Gets passed the model class and the attribute name
  • 242. contribute_to_class() Any attribute of the model class which has this method will have it called Gets passed the model class and the attribute name Used to set up any special behavior (usually for model field types)
  • 243. class_prepared
  • 244. class_prepared Has only one argument: sender, which is the class just constructed
  • 245. class_prepared Has only one argument: sender, which is the class just constructed Allows code to run any time a model class is parsed/ constructed
  • 246. class_prepared Has only one argument: sender, which is the class just constructed Allows code to run any time a model class is parsed/ constructed Django uses this signal to ensure each model has at least one manager attached
  • 247. The Options class
  • 248. The Options class In django.db.models.options
  • 249. The Options class In django.db.models.options Holds all the stuff you put in that class Meta declaration
  • 250. The Options class In django.db.models.options Holds all the stuff you put in that class Meta declaration Plus other metadata about the model
  • 251. The Options class In django.db.models.options Holds all the stuff you put in that class Meta declaration Plus other metadata about the model Ends up as the attribute _meta of the model class
  • 252. Useful tricks
  • 253. Useful tricks _meta.fields is a list of the model’s fields
  • 254. Useful tricks _meta.fields is a list of the model’s fields _meta.many_to_many is a list of the model’s many-to- many relationships
  • 255. Useful tricks _meta.fields is a list of the model’s fields _meta.many_to_many is a list of the model’s many-to- many relationships _meta.get_field() returns the actual field objects
  • 256. See it in action
  • 257. See it in action >>> from blog.models import Entry
  • 258. See it in action >>> from blog.models import Entry >>> opts = Entry._meta
  • 259. See it in action >>> from blog.models import Entry >>> opts = Entry._meta >>> [f.name for f in opts.fields]
  • 260. See it in action >>> from blog.models import Entry >>> opts = Entry._meta >>> [f.name for f in opts.fields] ['id', 'title', 'pub_date', 'body']
  • 261. See it in action >>> from blog.models import Entry >>> opts = Entry._meta >>> [f.name for f in opts.fields] ['id', 'title', 'pub_date', 'body'] >>> opts.get_field(‘title’)
  • 262. See it in action >>> from blog.models import Entry >>> opts = Entry._meta >>> [f.name for f in opts.fields] ['id', 'title', 'pub_date', 'body'] >>> opts.get_field(‘title’) <django.db.models.fields.CharField object at 0x1429530>
  • 263. See it in action >>> from blog.models import Entry >>> opts = Entry._meta >>> [f.name for f in opts.fields] ['id', 'title', 'pub_date', 'body'] >>> opts.get_field(‘title’) <django.db.models.fields.CharField object at 0x1429530> >>> opts.get_field('title').max_length
  • 264. See it in action >>> from blog.models import Entry >>> opts = Entry._meta >>> [f.name for f in opts.fields] ['id', 'title', 'pub_date', 'body'] >>> opts.get_field(‘title’) <django.db.models.fields.CharField object at 0x1429530> >>> opts.get_field('title').max_length 255
  • 265. See it in action
  • 266. See it in action >>> from django.contrib.auth.models import User
  • 267. See it in action >>> from django.contrib.auth.models import User >>> alice = User.objects.get(username=‘alice’)
  • 268. See it in action >>> from django.contrib.auth.models import User >>> alice = User.objects.get(username=‘alice’) >>> bob = User.objects.get(username=‘bob’)
  • 269. See it in action >>> from django.contrib.auth.models import User >>> alice = User.objects.get(username=‘alice’) >>> bob = User.objects.get(username=‘bob’) >>> opts = User._meta
  • 270. See it in action >>> from django.contrib.auth.models import User >>> alice = User.objects.get(username=‘alice’) >>> bob = User.objects.get(username=‘bob’) >>> opts = User._meta >>> username_field = opts.get_field(‘username’)
  • 271. See it in action >>> from django.contrib.auth.models import User >>> alice = User.objects.get(username=‘alice’) >>> bob = User.objects.get(username=‘bob’) >>> opts = User._meta >>> username_field = opts.get_field(‘username’) >>> username_field.value_from_object(alice)
  • 272. See it in action >>> from django.contrib.auth.models import User >>> alice = User.objects.get(username=‘alice’) >>> bob = User.objects.get(username=‘bob’) >>> opts = User._meta >>> username_field = opts.get_field(‘username’) >>> username_field.value_from_object(alice) u’alice’
  • 273. See it in action >>> from django.contrib.auth.models import User >>> alice = User.objects.get(username=‘alice’) >>> bob = User.objects.get(username=‘bob’) >>> opts = User._meta >>> username_field = opts.get_field(‘username’) >>> username_field.value_from_object(alice) u’alice’ >>> username_field.value_from_object(bob)
  • 274. See it in action >>> from django.contrib.auth.models import User >>> alice = User.objects.get(username=‘alice’) >>> bob = User.objects.get(username=‘bob’) >>> opts = User._meta >>> username_field = opts.get_field(‘username’) >>> username_field.value_from_object(alice) u’alice’ >>> username_field.value_from_object(bob) u’bob’
  • 275. Model instance lifecycle
  • 276. Model instance lifecycle Instantiation calls __init__()
  • 277. Model instance lifecycle Instantiation calls __init__() django.db.models.signals.pre_init sent
  • 278. Model instance lifecycle Instantiation calls __init__() django.db.models.signals.pre_init sent Field values initialized (if creating new object with values, or retrieving existing object from the database)
  • 279. Model instance lifecycle Instantiation calls __init__() django.db.models.signals.pre_init sent Field values initialized (if creating new object with values, or retrieving existing object from the database) django.db.models.signals.post_init sent
  • 280. Model instance lifecycle
  • 281. Model instance lifecycle Save data by calling save()…
  • 282. Model instance lifecycle Save data by calling save()… …which calls base_save()
  • 283. Model instance lifecycle Save data by calling save()… …which calls base_save() base_save() figures out whether to do INSERT or UPDATE query and which DB to use, and saves the data
  • 284. Model instance lifecycle Save data by calling save()… …which calls base_save() base_save() figures out whether to do INSERT or UPDATE query and which DB to use, and saves the data save() and save_base() are separate for reasons of API cleanliness
  • 285. Model instance lifecycle
  • 286. Model instance lifecycle Delete data by calling delete()
  • 287. Model instance lifecycle Delete data by calling delete() Figures out which related objects need deletion (ensures integrity even on DBs which don’t support it natively)
  • 288. Customization points
  • 289. Customization points Override save() or delete()
  • 290. Customization points Override save() or delete() Listen for signals sent during model life cycle
  • 291. Overriding save()
  • 292. Overriding save() # This is wrong def save(self): # This worked on 1.1 def save(self, force_insert=False, force_update=False): # This works on 1.2 def save(self, force_insert=False, force_update=False, using=None): # This is how you should actually do it: def save(self, *args, **kwargs): # Do your custom stuff # Always call parent save() at some point super(SomeModel, self).save(*args, **kwargs)
  • 293. Overriding delete()
  • 294. Overriding delete() # This worked in 1.1 def delete(self): # This works in 1.2 def delete(self, using=None): # Once again, you should do def delete(self, *args, **kwargs): # Do your custom stuff # Call parent delete() super(SomeModel, self).delete(*args, **kwargs)
  • 295. Overriding delete()
  • 296. Overriding delete() You can skip the parent class’ delete() method if you don’t want to actually delete data
  • 297. Overriding delete() You can skip the parent class’ delete() method if you don’t want to actually delete data Useful for implementing “delete with undo”: pair with a custom manager which hides “deleted” objects
  • 298. Don’t override __init__(). No, really. Don’t.
  • 299. Useful signals
  • 300. Useful signals All live in django.db.models.signals
  • 301. Useful signals All live in django.db.models.signals pre_init and post_init sent at beginning/end of __init__()
  • 302. Useful signals All live in django.db.models.signals pre_init and post_init sent at beginning/end of __init__() pre_save and post_save sent at beginning/end of save_base()
  • 303. Useful signals All live in django.db.models.signals pre_init and post_init sent at beginning/end of __init__() pre_save and post_save sent at beginning/end of save_base() pre_delete and post_delete sent at beginning/end of delete()
  • 304. The model cache
  • 305. The model cache Lives in django.db.models.loading
  • 306. The model cache Lives in django.db.models.loading Tracks all models from all installed applications
  • 307. The model cache Lives in django.db.models.loading Tracks all models from all installed applications Prevents certain issues with duplicate copies of model classes
  • 308. The model cache Lives in django.db.models.loading Tracks all models from all installed applications Prevents certain issues with duplicate copies of model classes Provides a generic way to get models
  • 309. How models are tracked
  • 310. How models are tracked Combination of application “label” and model name
  • 311. How models are tracked Combination of application “label” and model name django.contrib.auth.models.User ➟ “auth”, “user”
  • 312. How models are tracked Combination of application “label” and model name django.contrib.auth.models.User ➟ “auth”, “user” These exist on the model’s Options instance as the attributes app_label and module_name
  • 313. In action
  • 314. In action >>> from django.db.models.loading import cache >>> user_model = cache.get_model(‘auth’, ‘user’) >>> user_model <class 'django.contrib.auth.models.User'> >>> auth_app = cache.get_app('auth') >>> cache.get_models(auth_app) [<class 'django.contrib.auth.models.Permission'>, <class 'django.contrib.auth.models.Group'>, <class 'django.contrib.auth.models.User'>, <class 'django.contrib.auth.models.Message'>]
  • 315. Useful methods
  • 316. Useful methods get_model(app_label, model_name) -- returns a model class
  • 317. Useful methods get_model(app_label, model_name) -- returns a model class get_app(app_label) -- returns that application’s models module
  • 318. Useful methods get_model(app_label, model_name) -- returns a model class get_app(app_label) -- returns that application’s models module get_models(app) -- given models module, returns all model classes in it
  • 319. Useful methods get_model(app_label, model_name) -- returns a model class get_app(app_label) -- returns that application’s models module get_models(app) -- given models module, returns all model classes in it get_models() -- returns all installed models
  • 320. Generic querying
  • 321. Generic querying >>> from django.db.models.loading import cache >>> model_str = “some_app.some_model” >>> mod = cache.get_model(*model_str.split(‘.’)) >>> objects = mod._default_manager.all() # etc.
  • 322. It’s everywhere
  • 323. It’s everywhere AUTH_PROFILE_MODULE takes an app_label.model_name string
  • 324. It’s everywhere AUTH_PROFILE_MODULE takes an app_label.model_name string django.contrib.contenttypes uses app_label/ model_name pairs to track models
  • 325. It’s everywhere AUTH_PROFILE_MODULE takes an app_label.model_name string django.contrib.contenttypes uses app_label/ model_name pairs to track models Admin uses app_label/model_name pairs to identify models from URLs
  • 326. It’s everywhere AUTH_PROFILE_MODULE takes an app_label.model_name string django.contrib.contenttypes uses app_label/ model_name pairs to track models Admin uses app_label/model_name pairs to identify models from URLs etc., etc.
  • 327. Model fields
  • 328. Model fields Most live in django.db.models
  • 329. Model fields Most live in django.db.models Some bundled apps include more field types
  • 330. Model fields Most live in django.db.models Some bundled apps include more field types Each field type represents a particular type of data and optionally constraints on values of that type
  • 331. Under the hood: data types
  • 332. Under the hood: data types get_internal_type() can return the name of a built-in field type; DB-level data type will be same as that field type
  • 333. Under the hood: data types get_internal_type() can return the name of a built-in field type; DB-level data type will be same as that field type db_type() can name a custom data type for use at the DB level
  • 334. Under the hood: conversion
  • 335. Under the hood: conversion to_python() converts a value from the DB to a Python value suitable for the field type
  • 336. Under the hood: conversion to_python() converts a value from the DB to a Python value suitable for the field type get_prep_value() converts a Python field value to (generic) DB format
  • 337. Under the hood: conversion to_python() converts a value from the DB to a Python value suitable for the field type get_prep_value() converts a Python field value to (generic) DB format get_db_prep_value() is like get_prep_value() but applies DB-specific quirks
  • 338. Under the hood: querying
  • 339. Under the hood: querying get_prep_lookup() converts a value to the correct (generic) format for a particular type of query (and is given the lookup type as an argument)
  • 340. Under the hood: querying get_prep_lookup() converts a value to the correct (generic) format for a particular type of query (and is given the lookup type as an argument) get_db_prep_lookup() is similar, but applies DB- specific quirks
  • 341. Under the hood: saving
  • 342. Under the hood: saving pre_save() can do generic preprocessing
  • 343. Under the hood: saving pre_save() can do generic preprocessing get_db_prep_save() allows DB-specific formatting of data to be saved
  • 344. Miscellany
  • 345. Miscellany formfield() returns a form field class suitable for this field type
  • 346. Miscellany formfield() returns a form field class suitable for this field type value_to_string() converts the field value to a string for serializers
  • 347. Writing your own
  • 348. Writing your own From scratch: subclass django.db.models.Field
  • 349. Writing your own From scratch: subclass django.db.models.Field Use django.db.models.SubfieldBase as the metaclass
  • 350. Writing your own From scratch: subclass django.db.models.Field Use django.db.models.SubfieldBase as the metaclass Override any methods you need
  • 351. Writing your own From scratch: subclass django.db.models.Field Use django.db.models.SubfieldBase as the metaclass Override any methods you need Full documentation: http://docs.djangoproject.com/en/dev/ howto/custom-model-fields/
  • 352. Writing your own
  • 353. Writing your own Subclassing an existing field: just do it
  • 354. Writing your own Subclassing an existing field: just do it Override methods if you like
  • 355. Writing your own Subclassing an existing field: just do it Override methods if you like Or don’t
  • 356. A powerful use case
  • 357. A powerful use case class PubDateField(models.DateField): pass def get_publication_date(obj): opts = obj._meta pub_date_field = None for field in opts.fields: if isinstance(field, PubDateField): pub_date_field = field break return pub_date_field.value_from_object(obj)
  • 358. Model validation
  • 359. Model validation New in Django 1.2
  • 360. Model validation New in Django 1.2 Three flavors: field-level, instance-level, uniqueness checks
  • 361. Model validation New in Django 1.2 Three flavors: field-level, instance-level, uniqueness checks Run the whole suite by calling a model instance’s full_clean() method
  • 362. clean_fields()
  • 363. clean_fields() Each model field defines a clean() method
  • 364. clean_fields() Each model field defines a clean() method Additional validation can be added to a field in the model definition
  • 365. clean()
  • 366. clean() Instance-level validation
  • 367. clean() Instance-level validation Called after clean_fields()
  • 368. clean() Instance-level validation Called after clean_fields() Can perform validation involving multiple fields simultaneously
  • 369. validate_unique()
  • 370. validate_unique() Applies unique declarations on fields, unique_for_* declarations and unique_together in Meta
  • 371. validate_unique() Applies unique declarations on fields, unique_for_* declarations and unique_together in Meta Last step in the validation chain
  • 372. full_clean()
  • 373. full_clean() Raises django.core.exceptions.ValidationError if instance is invalid
  • 374. full_clean() Raises django.core.exceptions.ValidationError if instance is invalid Must be called manually; model saving does not implicitly validate
  • 375. Inheritance
  • 376. Inheritance Abstract/concrete
  • 377. Inheritance Abstract/concrete DB-level
  • 378. Inheritance Abstract/concrete DB-level Python-level
  • 379. Abstract models
  • 380. Abstract models Create an abstract model by declaring abstract = True in Meta
  • 381. Abstract models Create an abstract model by declaring abstract = True in Meta No database table created
  • 382. Abstract models Create an abstract model by declaring abstract = True in Meta No database table created Subclasses, if not abstract, will generate table with both their own and the parent’s fields
  • 383. Abstract models Create an abstract model by declaring abstract = True in Meta No database table created Subclasses, if not abstract, will generate table with both their own and the parent’s fields Subclasses can subclass and override parent’s Meta
  • 384. Abstract models
  • 385. Abstract models Meta cannot declare some attributes (db_table, for example)
  • 386. Abstract models Meta cannot declare some attributes (db_table, for example) Special interpolation syntax for related_name
  • 387. Use cases
  • 388. Use cases Common field set (e.g., title, publication date, author, etc.)
  • 389. Use cases Common field set (e.g., title, publication date, author, etc.) Common methods
  • 390. Use cases Common field set (e.g., title, publication date, author, etc.) Common methods Common Meta declarations
  • 391. Use cases Common field set (e.g., title, publication date, author, etc.) Common methods Common Meta declarations Common custom managers
  • 392. DB-level inheritance
  • 393. DB-level inheritance No special mechanism: just subclass the model you want to inherit from
  • 394. DB-level inheritance No special mechanism: just subclass the model you want to inherit from Can’t directly subclass parent’s Meta, but can override with new declarations
  • 395. DB-level inheritance No special mechanism: just subclass the model you want to inherit from Can’t directly subclass parent’s Meta, but can override with new declarations Can add new managers (parent’s managers are not inherited)
  • 396. DB-level inheritance
  • 397. DB-level inheritance Always implemented as multi-table
  • 398. DB-level inheritance Always implemented as multi-table Subclass gets a table with implicitly-created one-to-one relation to parent (specify a OneToOneField with parent_link=True to manually control this)
  • 399. DB-level inheritance Always implemented as multi-table Subclass gets a table with implicitly-created one-to-one relation to parent (specify a OneToOneField with parent_link=True to manually control this) Parent/child relation can be traversed like any one-to-one relation
  • 400. Use cases
  • 401. Use cases Logical “is-a” relationships: a Restaurant “is-a” Place
  • 402. Use cases Logical “is-a” relationships: a Restaurant “is-a” Place And then only maybe (inheritance isn’t a great pattern)
  • 403. Python-level inheritance
  • 404. Python-level inheritance Create a “proxy” subclass: proxy = True in Meta
  • 405. Python-level inheritance Create a “proxy” subclass: proxy = True in Meta Will use parent’s database table
  • 406. Python-level inheritance Create a “proxy” subclass: proxy = True in Meta Will use parent’s database table Can add/override declarations in Meta
  • 407. Python-level inheritance Create a “proxy” subclass: proxy = True in Meta Will use parent’s database table Can add/override declarations in Meta Can add new managers (parent’s will be inherited as well)
  • 408. Python-level inheritance
  • 409. Python-level inheritance Must have exactly one non-abstract parent
  • 410. Python-level inheritance Must have exactly one non-abstract parent Can have any number of abstract parents…
  • 411. Python-level inheritance Must have exactly one non-abstract parent Can have any number of abstract parents… …but abstract parents cannot define fields
  • 412. Use cases
  • 413. Use cases Adding methods to existing models
  • 414. Use cases Adding methods to existing models Adding managers or changing Meta behavior
  • 415. Use cases Adding methods to existing models Adding managers or changing Meta behavior Far better solution than monkeypatching
  • 416. General caveats
  • 417. General caveats Querying a model always returns instances of that model, never instances of parents/children
  • 418. General caveats Querying a model always returns instances of that model, never instances of parents/children Subclasses can never override parent field definitions
  • 419. General caveats Querying a model always returns instances of that model, never instances of parents/children Subclasses can never override parent field definitions Abstract classes cannot be queried
  • 420. General caveats Querying a model always returns instances of that model, never instances of parents/children Subclasses can never override parent field definitions Abstract classes cannot be queried First parent to define a name “wins”
  • 421. When in doubt, don’t use inheritance
  • 422. Miscellaneous ORM features
  • 423. Unmanaged models
  • 424. Unmanaged models Declare managed = False in Meta
  • 425. Unmanaged models Declare managed = False in Meta Django doesn’t attempt to create or maintain a DB table
  • 426. Unmanaged models Declare managed = False in Meta Django doesn’t attempt to create or maintain a DB table Django doesn’t add an automatic primary-key field
  • 427. Unmanaged models Declare managed = False in Meta Django doesn’t attempt to create or maintain a DB table Django doesn’t add an automatic primary-key field Django doesn’t attempt to create many-to-many join tables
  • 428. Unmanaged models Declare managed = False in Meta Django doesn’t attempt to create or maintain a DB table Django doesn’t add an automatic primary-key field Django doesn’t attempt to create many-to-many join tables Useful for wrapping existing tables or, more often, DB-level views
  • 429. Generic relations
  • 430. Generic relations Live in django.contrib.contenttypes.generic
  • 431. Generic relations Live in django.contrib.contenttypes.generic Require django.contrib.contenttypes installed
  • 432. Generic relations Live in django.contrib.contenttypes.generic Require django.contrib.contenttypes installed Allow relation to any instance of any model
  • 433. How it works
  • 434. How it works Add a ForeignKey to django.contrib.contenttypes.models.ContentT ype
  • 435. How it works Add a ForeignKey to django.contrib.contenttypes.models.ContentT ype Add a field which can hold a primary-key value (usually IntegerField or TextField)
  • 436. How it works Add a ForeignKey to django.contrib.contenttypes.models.ContentT ype Add a field which can hold a primary-key value (usually IntegerField or TextField) Add a GenericForeignKey specifying the above field names as arguments
  • 437. How it works
  • 438. How it works class Tag(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.TextField() object = generic.GenericForeignKey(‘content_type’, ‘object_id’) tag = models.CharField(max_length=255) >>> jacob = User.objects.get(username=‘jacob’) >>> t = Tag(object=jacob, tag=‘bdfl’) >>> t.save() >>> t.object <User: jacob>
  • 439. How it works
  • 440. How it works The ContentType relation is used to determine the model class
  • 441. How it works The ContentType relation is used to determine the model class Then a primary-key lookup is done against that model
  • 442. How it works The ContentType relation is used to determine the model class Then a primary-key lookup is done against that model Querying by generic relation always requires both values explicitly: GenericForeignKey fields are not legal in filter(), get(), etc.
  • 443. Reverse generic relations
  • 444. Reverse generic relations Declare on the model class you’ll “point” to
  • 445. Reverse generic relations Declare on the model class you’ll “point” to Use generic.GenericRelation
  • 446. Reverse generic relations Declare on the model class you’ll “point” to Use generic.GenericRelation Sets up a reverse relationship attribute for easy queries
  • 447. Q expressions
  • 448. Q expressions django.db.models.Q
  • 449. Q expressions django.db.models.Q Use normal field-lookup syntax
  • 450. Q expressions django.db.models.Q Use normal field-lookup syntax Legal anywhere field-lookup syntax is (but must come as positional arguments)
  • 451. Q expressions django.db.models.Q Use normal field-lookup syntax Legal anywhere field-lookup syntax is (but must come as positional arguments) Combine with logical operators & and |
  • 452. Q expressions django.db.models.Q Use normal field-lookup syntax Legal anywhere field-lookup syntax is (but must come as positional arguments) Combine with logical operators & and | Negate with logical ~
  • 453. Q expressions django.db.models.Q Use normal field-lookup syntax Legal anywhere field-lookup syntax is (but must come as positional arguments) Combine with logical operators & and | Negate with logical ~ Allow complex lookups, reuse of query fragments
  • 454. F expressions
  • 455. F expressions django.db.models.F
  • 456. F expressions django.db.models.F Takes a field name, becomes a reference to that field name
  • 457. F expressions django.db.models.F Takes a field name, becomes a reference to that field name Usable as a lookup value in queries
  • 458. F expressions django.db.models.F Takes a field name, becomes a reference to that field name Usable as a lookup value in queries Allows self-referential or field-comparing queries
  • 459. extra()
  • 460. extra() Allows a bit of raw SQL in an otherwise-normal query
  • 461. extra() Allows a bit of raw SQL in an otherwise-normal query Can add extra attributes to returned model instances
  • 462. extra() Allows a bit of raw SQL in an otherwise-normal query Can add extra attributes to returned model instances Only occasionally useful
  • 463. Natural keys
  • 464. Natural keys Define the method natural_key() on the model class
  • 465. Natural keys Define the method natural_key() on the model class Should return an iterable
  • 466. Natural keys Define the method natural_key() on the model class Should return an iterable Example: ContentType returns app label/model name
  • 467. Natural keys
  • 468. Natural keys Serializers will use a natural key if the model defines it
  • 469. Natural keys Serializers will use a natural key if the model defines it ContentType and Permission both have custom managers which do lookups by natural keys
  • 470. Natural keys
  • 471. Natural keys # Normal querying Permission.objects.get(code_name=‘change’, content_type__app_label=‘auth’, content_type__model=‘user’) # Using natural key Permission.objects.get_by_natural_key(‘change’, ‘auth’, ‘user’)
  • 472. Questions?
  • 473. The forms library
  • 474. Major parts
  • 475. Major parts Forms
  • 476. Major parts Forms Fields
  • 477. Major parts Forms Fields Widgets
  • 478. Major parts Forms Fields Widgets Form-generation utilities
  • 479. Major parts Forms Fields Widgets Form-generation utilities Media support
  • 480. Widgets
  • 481. Widgets Low-level display and parsing
  • 482. Widgets Low-level display and parsing Know how to render HTML
  • 483. Widgets Low-level display and parsing Know how to render HTML Know how to pull data out of a submission
  • 484. Widgets
  • 485. Widgets One for each type of HTML form control: Textarea, PasswordInput, etc.
  • 486. Widgets One for each type of HTML form control: Textarea, PasswordInput, etc. Can also bundle arbitrary media (CSS, JavaScript) to include and use for display
  • 487. Important methods
  • 488. Important methods render(self, name, value, attrs=None)
  • 489. Important methods render(self, name, value, attrs=None) Returns HTML (as a Unicode string) to represent the widget
  • 490. Important methods render(self, name, value, attrs=None) Returns HTML (as a Unicode string) to represent the widget attrs is a dict of HTML attributes to render
  • 491. Important methods render(self, name, value, attrs=None) Returns HTML (as a Unicode string) to represent the widget attrs is a dict of HTML attributes to render value is not guaranteed to be valid!
  • 492. Important methods
  • 493. Important methods value_from_datadict(self, data, files, name)
  • 494. Important methods value_from_datadict(self, data, files, name) Return the value input into this widget’s HTML control
  • 495. Important methods value_from_datadict(self, data, files, name) Return the value input into this widget’s HTML control data and files are the POST or GET and FILES dicts from an HTTP request
  • 496. Important methods
  • 497. Important methods id_for_label(self, id_)
  • 498. Important methods id_for_label(self, id_) Returns an HTML ID to use in tying the widget to a label element.
  • 499. Important methods
  • 500. Important methods build_attrs(self, extra_attrs=None, **kwargs)
  • 501. Important methods build_attrs(self, extra_attrs=None, **kwargs) Combines the widget instance’s existing attributes with any extra attributes specified by other means
  • 502. Important methods build_attrs(self, extra_attrs=None, **kwargs) Combines the widget instance’s existing attributes with any extra attributes specified by other means Not useful to override, but useful in other methods (e.g., render())
  • 503. MultiWidget
  • 504. MultiWidget Special-purpose widget, provides a wrapper around multiple other widgets
  • 505. MultiWidget Special-purpose widget, provides a wrapper around multiple other widgets Example: combining date and time widgets for a datetime input
  • 506. MultiWidget
  • 507. MultiWidget value argument to render() can be a list of values, one per wrapped widget
  • 508. MultiWidget value argument to render() can be a list of values, one per wrapped widget Or a single value; decompress() will be called to unpack into multiple values
  • 509. MultiWidget value argument to render() can be a list of values, one per wrapped widget Or a single value; decompress() will be called to unpack into multiple values Example: SplitDateTimeWidget.decompress() turns a datetime.datetime into separate date and time values
  • 510. Fields
  • 511. Fields Represent data type and validation constraints
  • 512. Fields Represent data type and validation constraints Have associated widgets for rendering
  • 513. Fields Represent data type and validation constraints Have associated widgets for rendering Can perform validation and return values of appropriate types
  • 514. Fields Represent data type and validation constraints Have associated widgets for rendering Can perform validation and return values of appropriate types Can be arbitrarily specialized (or generic)
  • 515. Changes in 1.2
  • 516. Changes in 1.2 Model-level validation implemented
  • 517. Changes in 1.2 Model-level validation implemented Also changed internals of form field validation
  • 518. Changes in 1.2 Model-level validation implemented Also changed internals of form field validation Backwards-compatible: old-style validation still works, but new-style is better
  • 519. Old-style field validation
  • 520. Old-style field validation clean(self, value)
  • 521. Old-style field validation clean(self, value) If value is valid, return it
  • 522. Old-style field validation clean(self, value) If value is valid, return it If not, raise django.forms.ValidationError with an appropriate message
  • 523. New-style field validation
  • 524. New-style field validation Three-step process
  • 525. New-style field validation Three-step process Convert value to appropriate Python type
  • 526. New-style field validation Three-step process Convert value to appropriate Python type Run field’s own validation
  • 527. New-style field validation Three-step process Convert value to appropriate Python type Run field’s own validation Run attached validators
  • 528. New-style field validation Three-step process Convert value to appropriate Python type Run field’s own validation Run attached validators django.core.exceptions.ValidationError (django.forms.ValidationError is an alias)
  • 529. Converting field values
  • 530. Converting field values to_python(self, value)
  • 531. Converting field values to_python(self, value) Converts value to the appropriate Python type for the field and returns it
  • 532. Converting field values to_python(self, value) Converts value to the appropriate Python type for the field and returns it Raises ValidationError if the value can’t be converted
  • 533. Field-level validation
  • 534. Field-level validation validate(self, value)
  • 535. Field-level validation validate(self, value) value has already been converted by to_python()
  • 536. Field-level validation validate(self, value) value has already been converted by to_python() If value is valid, do nothing
  • 537. Field-level validation validate(self, value) value has already been converted by to_python() If value is valid, do nothing If not, raise ValidationError
  • 538. Validators
  • 539. Validators Additional validation constraints, can be arbitrarily attached at any time
  • 540. Validators Additional validation constraints, can be arbitrarily attached at any time All validators attached to a field will be run during validation
  • 541. Validators
  • 542. Validators A validator is a callable which takes a single argument (value)
  • 543. Validators A validator is a callable which takes a single argument (value) Raises ValidationError if invalid
  • 544. Validators A validator is a callable which takes a single argument (value) Raises ValidationError if invalid Built-in validators in django.core.validators
  • 545. Validators
  • 546. Validators def require_pony(value): if ‘pony’ not in value: raise ValidationError(“A pony is required”) # In a form: pony = models.CharField(validators=[require_pony])
  • 547. Validators on a field
  • 548. Validators on a field run_validators(self, value)
  • 549. Validators on a field run_validators(self, value) Iterates over self.validators, calling each
  • 550. Validators on a field run_validators(self, value) Iterates over self.validators, calling each Traps ValidationError to collect error messages
  • 551. Validators on a field run_validators(self, value) Iterates over self.validators, calling each Traps ValidationError to collect error messages Raises a new ValidationError, with all error messages, if needed
  • 552. Choosing a validation approach
  • 553. to_python()
  • 554. to_python() When the validation constraint is tied to the data type
  • 555. to_python() When the validation constraint is tied to the data type Most commonly: requiring a number
  • 556. validate()
  • 557. validate() When the constraint is intrinsic to the field type
  • 558. validate() When the constraint is intrinsic to the field type Choice-based fields usually need to do this
  • 559. Validators
  • 560. Validators Any other type of validation
  • 561. Validators Any other type of validation Most of the time you can re-use a built-in
  • 562. Validators Any other type of validation Most of the time you can re-use a built-in If not, still less code than a custom field
  • 563. Don’t write new code using clean() on a field class
  • 564. Error messages
  • 565. Error messages For custom validation, supply your own
  • 566. Error messages For custom validation, supply your own raise ValidationError(“No you can’t have a pony”)
  • 567. Error messages
  • 568. Error messages Each field class also defines standard error messages
  • 569. Error messages Each field class also defines standard error messages These are combined with any additional messages passed when a field instance is created
  • 570. Error messages Each field class also defines standard error messages These are combined with any additional messages passed when a field instance is created Stored in the attribute error_messages (a dictionary) on the instance
  • 571. Error messages Each field class also defines standard error messages These are combined with any additional messages passed when a field instance is created Stored in the attribute error_messages (a dictionary) on the instance Standard keys: invalid, required (fields can define others)
  • 572. Error messages
  • 573. Error messages error_messages = { ‘invalid’: “No you can’t have a pony”, ‘required’: “You must supply your own pony”, } pony = forms.CharField(error_messages=error_messages)
  • 574. Fields and widgets
  • 575. Fields and widgets Each field defines two default widget classes
  • 576. Fields and widgets Each field defines two default widget classes widget is the standard widget
  • 577. Fields and widgets Each field defines two default widget classes widget is the standard widget hidden_widget is used for a hidden field
  • 578. Fields and widgets Each field defines two default widget classes widget is the standard widget hidden_widget is used for a hidden field Can be overridden per-instance with the keyword argument widget
  • 579. Fields and widgets
  • 580. Fields and widgets widget_attrs(self, widget)
  • 581. Fields and widgets widget_attrs(self, widget) Given a widget instance, return attributes which should be applied
  • 582. Other important bits
  • 583. Other important bits Keyword arguments when instantiating a field
  • 584. Other important bits Keyword arguments when instantiating a field required (boolean)
  • 585. Other important bits Keyword arguments when instantiating a field required (boolean) label (used as HTML label element)
  • 586. Other important bits Keyword arguments when instantiating a field required (boolean) label (used as HTML label element) help_text (additional longer explanation of field’s requirements)
  • 587. Fields for models
  • 588. Fields for models ModelChoiceField and ModelMultipleChoiceField
  • 589. Fields for models ModelChoiceField and ModelMultipleChoiceField Correspond to ForeignKey/OneToOneField and ManyToManyField
  • 590. Fields for models ModelChoiceField and ModelMultipleChoiceField Correspond to ForeignKey/OneToOneField and ManyToManyField Accept a QuerySet from which choices are drawn
  • 591. Fields for models ModelChoiceField and ModelMultipleChoiceField Correspond to ForeignKey/OneToOneField and ManyToManyField Accept a QuerySet from which choices are drawn Return the chosen object(s)
  • 592. MultiValueField
  • 593. MultiValueField Like MultiWidget, “wraps” multiple fields as a single logical unit
  • 594. MultiValueField Like MultiWidget, “wraps” multiple fields as a single logical unit MultiWidget has a decompress() method, MultiValueField has compress()
  • 595. MultiValueField Like MultiWidget, “wraps” multiple fields as a single logical unit MultiWidget has a decompress() method, MultiValueField has compress() Standard example: SplitDateTimeField
  • 596. Localization (pre-1.2)
  • 597. Localization (pre-1.2) Date- and time-based fields can be localized
  • 598. Localization (pre-1.2) Date- and time-based fields can be localized Pass the keyword argument input_formats
  • 599. Localization (pre-1.2) Date- and time-based fields can be localized Pass the keyword argument input_formats List of time.strptime format strings
  • 600. Localization (pre-1.2) Date- and time-based fields can be localized Pass the keyword argument input_formats List of time.strptime format strings Or specify in settings: DATE_INPUT_FORMATS, DATETIME_INPUT_FORMATS and TIME_INPUT_FORMATS
  • 601. Localization changes
  • 602. Localization changes New in Django 1.2: locale support includes localized data formats
  • 603. Localization changes New in Django 1.2: locale support includes localized data formats django.utils.formats
  • 604. Localization changes New in Django 1.2: locale support includes localized data formats django.utils.formats Dates, times, number formatting, calendaring
  • 605. Localization changes New in Django 1.2: locale support includes localized data formats django.utils.formats Dates, times, number formatting, calendaring Form fields automatically pull this from active locale
  • 606. Forms
  • 607. Forms Pull everything together
  • 608. Forms Pull everything together Fields
  • 609. Forms Pull everything together Fields Validation
  • 610. Forms Pull everything together Fields Validation Error handling
  • 611. Forms Pull everything together Fields Validation Error handling Rendering
  • 612. BaseForm
  • 613. BaseForm Base class for all forms
  • 614. BaseForm Base class for all forms Default field set for each class stored in the dictionary base_fields
  • 615. BaseForm Base class for all forms Default field set for each class stored in the dictionary base_fields __init__() sets up the dictionary fields, unique to each instance
  • 616. base_fields vs. fields
  • 617. base_fields vs. fields Altering base_fields changes every instance of that form class
  • 618. base_fields vs. fields Altering base_fields changes every instance of that form class Altering fields changes only that specific instance
  • 619. Building a form the hard way
  • 620. Building a form the hard way base_fields = { ‘name’: forms.CharField(max_length=255), ‘email’: forms.EmailField(), ‘message’: forms.CharField(widget=forms.Textarea), } ContactForm = type(‘ContactForm’, (forms.BaseForm,), {‘base_fields’: base_fields}) # Now you can use it... contact_form = ContactForm()
  • 621. Form
  • 622. Form Declare fields the same way as on models
  • 623. Form Declare fields the same way as on models Uses a metaclass (django.forms.DeclarativeFieldsMetaclass) to turn this into the base_fields dictionary
  • 624. Building a form the easy way
  • 625. Building a form the easy way class ContactForm(forms.Form): name = forms.CharField(max_length=255) email = forms.EmailField() message = forms.CharField(widget=forms.Textarea)
  • 626. Binding data
  • 627. Binding data Instantiate the form class with some data
  • 628. Binding data Instantiate the form class with some data form = SomeForm(data=request.POST)
  • 629. Bound fields
  • 630. Bound fields A form with data wraps its fields in BoundField instances
  • 631. Bound fields A form with data wraps its fields in BoundField instances Each BoundField has a field instance, a reference to the form, and the field’s name within the form
  • 632. Bound fields A form with data wraps its fields in BoundField instances Each BoundField has a field instance, a reference to the form, and the field’s name within the form Iterating a form instance yields the BoundField instances
  • 633. Form validation
  • 634. Form validation Call the form instance’s is_valid() method
  • 635. Form validation Call the form instance’s is_valid() method Short-circuit: an unbound form is never valid
  • 636. How form validation works
  • 637. How form validation works Fields are validated (_clean_fields())
  • 638. How form validation works Fields are validated (_clean_fields()) Form as a whole is validated (_clean_form())
  • 639. How form validation works Fields are validated (_clean_fields()) Form as a whole is validated (_clean_form()) If valid, the form instance gets an attribute called cleaned_data
  • 640. How form validation works Fields are validated (_clean_fields()) Form as a whole is validated (_clean_form()) If valid, the form instance gets an attribute called cleaned_data If not, an attribute called errors
  • 641. _clean_fields()
  • 642. _clean_fields() Loops over self.fields
  • 643. _clean_fields() Loops over self.fields Field’s widget.value_from_datadict() retrieves the data
  • 644. _clean_fields() Loops over self.fields Field’s widget.value_from_datadict() retrieves the data Field’s clean() called
  • 645. _clean_fields() Loops over self.fields Field’s widget.value_from_datadict() retrieves the data Field’s clean() called Custom validation on the form is called
  • 646. Custom validation
  • 647. Custom validation Method on the form, named clean_<fieldname>()
  • 648. Custom validation Method on the form, named clean_<fieldname>() Has access to form’s cleaned_data
  • 649. Custom validation Method on the form, named clean_<fieldname>() Has access to form’s cleaned_data Not called if the field’s own clean() already raised a validation error
  • 650. _clean_form()
  • 651. _clean_form() Calls form’s clean() method
  • 652. _clean_form() Calls form’s clean() method Implement clean() to do form-level validation (comparing multiple fields, etc.)
  • 653. _clean_form() Calls form’s clean() method Implement clean() to do form-level validation (comparing multiple fields, etc.) Can access cleaned_data
  • 654. _clean_form() Calls form’s clean() method Implement clean() to do form-level validation (comparing multiple fields, etc.) Can access cleaned_data Field values not guaranteed to be in there, though
  • 655. Form-level errors
  • 656. Form-level errors Raised by form’s clean()
  • 657. Form-level errors Raised by form’s clean() Special key in errors dictionary: __all__
  • 658. Form-level errors Raised by form’s clean() Special key in errors dictionary: __all__ Accessible via non_field_errors()
  • 659. Errors
  • 660. Errors Stored in an instance of django.forms.util.ErrorDict
  • 661. Errors Stored in an instance of django.forms.util.ErrorDict Values in an ErrorDict are instances of django.forms.util.ErrorList
  • 662. Errors Stored in an instance of django.forms.util.ErrorDict Values in an ErrorDict are instances of django.forms.util.ErrorList Both ErrorDict and ErrorList know how to print themselves as HTML
  • 663. Form display
  • 664. Form display Default string representation of a form instance is as an HTML table (as_table())
  • 665. Form display Default string representation of a form instance is as an HTML table (as_table()) Also available: as_ul(), as_p()
  • 666. Form display Default string representation of a form instance is as an HTML table (as_table()) Also available: as_ul(), as_p() as_table() and as_ul() do not output the containing table or list element
  • 667. Form display Default string representation of a form instance is as an HTML table (as_table()) Also available: as_ul(), as_p() as_table() and as_ul() do not output the containing table or list element None of these output wrapping form element or submit buttons
  • 668. Form display
  • 669. Form display _html_output()
  • 670. Form display _html_output() Arguments are strings with placeholders
  • 671. Form display _html_output() Arguments are strings with placeholders Appropriate field attributes and error messages interpolated in
  • 672. Fine-tuning display
  • 673. Fine-tuning display Iterate over the form, get BoundField instances (in order of definition)
  • 674. Fine-tuning display Iterate over the form, get BoundField instances (in order of definition) Or use dictionary-style access to the form to get specific (bound) field instances
  • 675. Fun with BoundField
  • 676. Fun with BoundField By default, uses the field’s defined widget
  • 677. Fun with BoundField By default, uses the field’s defined widget as_text() to get <input type=”text”>
  • 678. Fun with BoundField By default, uses the field’s defined widget as_text() to get <input type=”text”> as_textarea() to get <textarea>
  • 679. Fun with BoundField By default, uses the field’s defined widget as_text() to get <input type=”text”> as_textarea() to get <textarea> as_hidden() to get <input type=”hidden”>
  • 680. Fun with BoundField By default, uses the field’s defined widget as_text() to get <input type=”text”> as_textarea() to get <textarea> as_hidden() to get <input type=”hidden”> as_widget() to get whatever widget you want
  • 681. Other useful display tricks
  • 682. Other useful display tricks is_multipart() tells whether you need to handle file uploads
  • 683. Other useful display tricks is_multipart() tells whether you need to handle file uploads visible_fields() gives all non-hidden fields
  • 684. Other useful display tricks is_multipart() tells whether you need to handle file uploads visible_fields() gives all non-hidden fields hidden_fields() gives all hidden fields
  • 685. Forms for models
  • 686. ModelForm
  • 687. ModelForm Base class is django.forms.models.BaseModelForm
  • 688. ModelForm Base class is django.forms.models.BaseModelForm django.forms.ModelForm uses metaclass django.forms.models.ModelFormMetaclass
  • 689. ModelFormMetaclass
  • 690. ModelFormMetaclass Parses the Meta declaration
  • 691. ModelFormMetaclass Parses the Meta declaration Turns it into an instance of django.forms.models.ModelFormOptions
  • 692. ModelFormMetaclass Parses the Meta declaration Turns it into an instance of django.forms.models.ModelFormOptions Stores it as the attribute _meta of the form class
  • 693. ModelFormOptions
  • 694. ModelFormOptions Stores model class, fields to include and fields to exclude
  • 695. ModelFormOptions Stores model class, fields to include and fields to exclude New in Django 1.2: stores dictionary of field names and widget instances to override default widgets
  • 696. Mapping model fields
  • 697. Mapping model fields Each model field class defines a method formfield()
  • 698. Mapping model fields Each model field class defines a method formfield() Override by defining the method formfield_callback() on the form class
  • 699. formfield_callback()
  • 700. formfield_callback() Receives the model field class and any defined widget from the form class
  • 701. formfield_callback() Receives the model field class and any defined widget from the form class Must return an instance of a form field class
  • 702. Getting field values
  • 703. Getting field values django.forms.models.model_to_dict()
  • 704. Getting field values django.forms.models.model_to_dict() Uses model field’s value_from_object() method
  • 705. Getting field values django.forms.models.model_to_dict() Uses model field’s value_from_object() method Special-case handling for ManyToManyField
  • 706. Validation
  • 707. Validation Uses model’s own validation routines
  • 708. Validation Uses model’s own validation routines Excludes model fields not represented in the form
  • 709. Validation Uses model’s own validation routines Excludes model fields not represented in the form Excludes model fields which already have form-level errors
  • 710. Saving
  • 711. Saving django.forms.models.save_instance() and django.forms.models.construct_instance()
  • 712. Saving django.forms.models.save_instance() and django.forms.models.construct_instance() Uses model fields’ save_form_data() method
  • 713. Saving django.forms.models.save_instance() and django.forms.models.construct_instance() Uses model fields’ save_form_data() method Defers file-based fields until other fields have been set up (to allow dynamic upload_to based on other field values)
  • 714. Form media
  • 715. The Media class
  • 716. The Media class django.forms.widgets.Media
  • 717. The Media class django.forms.widgets.Media Two attributes store information about media to include
  • 718. CSS
  • 719. CSS Stored in the attribute css, which is a dictionary
  • 720. CSS Stored in the attribute css, which is a dictionary Keys are CSS media names (all, screen, etc.)
  • 721. CSS Stored in the attribute css, which is a dictionary Keys are CSS media names (all, screen, etc.) Values are paths to stylesheets
  • 722. CSS
  • 723. CSS Handled by Media.add_css()
  • 724. CSS Handled by Media.add_css() Uses setdefault and a check against existing values to avoid duplicates
  • 725. JavaScript
  • 726. JavaScript Stored in the attribute js, which is a tuple
  • 727. JavaScript Stored in the attribute js, which is a tuple Items are paths to JavaScript files
  • 728. JavaScript
  • 729. JavaScript Handled by Media.add_js()
  • 730. JavaScript Handled by Media.add_js() Does checking against existing declarations to avoid duplicates
  • 731. Bundling media
  • 732. Bundling media Works on widget classes and form classes
  • 733. Bundling media Works on widget classes and form classes Define an inner class named Media, with the appropriate attributes
  • 734. Example
  • 735. Example class MyWidget(forms.TextInput): class Media: js = (‘foo.js’, ‘bar.js’)
  • 736. How it works
  • 737. How it works Widgets have a metaclass (django.forms.widgets.MediaDefiningClass) which parses the Media declaration
  • 738. How it works Widgets have a metaclass (django.forms.widgets.MediaDefiningClass) which parses the Media declaration For forms, DeclarativeFieldsMetaclass does the same
  • 739. Media paths
  • 740. Media paths Can be full URLs, including domain
  • 741. Media paths Can be full URLs, including domain Can be relative URLs starting with ‘/’
  • 742. Media paths Can be full URLs, including domain Can be relative URLs starting with ‘/’ Can be file names; settings.MEDIA_URL will be prepended to generate the full URL
  • 743. Media paths Can be full URLs, including domain Can be relative URLs starting with ‘/’ Can be file names; settings.MEDIA_URL will be prepended to generate the full URL Media.absolute_path() has the logic for this
  • 744. Rendering
  • 745. Rendering String representation of a Media instance is the correct HTML
  • 746. Rendering String representation of a Media instance is the correct HTML Dictionary access works: some_form.media[‘js’]
  • 747. Rendering String representation of a Media instance is the correct HTML Dictionary access works: some_form.media[‘js’]
  • 748. Rendering
  • 749. Rendering render() spits out all media for the instance
  • 750. Rendering render() spits out all media for the instance render_css() does just the CSS
  • 751. Rendering render() spits out all media for the instance render_css() does just the CSS render_js() does just the JavaScript
  • 752. Rendering render() spits out all media for the instance render_css() does just the CSS render_js() does just the JavaScript __unicode__() just calls render()
  • 753. Rendering render() spits out all media for the instance render_css() does just the CSS render_js() does just the JavaScript __unicode__() just calls render() __getitem__() calls the appropriate rendering method
  • 754. Media and inheritance
  • 755. Media and inheritance By default, a subclass of an existing widget or form inherits the parent’s media definitions
  • 756. Media and inheritance By default, a subclass of an existing widget or form inherits the parent’s media definitions Specify extend = False in the subclass’ Media class to change this
  • 757. Combining media
  • 758. Combining media Simply add media instances
  • 759. Combining media Simply add media instances combined = form1.media + form2.media
  • 760. Combining media Simply add media instances combined = form1.media + form2.media Duplicate-checking is applied
  • 761. You can use Media outside of forms
  • 762. Make your own
  • 763. Make your own from django.forms.widgets import MediaDefiningClass class MyClass(object): __metaclass__ = MediaDefiningClass # Now you can define an inner ‘Media’ class on # subclasses class MyClassWithMedia(MyClass): class Media: css = {‘all’: ‘foobar.css’}
  • 764. Media isn’t really designed for direct instantiation
  • 765. Questions?
  • 766. The template system
  • 767. Major components
  • 768. Major components Templates
  • 769. Major components Templates Tag and filter libraries
  • 770. Major components Templates Tag and filter libraries Loaders
  • 771. Template loaders
  • 772. Template loaders Locate templates (wherever they might be)
  • 773. Template loaders Locate templates (wherever they might be) Compile raw template source into a Template instance
  • 774. Template loaders Locate templates (wherever they might be) Compile raw template source into a Template instance Base class: django.template.loader.BaseLoader
  • 775. Loading a template
  • 776. Loading a template load_template(self, template_name, template_dirs=None)
  • 777. Loading a template load_template(self, template_name, template_dirs=None) Returns a 2-tuple: (Template instance, origin)
  • 778. load_template()
  • 779. load_template() Default implementation calls load_template_source()
  • 780. load_template() Default implementation calls load_template_source() Most custom loaders should override that method
  • 781. load_template_source()
  • 782. load_template_source() Filesystem loader searches TEMPLATE_DIRS and returns the file path as the origin
  • 783. load_template_source() Filesystem loader searches TEMPLATE_DIRS and returns the file path as the origin App directories loader searches for templates directories in applications, returns the file path as origin
  • 784. load_template_source() Filesystem loader searches TEMPLATE_DIRS and returns the file path as the origin App directories loader searches for templates directories in applications, returns the file path as origin Egg loader does the same, but inside eggs (and returns an egg name as the origin)
  • 785. Other template languages
  • 786. Other template languages A “template” is really just an object defining the method render(self, context)
  • 787. Other template languages A “template” is really just an object defining the method render(self, context) Write a wrapper which implements that method
  • 788. Other template languages A “template” is really just an object defining the method render(self, context) Write a wrapper which implements that method And a loader which knows how to apply it
  • 789. The Template class
  • 790. The Template class django.template.Template
  • 791. The Template class django.template.Template Compiled from a string source
  • 792. The Template class django.template.Template Compiled from a string source Ultimate result is a wrapper around a list of django.template.Node instances
  • 793. Compiling a template
  • 794. Compiling a template django.template.Lexer breaks the source string into appropriate tokens (django.template.Token)
  • 795. Compiling a template django.template.Lexer breaks the source string into appropriate tokens (django.template.Token) django.template.Parser turns the tokens into Node instances and returns the NodeList
  • 796. Lexing
  • 797. Lexing Regex-based: django.template.tag_re
  • 798. Lexing Regex-based: django.template.tag_re Lexer uses defined constants to identify known syntax (tags, variables, etc.)
  • 799. Lexing Regex-based: django.template.tag_re Lexer uses defined constants to identify known syntax (tags, variables, etc.) Instantiate with source string and origin; Lexer.tokenize() returns list of Token instances
  • 800. Tokens
  • 801. Tokens django.template.Token
  • 802. Tokens django.template.Token Stores type and contents
  • 803. Tokens django.template.Token Stores type and contents Types are text, variable, comment and block
  • 804. Tokens django.template.Token Stores type and contents Types are text, variable, comment and block Token.split_contents() breaks up contents into a list for further use
  • 805. The parser
  • 806. The parser django.template.Parser
  • 807. The parser django.template.Parser Instantiate with a list of tokens
  • 808. The parser django.template.Parser Instantiate with a list of tokens parse() turns the tokens into a NodeList
  • 809. Node
  • 810. Node django.template.Node
  • 811. Node django.template.Node Everything in the template becomes an instance of a Node subclass
  • 812. Node django.template.Node Everything in the template becomes an instance of a Node subclass Node must define the method render() which takes a Context instance
  • 813. NodeList
  • 814. NodeList A list of Node instances
  • 815. NodeList A list of Node instances Renders by iterating its nodes, calling render() on each and concatenating the results
  • 816. Mapping tokens to nodes
  • 817. Mapping tokens to nodes Parser maps plain text and variables to TextNode and VariableNode
  • 818. Mapping tokens to nodes Parser maps plain text and variables to TextNode and VariableNode Comments are skipped
  • 819. Mapping tokens to nodes Parser maps plain text and variables to TextNode and VariableNode Comments are skipped All other tokens treated as tags and looked up by name
  • 820. Handling tags
  • 821. Handling tags Parser has a dictionary, tags, mapping tag names to compilation functions
  • 822. Handling tags Parser has a dictionary, tags, mapping tag names to compilation functions Loading new tag libraries updates this dictionary
  • 823. Handling tags Parser has a dictionary, tags, mapping tag names to compilation functions Loading new tag libraries updates this dictionary No namespacing (yet)! Second tag with same name will overwrite the first
  • 824. Built-in tags and filters
  • 825. Built-in tags and filters django.template.builtins is the list of default libraries to load
  • 826. Built-in tags and filters django.template.builtins is the list of default libraries to load django.template.add_to_builtins() can add new ones
  • 827. Built-in tags and filters django.template.builtins is the list of default libraries to load django.template.add_to_builtins() can add new ones By default, loads django.template.defaulttags and django.template.defaultfilters
  • 828. Tag loading
  • 829. Tag loading Rewritten for Django 1.2
  • 830. Tag loading Rewritten for Django 1.2 Previously, templatetags module in an app was added to django.templatetags.__path__
  • 831. Tag loading Rewritten for Django 1.2 Previously, templatetags module in an app was added to django.templatetags.__path__ Now, importlib is used to collect all modules which provide tag libraries
  • 832. Tag loading
  • 833. Tag loading Keyed by module name
  • 834. Tag loading Keyed by module name First location to have <app>.templatetags.<name> wins
  • 835. Parser tricks
  • 836. Parser tricks Tag compilation functions have access to the parser
  • 837. Parser tricks Tag compilation functions have access to the parser Can parse forward, back up, look for specific tags
  • 838. parse()
  • 839. parse() Optional argument parse_until
  • 840. parse() Optional argument parse_until List of tag names
  • 841. parse() Optional argument parse_until List of tag names Continues parsing until a token of that name is reached
  • 842. parse() Optional argument parse_until List of tag names Continues parsing until a token of that name is reached Parser.delete_first_token() will remove that token
  • 843. next_token()
  • 844. next_token() Returns the next token in the template
  • 845. next_token() Returns the next token in the template Used by for/empty, if/else, etc.
  • 846. skip_past()
  • 847. skip_past() Takes a tag name
  • 848. skip_past() Takes a tag name Parses to just past the next tag matching that name
  • 849. skip_past() Takes a tag name Parses to just past the next tag matching that name Example: endcomment
  • 850. Debugging
  • 851. Debugging DEBUG = True swaps out the lexer and parser
  • 852. Debugging DEBUG = True swaps out the lexer and parser django.template.debug.DebugParser and django.template.debug.DebugLexer
  • 853. Other parsing tricks
  • 854. Other parsing tricks Node classes can set must_be_first = True (e.g., for extends)
  • 855. Other parsing tricks Node classes can set must_be_first = True (e.g., for extends) Overridable enter_command and exit_command (debugging parser uses these)
  • 856. Variables
  • 857. Variables django.template.VariableNode
  • 858. Variables django.template.VariableNode Wraps an instance of django.template.FilterExpression
  • 859. FilterExpression
  • 860. FilterExpression Splits out variable name and any filters
  • 861. FilterExpression Splits out variable name and any filters Checks that all filter names are valid
  • 862. FilterExpression Splits out variable name and any filters Checks that all filter names are valid Wraps variable in a template.Variable instance if possible
  • 863. Variable
  • 864. Variable Actually resolves the variable in a given Context
  • 865. Variable Actually resolves the variable in a given Context Also understands gettext syntax and will apply translation when needed
  • 866. Context
  • 867. Context Behaves like a dictionary
  • 868. Context Behaves like a dictionary Is actually a stack of dictionaries
  • 869. Context Behaves like a dictionary Is actually a stack of dictionaries Fall-through lookup semantics: checks from top to bottom looking for variables
  • 870. Context tricks
  • 871. Context tricks push() adds a new dictionary on the top of the stack
  • 872. Context tricks push() adds a new dictionary on the top of the stack New variables are added to the topmost dictionary
  • 873. Context tricks push() adds a new dictionary on the top of the stack New variables are added to the topmost dictionary pop() removes the topmost dictionary
  • 874. RenderContext
  • 875. RenderContext New in 1.2: thread-safe storage of node rendering state
  • 876. RenderContext New in 1.2: thread-safe storage of node rendering state Only the topmost dictionary is checked for variable resolution
  • 877. RenderContext New in 1.2: thread-safe storage of node rendering state Only the topmost dictionary is checked for variable resolution Each Context has an attached RenderContext
  • 878. RenderContext New in 1.2: thread-safe storage of node rendering state Only the topmost dictionary is checked for variable resolution Each Context has an attached RenderContext New dictionary pushed on top at start of render(), popped at end
  • 879. Autoescaping
  • 880. Autoescaping By default, all variables are escaped
  • 881. Autoescaping By default, all variables are escaped safe filter turns this off case-by-case
  • 882. Autoescaping By default, all variables are escaped safe filter turns this off case-by-case autoescape tag turns it on/off for sections of a template
  • 883. Autoescaping By default, all variables are escaped safe filter turns this off case-by-case autoescape tag turns it on/off for sections of a template autoescape attribute of Context controls
  • 884. Questions?
  • 885. Request/response processing
  • 886. Request handlers
  • 887. Request handlers Base class django.core.handlers.base.BaseHandler
  • 888. Request handlers Base class django.core.handlers.base.BaseHandler Implement the request/response pipeline
  • 889. Request handlers Base class django.core.handlers.base.BaseHandler Implement the request/response pipeline One subclass for mod_python, one for WSGI
  • 890. Request handlers
  • 891. Request handlers Handler’s __call__() is the entry point for Django
  • 892. Request handlers Handler’s __call__() is the entry point for Django Loads middleware
  • 893. Request handlers Handler’s __call__() is the entry point for Django Loads middleware Initializes HttpRequest object
  • 894. Request handlers Handler’s __call__() is the entry point for Django Loads middleware Initializes HttpRequest object Calls handler’s get_response()
  • 895. Middleware loading
  • 896. Middleware loading Handler’s load_middleware() method
  • 897. Middleware loading Handler’s load_middleware() method Populates attributes containing request, response, view and exception middleware classes
  • 898. HttpRequest
  • 899. HttpRequest Base class django.http.HttpRequest
  • 900. HttpRequest Base class django.http.HttpRequest One subclass for mod_python, one subclass for WSGI
  • 901. HttpRequest Base class django.http.HttpRequest One subclass for mod_python, one subclass for WSGI mod_python: _req is the raw request object
  • 902. HttpRequest Base class django.http.HttpRequest One subclass for mod_python, one subclass for WSGI mod_python: _req is the raw request object WSGI: environ is the original WSGI environ
  • 903. get_response()
  • 904. get_response() Applies request middleware
  • 905. get_response() Applies request middleware Resolves URL
  • 906. get_response() Applies request middleware Resolves URL Applies view middleware
  • 907. get_response() Applies request middleware Resolves URL Applies view middleware Calls view
  • 908. get_response() Applies request middleware Resolves URL Applies view middleware Calls view Applies response middleware
  • 909. URL resolution
  • 910. URL resolution django.core.urlresolvers.set_urlconf() sets the (thread-local) URL configuration
  • 911. URL resolution django.core.urlresolvers.set_urlconf() sets the (thread-local) URL configuration django.core.urlresolvers.RegexURLResolver is the class used to perform resolution
  • 912. RegexURLResolver
  • 913. RegexURLResolver Instantiate with string name of a root URLconf
  • 914. RegexURLResolver Instantiate with string name of a root URLconf Call resolve() with a URL path to resolve
  • 915. RegexURLResolver Instantiate with string name of a root URLconf Call resolve() with a URL path to resolve Returns tuple of (view, positional args, keyword args)
  • 916. RegexURLResolver Instantiate with string name of a root URLconf Call resolve() with a URL path to resolve Returns tuple of (view, positional args, keyword args) Or raises django.core.urlresolvers.Resolver404
  • 917. RegexURLPattern
  • 918. RegexURLPattern Represents a single URL pattern
  • 919. RegexURLPattern Represents a single URL pattern resolve() method takes a path
  • 920. RegexURLPattern Represents a single URL pattern resolve() method takes a path Returns (view, args, kwargs) tuple if it matches
  • 921. Resolution errors
  • 922. Resolution errors Resolver404 is a subclass of django.http.Http404
  • 923. Resolution errors Resolver404 is a subclass of django.http.Http404 Handler will detect this and call resolver’s resolve404() method
  • 924. Resolution errors Resolver404 is a subclass of django.http.Http404 Handler will detect this and call resolver’s resolve404() method Works with Http404 raised from view, too
  • 925. 404 handlers
  • 926. 404 handlers Specified by root URLconf’s handler404 attribute
  • 927. 404 handlers Specified by root URLconf’s handler404 attribute Default is django.views.defaults.page_not_found
  • 928. Views
  • 929. Views Three requirements to qualify as a Django view
  • 930. Views Three requirements to qualify as a Django view Callable
  • 931. Views Three requirements to qualify as a Django view Callable Accepts an HttpRequest as first positional argument
  • 932. Views Three requirements to qualify as a Django view Callable Accepts an HttpRequest as first positional argument Returns an HttpResponse or raises an exception
  • 933. Simple views
  • 934. Simple views Just Python functions
  • 935. Simple views Just Python functions 95% of views “in the wild”
  • 936. Class-based views
  • 937. Class-based views Class whose instances define __call__()
  • 938. Class-based views Class whose instances define __call__() Various proposals for standardization
  • 939. Class-based views Class whose instances define __call__() Various proposals for standardization http://www.slideshare.net/simon/classbased-views-with- django
  • 940. Class-based views
  • 941. Class-based views Turn functionality into methods
  • 942. Class-based views Turn functionality into methods get_template(), get_context(), etc.
  • 943. Class-based views Turn functionality into methods get_template(), get_context(), etc. To change behavior, subclass and override
  • 944. Class-based views
  • 945. Class-based views One object can also be multiple views
  • 946. Class-based views One object can also be multiple views Can provide its own URL patterns too
  • 947. Class-based views One object can also be multiple views Can provide its own URL patterns too Admin does this
  • 948. HttpResponse
  • 949. HttpResponse Lives in django.http
  • 950. HttpResponse Lives in django.http No gateway-specific subclasses
  • 951. HttpResponse Lives in django.http No gateway-specific subclasses Handler converts HttpResponse to gateway-appropriate response mechanism
  • 952. HttpResponse
  • 953. HttpResponse Subclasses for HTTP status codes
  • 954. HttpResponse Subclasses for HTTP status codes 301, 302, 304, 400, 403, 404, 405, 410, 500
  • 955. HttpResponse Subclasses for HTTP status codes 301, 302, 304, 400, 403, 404, 405, 410, 500 Easy to write your own: override status_code
  • 956. Handling errors
  • 957. Handling errors Most exceptions will cause exception middleware to be applied
  • 958. Handling errors Most exceptions will cause exception middleware to be applied SystemExit is not caught
  • 959. Handling errors Most exceptions will cause exception middleware to be applied SystemExit is not caught Exceptions raised by exception middleware or 404 handler not apply exception middleware
  • 960. Handling errors
  • 961. Handling errors Handler’s handle_uncaught_exception()
  • 962. Handling errors Handler’s handle_uncaught_exception() Uses root URLconf’s handler500
  • 963. Handling errors Handler’s handle_uncaught_exception() Uses root URLconf’s handler500 Default error view: django.views.defaults.server_error
  • 964. Handling errors Handler’s handle_uncaught_exception() Uses root URLconf’s handler500 Default error view: django.views.defaults.server_error Deliberately uses empty Context
  • 965. Middleware
  • 966. Middleware Middleware methods can modify request/response and return None
  • 967. Middleware Middleware methods can modify request/response and return None Or return an HttpResponse directly (short-circuits all other request processing)
  • 968. Middleware Middleware methods can modify request/response and return None Or return an HttpResponse directly (short-circuits all other request processing) Or raise an exception (goes straight to error handling)
  • 969. Middleware calls
  • 970. Middleware calls process_request() called before URL resolution
  • 971. Middleware calls process_request() called before URL resolution process_view() called after URL resolution
  • 972. Middleware calls process_request() called before URL resolution process_view() called after URL resolution process_response() called after successful get_response()
  • 973. Middleware calls process_request() called before URL resolution process_view() called after URL resolution process_response() called after successful get_response() Exceptions can shortcut processing
  • 974. Request signals
  • 975. Request signals django.core.signals.request_started
  • 976. Request signals django.core.signals.request_started django.core.signals.request_finished
  • 977. Request signals django.core.signals.request_started django.core.signals.request_finished django.core.signals.got_request_exception
  • 978. Questions?
  • 979. The admin
  • 980. AdminSite
  • 981. AdminSite Represents an admin interface
  • 982. AdminSite Represents an admin interface Knows which models and actions are registered with it
  • 983. AdminSite Represents an admin interface Knows which models and actions are registered with it Can have multiple instances active in a single install
  • 984. AdminSite
  • 985. AdminSite urls delegates to get_urls()
  • 986. AdminSite urls delegates to get_urls() Auth checks (login, logout, etc.) implemented as methods
  • 987. ModelAdmin
  • 988. ModelAdmin Provides admin options
  • 989. ModelAdmin Provides admin options And acts as a set of class-based views
  • 990. ModelAdmin Provides admin options And acts as a set of class-based views Also uses MediaDefiningClass metaclass
  • 991. ModelAdmin
  • 992. ModelAdmin Class attributes for easy customization
  • 993. ModelAdmin Class attributes for easy customization Overridable templates
  • 994. ModelAdmin Class attributes for easy customization Overridable templates Overridable forms
  • 995. ModelAdmin Class attributes for easy customization Overridable templates Overridable forms Overridable form fields
  • 996. Overriding templates
  • 997. Overriding templates add_form_template change_form_template change_list_template delete_confirmation_template object_history_template
  • 998. Overriding forms
  • 999. Overriding forms Set form on the ModelAdmin
  • 1000. Overriding forms Set form on the ModelAdmin Or override get_form()
  • 1001. Overriding forms Set form on the ModelAdmin Or override get_form() Or override render_change_form() to control form rendering
  • 1002. Admin forms
  • 1003. Admin forms django.contrib.admin.helpers.AdminForm implements fieldsets
  • 1004. Admin forms django.contrib.admin.helpers.AdminForm implements fieldsets django.contrib.admin.widgets contains special- case widgets for certain fields
  • 1005. Overriding fields
  • 1006. Overriding fields Set fields or exclude
  • 1007. Overriding fields Set fields or exclude To control specific fields, define a custom form class
  • 1008. Overriding fields Set fields or exclude To control specific fields, define a custom form class Or define methods
  • 1009. Overriding fields
  • 1010. Overriding fields formfield_for_dbfield() formfield_for_choice_field() formfield_for_foreignkey() formfield_for_manytomany()
  • 1011. Permission control
  • 1012. Permission control has_add_permission() has_change_permission() has_delete_permission() get_model_perms() queryset()
  • 1013. Logging
  • 1014. Logging log_addition()
  • 1015. Logging log_addition() log_change()
  • 1016. Logging log_addition() log_change() construct_change_message()
  • 1017. Logging log_addition() log_change() construct_change_message() log_deletion()
  • 1018. Views
  • 1019. Views add_view()/change_view()/delete_view()/ history_view()
  • 1020. Views add_view()/change_view()/delete_view()/ history_view() response_add()/response_change()
  • 1021. ChangeList
  • 1022. ChangeList django.contrib.admin.views.main.ChangeList
  • 1023. ChangeList django.contrib.admin.views.main.ChangeList Last bit of truly “legacy” code in admin
  • 1024. ChangeList django.contrib.admin.views.main.ChangeList Last bit of truly “legacy” code in admin ModelAdmin.get_changelist() allows overriding
  • 1025. Questions?