The django book - Chap10 : Advanced Models

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日星期三