View
1.460
Download
0
Category
Preview:
Citation preview
Advanced Models
「The Django Book」 Chapter 10
Spin Lai
July 23, 20132013年7月24日星期三
賴司平 (Spin)
資拓宏宇 氣象科技事業處
PHP、Javascript、Python
About me
2013年7月24日星期三
Related objects Update database schema Model methods Managers Execute raw SQL queries More about Managers
Today’s topics
2013年7月24日星期三
Related objects
2013年7月24日星期三
Recalling the Chapter 5 ...
2013年7月24日星期三
Publisheridnameaddresscitystate_provincecountrywebsite
AutoFieldCharFieldCharFieldCharFieldCharFieldCharFieldURLField
Bookidtitleauthorspublisherpublication_date
AutoFieldCharFieldManyToManyFieldForeignKeyDateField
Authoridfirst_namelast_nameemail
AutoFieldCharFieldCharFieldEmailField
N:1N:M
2013年7月24日星期三
class Publisher(models.Model): name = models.CharField(max_length=30) address = models.CharField(max_length=50) city = models.CharField(max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField()
def __unicode__(self): return self.name
2013年7月24日星期三
class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField()
def __unicode__(self): return u'%s %s' % (self.first_name,
self.last_name)
2013年7月24日星期三
class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField()
def __unicode__(self): return self.title
2013年7月24日星期三
class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField()
def __unicode__(self): return self.title
2013年7月24日星期三
Foreign Key Value
Publisheridnameaddresscitystate_provincecountrywebsite
AutoFieldCharFieldCharFieldCharFieldCharFieldCharFieldURLField
Bookidtitleauthorspublisher
AutoFieldCharFieldManyToManyFieldForeignKey
>>> b = Book.objects.get(id=50)>>> b.publisher<Publisher: Apress Publishing>>>> b.publisher.websiteu'http://www.apress.com/'<Publisher: Apress Publishing>
2013年7月24日星期三
Foreign Key Value
Publisheridnameaddresscitystate_provincecountrywebsite
AutoFieldCharFieldCharFieldCharFieldCharFieldCharFieldURLField
Bookidtitleauthorspublisher
AutoFieldCharFieldManyToManyFieldForeignKey
>>> p = Publisher.objects.get(name='Apress Publishing')>>> p.book_set.all()[<Book: The Django Book>, <Book: Dive Into Python>, ...]>>> p.book_set.filter(name__icontains='django')[<Book: The Django Book>, <Book: Pro Django>]
2013年7月24日星期三
Foreign Key Value
Publisheridnameaddresscitystate_provincecountrywebsite
AutoFieldCharFieldCharFieldCharFieldCharFieldCharFieldURLField
Bookidtitleauthorspublisher
AutoFieldCharFieldManyToManyFieldForeignKey
>>> p = Publisher.objects.get(name='Apress Publishing')>>> p.book_set.all()[<Book: The Django Book>, <Book: Dive Into Python>, ...]>>> p.book_set.filter(name__icontains='django')[<Book: The Django Book>, <Book: Pro Django>]
2013年7月24日星期三
class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField()
def __unicode__(self): return self.title
2013年7月24日星期三
class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField()
def __unicode__(self): return self.title
2013年7月24日星期三
Many-to-Many Values
Authoridfirst_namelast_nameemail
AutoFieldCharFieldCharFieldEmailField
Bookidtitleauthorspublisher
AutoFieldCharFieldManyToManyFieldForeignKey
>>> b = Book.objects.get(id=50)>>> b.authors.all()[<Author: Adrian Holovaty>, <Author: Jacob Kaplan-Moss>]>>> b.authors.filter(first_name='Adrian')[<Author: Adrian Holovaty>]>>> b.authors.filter(first_name='Adam')[]
2013年7月24日星期三
Many-to-Many Values
Authoridfirst_namelast_nameemail
AutoFieldCharFieldCharFieldEmailField
Bookidtitleauthorspublisher
AutoFieldCharFieldManyToManyFieldForeignKey
>>> a = Author.objects.get(first_name='Adrian', last_name='Holovaty')>>> a.book_set.all()[<Book: The Django Book>, <Book: Adrian's Other Book>]'
2013年7月24日星期三
Many-to-Many Values
Authoridfirst_namelast_nameemail
AutoFieldCharFieldCharFieldEmailField
Bookidtitleauthorspublisher
AutoFieldCharFieldManyToManyFieldForeignKey
>>> a = Author.objects.get(first_name='Adrian', last_name='Holovaty')>>> a.book_set.all()[<Book: The Django Book>, <Book: Adrian's Other Book>]'
2013年7月24日星期三
Update database schema
2013年7月24日星期三
First of all
2013年7月24日星期三
Does not care extra DB table columns Does not care extra DB tables Complains if model contains fields that
has not yet been created in the DB table
Django
2013年7月24日星期三
`syncdb` only create tables for models which
have not yet been installed.
2013年7月24日星期三
Add model fields Remove model fields Remove models
Update database schema
2013年7月24日星期三
Add model fields
2013年7月24日星期三
Add model fields
Add fields to the model Check the column definitions for new fields Add new columns to the DB table Verify new fields was added properly
In development environments ...
2013年7月24日星期三
Add model fields
Add fields to the model Check the column definitions for new fields Add new columns to the DB table Verify new fields was added properly
In development environments ...
class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() num_pages = models.IntegerField(blank=True, null=True)
def __unicode__(self): return self.title
2013年7月24日星期三
Add model fields
Add fields to the model Check the column definitions for new fields Add new columns to the DB table Verify new fields was added properly
In development environments ...
CREATE TABLE "books_book" ( "id" serial NOT NULL PRIMARY KEY, "title" varchar(100) NOT NULL, "publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id"), "publication_date" date NOT NULL, "num_pages" integer NULL);
$ python manage.py sqlall books
2013年7月24日星期三
Add model fields
Add fields to the model Check the column definitions for new fields Add new columns to the DB table Verify new fields was added properly
In development environments ...
ALTER TABLE books_book ADD COLUMN num_pages integer;
2013年7月24日星期三
Add model fields
Add fields to the model Check the column definitions for new fields Add new columns to the DB table Verify new fields was added properly
In development environments ...
>>> from mysite.books.models import Book>>> Book.objects.all()[:5]
2013年7月24日星期三
Add model fields
Add new columns to the DB table Add fields to the model Restart the web server
In production environments ...
2013年7月24日星期三
Remove model fields
2013年7月24日星期三
Add model fields
Remove fields from the model Restart the web server Remove columns from the DB table
Remove Normal fields ...
2013年7月24日星期三
Add model fields
Remove fields from the model Restart the web server Remove columns from the DB table
Remove Normal fields ...
ALTER TABLE books_book DROP COLUMN num_pages;
2013年7月24日星期三
Add model fields
Remove Many-to-Many fields from the model Restart the web server Remove Many-to-Many table from the DB
Remove Many-to-Many fields ...
2013年7月24日星期三
Add model fields
Remove Many-to-Many fields from the model Restart the web server Remove Many-to-Many table from the DB
Remove Many-to-Many fields ...
DROP TABLE books_book_authors;
2013年7月24日星期三
Remove models
2013年7月24日星期三
Remove model from the models.py Restart the web server Remove dependent tables from the DB Remove the target table from the DB
Remove models
2013年7月24日星期三
Remove model from the `models.py` Restart the web server Remove dependent tables from the DB Remove the target table from the DB
Remove models
DROP TABLE books_book;
2013年7月24日星期三
Model methods
2013年7月24日星期三
class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) birth_date = models.DateField() address = models.CharField(max_length=100) city = models.CharField(max_length=50) state = USStateField() # Yes, this is U.S.-centric...
def baby_boomer_status(self): "Returns the person's baby-boomer status." import datetime if datetime.date(1945, 8, 1) <= self.birth_date <= datetime.date(1964, 12, 31): return "Baby boomer" if self.birth_date < datetime.date(1945, 8, 1): return "Pre-boomer" return "Post-boomer"
def is_midwestern(self): "Returns True if this person is from the Midwest." return self.state in ('IL', 'WI', 'MI', 'IN', 'OH', 'IA', 'MO')
def _get_full_name(self): "Returns the person's full name." return u'%s %s' % (self.first_name, self.last_name) full_name = property(_get_full_name)
2013年7月24日星期三
class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) birth_date = models.DateField() address = models.CharField(max_length=100) city = models.CharField(max_length=50) state = USStateField() # Yes, this is U.S.-centric...
def baby_boomer_status(self): "Returns the person's baby-boomer status." import datetime if datetime.date(1945, 8, 1) <= self.birth_date <= datetime.date(1964, 12, 31): return "Baby boomer" if self.birth_date < datetime.date(1945, 8, 1): return "Pre-boomer" return "Post-boomer"
def is_midwestern(self): "Returns True if this person is from the Midwest." return self.state in ('IL', 'WI', 'MI', 'IN', 'OH', 'IA', 'MO')
def _get_full_name(self): "Returns the person's full name." return u'%s %s' % (self.first_name, self.last_name) full_name = property(_get_full_name)
2013年7月24日星期三
Model methods
>>> p = Person.objects.get(first_name='Barack', last_name='Obama')>>> p.birth_datedatetime.date(1961, 8, 4)>>> p.baby_boomer_status()'Baby boomer'>>> p.is_midwestern()True
2013年7月24日星期三
class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) birth_date = models.DateField() address = models.CharField(max_length=100) city = models.CharField(max_length=50) state = USStateField() # Yes, this is U.S.-centric...
def baby_boomer_status(self): "Returns the person's baby-boomer status." import datetime if datetime.date(1945, 8, 1) <= self.birth_date <= datetime.date(1964, 12, 31): return "Baby boomer" if self.birth_date < datetime.date(1945, 8, 1): return "Pre-boomer" return "Post-boomer"
def is_midwestern(self): "Returns True if this person is from the Midwest." return self.state in ('IL', 'WI', 'MI', 'IN', 'OH', 'IA', 'MO')
def _get_full_name(self): "Returns the person's full name." return u'%s %s' % (self.first_name, self.last_name) full_name = property(_get_full_name)
2013年7月24日星期三
Property methods
>>> p = Person.objects.get(first_name='Barack', last_name='Obama')>>> p.full_name # Note this isn't a method u'Barack Obama'
2013年7月24日星期三
Managers
2013年7月24日星期三
What is Manager?
2013年7月24日星期三
Manager
Book.objects.all()
2013年7月24日星期三
Manager
Book.objects.all()
2013年7月24日星期三
Manager
“ The interface through which database query operations are provided to Django models. ”
2013年7月24日星期三
Why do we need a custom Manager?
2013年7月24日星期三
Add extra Manager methods Modify initial Manager QuerySets
Custom Mangers
2013年7月24日星期三
Add extra Manager methods
2013年7月24日星期三
Add extra Manager methods
# Get the number of books that have a title ‘Django’>>> Book.objects.filter(title__icontains='Django')
# Get the number of books that have a title ‘Python’>>> Book.objects.filter(title__icontains='Python')
# Get the number of books that have a title ‘xxx’ ....# Get the number of books that have a title ‘yyy’ .... ..... .......
2013年7月24日星期三
Add extra Manager methods
# takes a keyword and returns the number of books>>> Book.objects.title_count('django')
>>> Book.objects.title_count('python')
2013年7月24日星期三
# models.pyfrom django.db import models
# ... Author and Publisher models here ...
class BookManager(models.Manager): def title_count(self, keyword): return self.filter(title__icontains=keyword).count()
class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() num_pages = models.IntegerField(blank=True, null=True) objects = BookManager()
def __unicode__(self): return self.title
2013年7月24日星期三
# models.pyfrom django.db import models
# ... Author and Publisher models here ...
class BookManager(models.Manager): def title_count(self, keyword): return self.filter(title__icontains=keyword).count()
class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() num_pages = models.IntegerField(blank=True, null=True) objects = BookManager()
def __unicode__(self): return self.title
2013年7月24日星期三
Modify initial Manager QuerySets
2013年7月24日星期三
Modify initial Manager QuerySets
Default Manager return all the records Override Manager’s base QuerySets
2013年7月24日星期三
Modify initial Manager QuerySets
Default Manager return all the records Override Manager’s base QuerySets
# returns all books in the book database>>> Book.objects.all()
2013年7月24日星期三
Modify initial Manager QuerySets
Default Manager return all the records Override Manager’s base QuerySets
By overriding the Manager.get_query_set()
2013年7月24日星期三
Modify initial Manager QuerySets
from django.db import models
# First, define the Manager subclass.class DahlBookManager(models.Manager): def get_query_set(self): return super(DahlBookManager, self).get_query_set()\ .filter(author='Roald Dahl')
# Then hook it into the Book model explicitly.class Book(models.Model): title = models.CharField(max_length=100) author = models.CharField(max_length=50) # ...
objects = models.Manager() # The default manager. dahl_objects = DahlBookManager() # The Dahl-specific manager.
2013年7月24日星期三
Modify initial Manager QuerySets
from django.db import models
# First, define the Manager subclass.class DahlBookManager(models.Manager): def get_query_set(self): return super(DahlBookManager, self).get_query_set()\ .filter(author='Roald Dahl')
# Then hook it into the Book model explicitly.class Book(models.Model): title = models.CharField(max_length=100) author = models.CharField(max_length=50) # ...
objects = models.Manager() # The default manager. dahl_objects = DahlBookManager() # The Dahl-specific manager.
2013年7月24日星期三
Modify initial Manager QuerySets
>>> Book.dahl_objects.all()>>> Book.dahl_objects.filter(title='Matilda')>>> Book.dahl_objects.count()
2013年7月24日星期三
Another example
class MaleManager(models.Manager): def get_query_set(self): return super(MaleManager, self).get_query_set().filter(sex='M')
class FemaleManager(models.Manager): def get_query_set(self): return super(FemaleManager, self).get_query_set().filter(sex='F')
class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) sex = models.CharField(max_length=1, choices=(('M', 'Male'), ('F', 'Female'))) people = models.Manager() men = MaleManager() women = FemaleManager()
2013年7月24日星期三
Another example
class MaleManager(models.Manager): def get_query_set(self): return super(MaleManager, self).get_query_set().filter(sex='M')
class FemaleManager(models.Manager): def get_query_set(self): return super(FemaleManager, self).get_query_set().filter(sex='F')
class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) sex = models.CharField(max_length=1, choices=(('M', 'Male'), ('F', 'Female'))) people = models.Manager() men = MaleManager() women = FemaleManager()
2013年7月24日星期三
Modify initial Manager QuerySets
>>> Person.men.all()>>> Person.women.all()>>> Person.people.all()
2013年7月24日星期三
Execute raw SQL queries
2013年7月24日星期三
Django DB objects
2013年7月24日星期三
connection object cursor object cursor.execute() cursor.fetchone() / cursor.fetchall() Implement the Python DB-API (PEP-0249)
Django DB objects
2013年7月24日星期三
Execute raw SQL queries
>>> from django.db import connection>>> cursor = connection.cursor()>>> cursor.execute("""... SELECT DISTINCT first_name... FROM people_person... WHERE last_name = %s""", ['Lennon'])>>> row = cursor.fetchone()>>> print row['John']
2013年7月24日星期三
Execute raw SQL queriesclass PersonManager(models.Manager): def first_names(self, last_name): cursor = connection.cursor() cursor.execute(""" SELECT DISTINCT first_name FROM people_person WHERE last_name = %s""", [last_name]) return [row[0] for row in cursor.fetchone()]
class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) objects = PersonManager()
2013年7月24日星期三
Execute raw SQL queriesclass PersonManager(models.Manager): def first_names(self, last_name): cursor = connection.cursor() cursor.execute(""" SELECT DISTINCT first_name FROM people_person WHERE last_name = %s""", [last_name]) return [row[0] for row in cursor.fetchone()]
class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) objects = PersonManager()
2013年7月24日星期三
Execute raw SQL queriesclass PersonManager(models.Manager): def first_names(self, last_name): cursor = connection.cursor() cursor.execute(""" SELECT DISTINCT first_name FROM people_person WHERE last_name = %s""", [last_name]) return [row[0] for row in cursor.fetchone()]
class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) objects = PersonManager()
2013年7月24日星期三
Execute raw SQL queries
>>> Person.objects.first_names('Lennon')['John', 'Cynthia']
2013年7月24日星期三
More about Managers
2013年7月24日星期三
As mentioned previously...
2013年7月24日星期三
Business logic encapsulation
Add custom Managers Override default Manager with custom
methods
2013年7月24日星期三
Add custom Managers
class IncompleteTodoManager(models.Manager): def get_query_set(self): return super(TodoManager, self).get_query_set().filter(is_done=False)
class HighPriorityTodoManager(models.Manager): def get_query_set(self): return super(TodoManager, self).get_query_set().filter(priority=1)
class Todo(models.Model): content = models.CharField(max_length=100) # other fields go here..
objects = models.Manager() # the default manager
# attach our custom managers: incomplete = models.IncompleteTodoManager() high_priority = models.HighPriorityTodoManager()
2013年7月24日星期三
Add custom Managers
class IncompleteTodoManager(models.Manager): def get_query_set(self): return super(TodoManager, self).get_query_set().filter(is_done=False)
class HighPriorityTodoManager(models.Manager): def get_query_set(self): return super(TodoManager, self).get_query_set().filter(priority=1)
class Todo(models.Model): content = models.CharField(max_length=100) # other fields go here..
objects = models.Manager() # the default manager
# attach our custom managers: incomplete = models.IncompleteTodoManager() high_priority = models.HighPriorityTodoManager()
2013年7月24日星期三
Add custom Managers
>>> Todo.incomplete.all()>>> Todo.high_priority.all()
2013年7月24日星期三
But
2013年7月24日星期三
Drawbacks
Verbose Managers Cluttered model namespace Not chainable
2013年7月24日星期三
Custom Manager methods
2013年7月24日星期三
Custom Manager methods
class TodoManager(models.Manager): def incomplete(self): return self.filter(is_done=False)
def high_priority(self): return self.filter(priority=1)
class Todo(models.Model): content = models.CharField(max_length=100) # other fields go here..
objects = TodoManager()
2013年7月24日星期三
Custom Manager methods
>>> Todo.objects.incomplete()>>> Todo.objects.high_priority()
2013年7月24日星期三
But
2013年7月24日星期三
Drawbacks
Not chainable between custom methods.
# It didn’t work !>>> Todo.objects.incomplete().high_priority()
2013年7月24日星期三
Custom QuerySets
2013年7月24日星期三
Custom QuerySets
class TodoQuerySet(models.query.QuerySet): def incomplete(self): return self.filter(is_done=False)
def high_priority(self): return self.filter(priority=1)
class TodoManager(models.Manager): def get_query_set(self): return TodoQuerySet(self.model, using=self._db)
class Todo(models.Model): content = models.CharField(max_length=100) # other fields go here..
objects = TodoManager()
2013年7月24日星期三
Custom QuerySets
class TodoQuerySet(models.query.QuerySet): def incomplete(self): return self.filter(is_done=False)
def high_priority(self): return self.filter(priority=1)
class TodoManager(models.Manager): def get_query_set(self): return TodoQuerySet(self.model, using=self._db)
class Todo(models.Model): content = models.CharField(max_length=100) # other fields go here..
objects = TodoManager()
2013年7月24日星期三
Custom QuerySets
>>> Todo.objects.get_query_set().incomplete()>>> Todo.objects.get_query_set().high_priority()>>> # (or)>>> Todo.objects.all().incomplete()>>> Todo.objects.all().high_priority()
>>> # Chainable !!!>>> Todo.objects.all().incomplete().high_priority()
2013年7月24日星期三
But
2013年7月24日星期三
Custom QuerySets
>>> Todo.objects.get_query_set().incomplete()>>> Todo.objects.get_query_set().high_priority()>>> # (or)>>> Todo.objects.all().incomplete()>>> Todo.objects.all().high_priority()
>>> # Chainable !!!>>> Todo.objects.all().incomplete().high_priority()
2013年7月24日星期三
Custom QuerySets
>>> Todo.objects.get_query_set().incomplete()>>> Todo.objects.get_query_set().high_priority()>>> # (or)>>> Todo.objects.all().incomplete()>>> Todo.objects.all().high_priority()
>>> # Chainable !!!>>> Todo.objects.all().incomplete().high_priority()
Ug==Ugly !!
2013年7月24日星期三
Proxy everything !!
2013年7月24日星期三
Proxy everything !!
class TodoQuerySet(models.query.QuerySet): def incomplete(self): return self.filter(is_done=False)
def high_priority(self): return self.filter(priority=1)
class TodoManager(models.Manager): def get_query_set(self): return TodoQuerySet(self.model, using=self._db)
def incomplete(self): return self.get_query_set().incomplete()
def high_priority(self): return self.get_query_set().high_priority()
2013年7月24日星期三
Proxy everything !!
class TodoQuerySet(models.query.QuerySet): def incomplete(self): return self.filter(is_done=False)
def high_priority(self): return self.filter(priority=1)
class TodoManager(models.Manager): def get_query_set(self): return TodoQuerySet(self.model, using=self._db)
def incomplete(self): return self.get_query_set().incomplete()
def high_priority(self): return self.get_query_set().high_priority()
2013年7月24日星期三
Proxy everything !!
>>> Todo.objects.incomplete().high_priority() # Perfect !!
2013年7月24日星期三
Thank you
2013年7月24日星期三
Recommended