Django book2 模型 学习笔记

Djanjo采用模型的优点:
1 自省(运行时自动识别数据库)会导致过载和有数据完整性问题
2 把数据模型用代码的方式表述来让你可以容易对它们进行版本控制。 这样,你可以很容易了解数据层 的变
  动情况。
3  SQL只能描述特定类型的数据字段。 例如,大多数数据库都没有专用的字段类型来描述Email地址
  而用Django的模型可以做到这一点。 好处就是高级的数据类型带来更高的效率和更好的代码复用
  
4  SQL还有在不同数据库平台的兼容性问题。 发布Web应用的时候,使用Python模块描述数据库结构信息可
  以避免为MySQL, PostgreSQL, and SQLite编写不同的CREATE TABLE。


要解决的问题:
  Python代码和数据库表的同步问题。 如果你修改了一个Django模型, 你要自己来修改数据库来保证和模型同步
  
  
一 创建MSSQL数据库 bookdb
   建立project
   django-admin.py startproject mysite
   
二 配置settings.py的DATABASES配置
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
            'NAME': 'bookdb',                      # Or path to database file if using sqlite3.
            'USER': 'root',                      # Not used with sqlite3.
            'PASSWORD': '423423',                  # Not used with sqlite3.
            'HOST': 'localhost',                      # Set to empty string for localhost. Not used with sqlite3.
            'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
        }
    }
    一旦在输入了那些设置并保存之后应当测试一下你的配置。 我们可以在`` depot`` 项目目录下执行`
    python manage.py shell 
    来进行测试。(是以正确Django配置启用Python交互解释器的一种方法。 这个方法在这里是很有必要的,
    因为Django需要知道加载 哪个配置文件来获取数据库连接信息。)
    输入下面这些命令来测试你的数据库配置:
    >>> from django.db import connection
    >>> cursor = connection.cursor()
三 创建模型


1 在项目中创建一个app
    系统对app有一个约定: 如果你使用了Django的数据库层(模型),你 必须创建一个Django app。
    模型必须存放在apps中。 因此,为了开始建造 我们的模型,我们必须创建一个新的app    
    python manage.py startapp books
    
2 创建module.py:


   1)首先要注意的事是每个数据模型都是 django.db.models.Model 的子
   类。它的父类 Model 包含了所有必要的和数据库交互的方法,并提供了一个简洁漂亮的定义数据库字段的语
   法。
   
   2)每个模型相当于单个数据库表,每个属性也是这个表中的一个字段。 属性名就是字段名,它的类型(例如
     CharField )相当于数据库的字段类型 (例如 varchar )
     
   3)“每个数据库表对应一个类”这条规则的例外情况是多对多关系。 在我们的范例模型中, Book 有一个
    多对多字段 叫做 authors 。 该字段表明一本书籍有一个或多个作者,但 Book 数据库表却并没有 authors 字
    段。 相反,Django创建了一个额外的表(多对多连接表)来处理书籍和作者之间的映射关系
    
  ###自定义表属性相关
  4)我们并没有显式地为这些模型定义任何主键。 除非你单独指明,否则Django会自动为每
   个模型生成一个自增长的整数主键字段每个Django模型都要求有单独的主键。id
   
  5)默认的字段名与属性名相同,可以通过字段的db_column属性自定义,如
      where = models.CharField(max_length=400, db_column='place')
  6)除了ID字段,其他字段默认不建立索引,可以通过字段的db_index属性自定义,如
     publish_date= models.DateField(db_index=True)
  7)自定义ID
     如果不指定ID,默认创建一个类型为IntegerField的id字段,可以通过字段的primary_key属性指定自己的ID字段。如:
     key = models.IntegerField(primary_key=True)
  8)自定义表名 
    默认的表名是appName_modelName,在Model类的Meta中可以通过db_table属性改变默认的表名。
        
  9) 自定义表空间
    对于索引字段,默认在settings的DEFAULT_INDEX_TABLESPACE设定的表空间中建立索引,可以通过字段的db_tablespace属性指定,如
         publish_date= models.DateField(db_index=True,db_tablespace='another_tbs')
    对于Model类,通过Meta中的db_tablespace属性指定特定的表空间。


  10)自定义关联关系
       1)外键默认关联到对方的主键字段,可以通过外键的to_field指定关联到的字段,如
              to_model = models.ForeighKey(ToModel,to_field='key')
       2)对于many-to-many关联,Django会创建一个关联表,默认表名是两个表名通过下划线连接起来。
        可以通过ManyToManyField的db_table指定关联表的表名。  
       3)对于many-to-many关联,如果不想使用Django创建的关联表,可以通过ManyToManyField的through属性指定到一个已存在的Model类。 
    
3 模型安装(生成数据库表)
 完成这些代码之后,现在让我们来在数据库中创建这些表。 要完成该项工作,
 1)第一步是在 Django 项目中 激活这些模型。 将 books app 添加到配置文件的已安装应用列表中即可完成此步骤。
 
 2)验证模型的有效性:validate 命令检查你的模型的语法和逻辑是否正确。
  python manage.py validate
  
 3)运行下面的命令来生成 CREATE TABLE 语句
    python manage.py sqlall books
    
    sqlall 命令并没有在数据库中真正创建数据表,只是把SQL语句段打印出来,这样你可以看到Django究竟会做
   些什么。 如果你想这么做的话,你可以把那些SQL语句复制到你的数据库客户端执行,或者通过Unix管道直接
   进行操作(例如,`` python manager.py sqlall books | psql mydb`` )。
   
4)Django提供了一种更为简易的提交SQL语句至数据库的方法: `` syncdb`` 命令
    python manage.py syncdb
    
    执行这个命令后,将看到类似以下的内容:
    Creating table books_publisher
    Creating table books_author
    Creating table books_book
    Installing index for books.Book model
    syncdb 命令是同步你的模型到数据库的一个简单方法。 它会根据 INSTALLED_APPS 里设置的app来检查数据库, 
    如果表不存在,它就会创建它。 需要注意的是, syncdb 并 不能将模型的修改或删除同步到数据库;
    如果你修改或删除了一个模型,并想把它提交到数据库,syncdb并不会做出任何处理。
   
    如果你再次运行 python manage.py syncdb ,什么也没发生,因为你没有添加新的表,运行python manage.py syncdb总是安全的,因为它不会重复执行SQL语句。
    如果你有兴趣,花点时间用你的SQL客户端登录进数据库服务器看看刚才Django创建的数据表。 你可以手动启
    动命令行客户端(例如,执行PostgreSQL的`` psql`` 命令),也可以执行 `` python manage.py dbshell`` ,
    这个命令将依据`` DATABASE_SERVER`` 的里设置自动检测使用哪种命令行客户端。
    
5)基本数据访问
    python manage.py shell 并输入
    下面的内容试试看:
    >>> from books.models import Publisher
    >>> p1 = Publisher(name='Apress', address='2855 Telegraph Avenue',
    ...     city='Berkeley', state_province='CA', country='U.S.A.',
    ...     website='http://www.apress.com/')
    >>> p1.save()
    >>> p2 = Publisher(name="O'Reilly", address='10 Fawcett St.',
    ...     city='Cambridge', state_province='MA', country='U.S.A.',
    ...     website='http://www.oreilly.com/')
    >>> p2.save()
    >>> publisher_list = Publisher.objects.all()
    >>> publisher_list
    [<Publisher: Publisher object>, <Publisher: Publisher object>]
    
    这短短几行代码干了不少的事。 这里简单的说一下:
    1)首先,导入Publisher模型类, 通过这个类我们可以与包含 出版社 的数据表进行交互。
    2)接着,创建一个`` Publisher`` 类的实例并设置了字段`` name, address`` 等的值。
    3)调用该对象的 save() 方法,将对象保存到数据库中。 Django 会在后台执行一条 INSERT 语句。
    4)最后,使用`` Publisher.objects`` 属性从数据库取出出版商的信息,这个属性可以认为是包含出版商的记
    录集。 这个属性有许多方法, 这里先介绍调用`` Publisher.objects.all()`` 方法获取数据库中`` Publisher``
    类的所有对象。这个操作的幕后,Django执行了一条SQL `` SELECT`` 语句。
    
    这里有一个值得注意的地方,在这个例子可能并未清晰地展示。 当你使用Django modle API创建对象时
    Django并未将对象保存至数据库内,除非你调用`` save()`` 方法:
    
   如果需要一步完成对象的创建与存储至数据库,就使用`` objects.create()`` 方法


6)添加模块的字符串表现
为Publisher 对象添加一个方法 __unicode__() 。 __unicode__() 方法告
诉Python如何将对象以unicode的方式显示出来。 
为以上三个模型添加__unicode__()方法后,就可以看到效
果了:


 为了让我们的修改生效,先退出Python Shell,然后再次运行 python manage.py shell 进入。(这是保证代码
修改生效的最简单方法。)现在`` Publisher``对象列表容易理解多了。
>>> from books.models import Publisher
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
[<Publisher: Apress>, <Publisher: O'Reilly>]


请确保你的每一个模型里都包含 __unicode__() 方法,这不只是为了交互时方便,也是因为 Django会在其他
一些地方用 __unicode__() 来显示对象。
最后, __unicode__() 也是一个很好的例子来演示我们怎么添加 行为 到模型里。 Django的模型不只是为对象
定义了数据库表的结构,还定义了对象的行为。 __unicode__() 就是一个例子来演示模型知道怎么显示它们自
己。


四 模型操作(一般在view.py进行)
1 插入数据
    先使用一些关键参数创建对象实例,如下:
    >>> p = Publisher(name='Apress',
    ...         address='2855 Telegraph Ave.',
    ...         city='Berkeley',
    ...         state_province='CA',
    ...         country='U.S.A.',
                website='http://www.apress.com/')
    >>> p.save()  
    


            
    因为 Publisher 模型有一个自动增加的主键 id ,所以第
    并把它赋值给这个对象实例:
    >>> p.id
    52    # this will differ based on your own data     


2 查询数据
    1)filter:获取列表
    你可以传递多个参数到 filter() 来缩小选取范围:    
    >>> Publisher.objects.filter(country="U.S.A.", state_province="CA")
    [<Publisher: Apress>]


    在 name 和 contains 之间有双下划线。和Python一样,Django也使用双下划线来表明会进行一些魔术般的
    操作。这里,contains部分会被Django翻译成LIKE语句:
    >>> Publisher.objects.filter(name__contains="press")
    [<Publisher: Apress>]


    其他的一些查找类型有:icontains(大小写无关的LIKE),startswith和endswith, 还有range(SQLBETWEEN查
    询)。
    
    2)get:获取单个对象
    上面的例子中`` filter()`` 函数返回一个记录集,这个记录集是一个列表。 相对列表来说,有些时候我们更需要
    获取单个的对象, `` get()`` 方法就是在此时使用的:(一般是主键作为查询)
    >>> Publisher.objects.get(name="Apress")
    <Publisher: Apress>
    这样,就返回了单个对象,而不是列表(更准确的说,QuerySet)。 所以,如果结果是多个对象,会导致抛出
    异常:
    
    3)数据排序
    如果需要以多个字段为标准进行排序(第二个字段会在第一个字段的值相同的情况下被使用到),使用多个参
    数就可以了,如下:
    >>> Publisher.objects.order_by("state_province", "address")
     [<Publisher: Apress>, <Publisher: O'Reilly>]
    我们还可以指定逆向排序,在前面加一个减号 ‐ 前缀:
    >>> Publisher.objects.order_by("-name")
    [<Publisher: O'Reilly>, <Publisher: Apress>]
    
    缺省排序
    
    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
    class Meta:
        ordering = ['name']
    现在,让我们来接触一个新的概念。 class Meta,内嵌于 Publisher 这个类的定义中(如果 class Publisher
    是顶格的,那么 class Meta 在它之下要缩进4个空格--按 Python 的传统 )。你可以在任意一个 模型 类中
    使用 Meta 类,来设置一些与特定模型相关的选项。 在 附录B 中有 Meta 中所有可选项的完整参考,现在,我
    关注 ordering 这个选项就够了。 如果你设置了这个选项,那么除非你检索时特意额外地使用了 order_by(),
    否则,当你使用 Django 的数据库 API 去检索时,Publisher对象的相关返回值默认地都会按 name 字段排序。
    
    4)我们已经知道如何对数据进行过滤和排序。 当然,通常我们需要同时进行过滤和排序查询的操作。 因此,你可
    以简单地写成这种“链式”的形式:
    >>> Publisher.objects.filter(country="U.S.A.").order_by("-name")
    [<Publisher: O'Reilly>, <Publisher: Apress>]
    
    5)限制返回的数据 
    另一个常用的需求就是取出固定数目的记录。 想象一下你有成千上万的出版商在你的数据库里, 但是你只想显
    示第一个。 你可以使用标准的Python列表裁剪语句:
    >>> Publisher.objects.order_by('name')[0]
    <Publisher: Apress>
    
    类似的,你可以用Python的range-slicing语法来取
    >>> Publisher.objects.order_by('name')[0:2]
    
3 更新数据
    我们可以看到Django的save()方法更新了不仅仅是name列的值,还有更新了所有的列。 若
    name以外的列有可能会被其他的进程所改动的情况下,只更改name列显然是更加明智的。 更改某一指定的
    列,我们可以调用结果集(QuerySet)对象的update()方法: 示例如下:
    >>> Publisher.objects.filter(id=4).update(name='Apress Publishing')
    
    与之等同的SQL语句变得更高效,并且不会引起竞态条件。
    UPDATE books_publisher
    SET name = 'Apress Publishing'
    WHERE id = 52;
    update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录。 以下示例演示如何
    将所有Publisher的country字段值由’U.S.A’更改为’USA’:
    >>> Publisher.objects.all().update(country='USA')
    2
    update()方法会返回一个整型数值,表示受影响的记录条数。 在上面的例子中,这个值是2。
4 删除数据


    >>> Publisher.objects.filter(name='Apress Publishing').delete()
    >>> Publisher.objects.filter(country='USA').delete()
    >>> Publisher.objects.all().delete()
    >>> Publisher.objects.all()
    []
    删除数据时要谨慎! 为了预防误删除掉某一个表内的所有数据,Django要求在删除表内所有数据时显示使用
    all()。
    
    
    
    
    
    
 五 数据模型高级进阶
 from django.db import models
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
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)
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
        
1 访问外键(Foreign Key)值
    当你获取一个ForeignKey 字段时,你会得到相关的数据模型对象。 例如:
    >>> b = Book.objects.get(id=1)
    >>> b.publisher
    <Publisher: Apress Publishing>
    >>> b.publisher.website
    u'http://www.apress.com/'
    
    对于用`` ForeignKey`` 来定义的关系来说,在关系的另一端也能反向的追溯回来,
    。通过一个`` publisher`` 对象,直接获取 books ,用 publisher.book_set.all() ,如
    
    >>> p = Publisher.objects.get(name='O\'Reilly')
    >>> p.book_set.all()
    [<Book: The Django Book>, <Book: Dive Into Python>, ...]
    
    实际上,book_set 只是一个 QuerySet(参考第5章的介绍),所以它可以像QuerySet一样,能实现数据过滤和分
    切,例如:
    >>> p = Publisher.objects.get(name='Apress Publishing')
    >>> p.book_set.filter(name__icontains='django')
    [<Book: The Django Book>, <Book: Pro Django>]
    属性名称book_set是由模型名称的小写(如book)加_set组成的。 
    
2  访问多对多值(Many-to-Many Values)
    多对多和外键工作方式相同,只不过我们处理的是QuerySet而不是模型实例。 
    >>> b = Book.objects.get(id=1)
    >>> b.authors.all()
    [<Author: Apress Holovaty>] 
    >>> b.authors.filter(first_name='Apress')
    [<Author: Adrian Holovaty>]
    >>> b.authors.filter(first_name='Adam')
    []
    反向查询也可以。 要查看一个作者的所有书籍,使用author.book_set ,就如这样:
    >>> a = Author.objects.get(first_name='Apress', last_name='Holovaty')
    >>> a.book_set.all()
    [<Book: The Django Book>]
    这里,就像使用 ForeignKey字段一样,属性名book_set是在数据模型(model)名后追加_set。


                             
3 更改数据库模式(Database Schema)
    在我们在第5章介绍 syncdb 这个命令时, 我们注意到 syncdb仅仅创建数据库里还没有的表,它 并不 对你数据模
    型的修改进行同步,也不处理数据模型的删除。 如果你新增或修改数据模型里的字段,或是删除了一个数据模型,
    你需要手动在数据库里进行相应的修改。


    当处理模型修改的时候,将Django的数据库层的工作流程铭记于心是很重要的。
    1)如果模型包含一个未曾在数据库里建立的字段,Django会报出错信息。 当你第一次用Django的数据库
      API请求表中不存在的字段时会导致错误(就是说,它会在运行时出错,而不是编译时)。
    2) Django不关心数据库表中是否存在未在模型中定义的列。
    3)Django不关心数据库中是否存在未被模型表示的表格。
    
    改变模型的模式架构意味着需要按照顺序更改Python代码和数据库。
    
    A 添加字段
    
        当要向一个产品设置表(或者说是model)添加一个字段的时候,要使用的技巧是利用Django不关心表里是否包
        含model里所没有的列的特性。 策略就是现在数据库里加入字段,然后同步Django的模型以包含新字段。
        
        首先,进入开发环境(也就是说,不是在发布环境里):
        1.  在你的模型里添加字段。
        2.  运行 manage.py sqlall [yourapp] 来测试模型新的 CREATE TABLE 语句。 注意为新字段的列定义。
        3.  开启你的数据库的交互命令界面(比如, psql 或mysql , 或者可以使用 manage.py dbshell )。 执行
        ALTER TABLE 语句来添加新列。
        4.  使用Python的manage.py shell,通过导入模型和选中表单(例如, MyModel.objects.all()[:5] )来验证
        新的字段是否被正确的添加 ,如果一切顺利,所有的语句都不会报错。
        然后在你的产品服务器上再实施一遍这些步骤。
        1.  启动数据库的交互界面。
        2.  执行在开发环境步骤中,第三步的ALTER TABLE语句。
        3.  将新的字段加入到模型中。 如果你使用了某种版本控制工具,并且在第一步中,已经提交了你在开发环境
        上的修改,现在,可以在生产环境中更新你的代码了(例如,如果你使用Subversion,执行svn update。
        4.  重新启动Web server,使修改生效。
        
            让我们实践下,比如添加一个num_pages字段到第五章中Book模型。首先,我们会把开发环境中的模型改成
            如下形式:
            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
            (注意 阅读第六章的“设置可选字段”以及本章下面的“添加非空列”小节以了解我们在这里添
            加blank=True和null=True的原因。)
            然后,我们运行命令manage.py sqlall books 来查看CREATE TABLE语句。 语句的具体内容取决与你所使用
            的数据库, 大概是这个样子:
            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
            );
            新加的字段被这样表示:
            "num_pages" integer NULL
            接下来,我们要在开发环境上运行数据库客户端,如果是PostgreSQL,运行 psql,,然后,我执行如下语句。
            ALTER TABLE books_book ADD COLUMN num_pages integer NULL;
            添加 NULL 字段
            
           
            >>> from mysite.books.models import Book
            >>> Book.objects.all()[:5]
            如果没有异常发生,我们将切换到生产服务器,然后在生产环境的数据库中执行命令ALTER TABLE 然后我们更新
            生产环境中的模型,最后重启web服务器。
    B 删除字段
        删除字段,然后重新启动你的web服务器。
        用以下命令从数据库中删除字段:
        ALTER TABLE books_book DROP COLUMN num_pages;


    C  删除多对多关联字段
        从你的模型中删除ManyToManyField,然后重启web服务器。
        用下面的命令从数据库删除关联表:
        DROP TABLE books_book_authors;
    
    D  删除模型
   
        从文件中删除你想要删除的模型,然后重启web 服务器models.py
        然后用以下命令从数据库中删除表:
        DROP TABLE books_book;
        当你需要从数据库中删除任何有依赖的表时要注意(也就是任何与表books_book有外键的表 )。




4 Managers
    在语句Book.objects.all()中,objects是一个特殊的属性,需要通过它查询数据库。在第5章,我们只是简要地说这是模块的manager 。
    模块manager是一个对象,Django模块通过它进行数据库查询。 
    每个Django模块至少有一个manager,你可以创建自定义manager以定制数据库访问。
    下面是你创建自定义manager的两个原因: 增加额外的manager方法,和/或修改manager返回的初始QuerySet。


    1)增加额外的Manager方法
        增加额外的manager方法是为模块添加表级功能的首选办法。 (至于行级功能,也就是只作用于模型对象实例
        的函数,一会儿将在本章后面解释。)
        例如,我们为Book模型定义了一个title_count()方法,它需要一个关键字,返回包含这个关键字的书的数量。


        # models.py
        from 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
                
        有了这个manager,我们现在可以这样做:
        >>> Book.objects.title_count('Django')
        4


        下面是编码该注意的一些地方:
        A 我们建立了一个BookManager类,它继承了django.db.models.Manager。这个类只有一个title_count()方法,用来做统计。 
        注意,这个方法使用了self.filter(),此处self指manager本身。


        B 我们把BookManager()赋值给模型的objects属性。 它将取代模型的默认manager(objects)如果我们
        没有特别定义,它将会被自动创建。 我们把它命名为objects,这是为了与自动创建的manager保持一致。


        C 为什么我们要添加一个title_count()方法呢?是为了将经常使用的查询进行封装,这样我们就不必重复编码了。
        
    2)修改初始Manager QuerySets
        
        manager的基本QuerySet返回系统中的所有对象。 例如,`` Book.objects.all()`` 返回数据库book中的所有书本。
        我们可以通过覆盖Manager.get_query_set()方法来重写manager的基本QuerySet。 get_query_set()按照你的要求返回一个QuerySet。
        例如,下面的模型有两个manager。一个返回所有对像,另一个只返回作者标题The Django Book的书。
        
        class BookManager(models.Manager):
            def title_count(self, keyword):
                return self.filter(title__icontains=keyword).count()
                
        class DahlBookManager(models.Manager):
            def get_query_set(self):
                return super(DahlBookManager, self).get_query_set().filter(title='The Django Book')
                
        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()
            dahl_objects = DahlBookManager()
            def __unicode__(self):
                return self.title
            
        在这个示例模型中,Book.objects.all()返回了数据库中的所有书本,而Book.dahl_objects.all()只返回了一本. 
        注意我们明确地将objects设置成manager的实例,因为如果我们不这么做,那么唯一可用的manager就将是dah1_objects。
        当然,由于get_query_set()返回的是一个QuerySet对象,所以我们可以使用filter(),exclude()和其他一切
        QuerySet的方法。 像这些语法都是正确的:


        Book.dahl_objects.all()
        Book.dahl_objects.filter(title='The Django Book')
        Book.dahl_objects.count()


        这个例子也指出了其他有趣的技术: 在同一个模型中使用多个manager。
        只要你愿意,你可以为你的模型添加多个manager()实例。 
        这是一个为模型添加通用滤器的简单方法。
        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()
        这个例子允许你执行`` Person.men.all()`` ,`` Person.women.all()`` ,`` Person.people.all()`` 查询,生成你
        想要的结果。
        
        如果你使用自定义的Manager对象,请注意,Django遇到的第一个Manager(以它在模型中被定义的位置为准)
        会有一个特殊状态。 Django将会把第一个Manager 定义为默认Manager ,Django的许多部分(但是不包括
        admin应用)将会明确地为模型使用这个manager。
        结论是,你应该小心地选择你的默认manager。因为覆盖get_query_set() 了,你可能接受到一个无用的返回对像,你必须避免这种情况。




5 模型方法


    为了给你的对像添加一个行级功能,那就定义一个自定义方法。 
    有鉴于manager经常被用来用一些整表操作(table-wide),模型方法应该只对特殊模型实例起作用。
    这是一项在模型的一个地方集中业务逻辑的技术。
    
    from django.contrib.localflavor.us.models import USStateField
    from django.db import models
    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 "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)
        
    例子中的最后一个方法是一个property。 想了解更多关于属性的信息请访
    问http://www.python.org/download/releases/2.2/descrintro/#property
    
    这是用法的实例:
    >>> p = Person.objects.get(first_name='Barack', last_name='Obama')
    >>> p.birth_date
    datetime.date(1961, 8, 4)
    >>> p.baby_boomer_status()
    'Baby boomer'
    >>> p.is_midwestern()
    True
    >>> p.full_name  # Note this isn't a method ‐‐ it's treated as an attribute
    u'Barack Obama'




6 执行原始SQL查询


    有时候你会发现Django数据库API带给你的也只有这么多,那你可以为你的数据库写一些自定义SQL查询。 
    你可以通过导入django.db.connection对像来轻松实现,它代表当前数据库连接。 
    要使用它,需要通过connection.cursor()得到一个游标对像。 然后,使用cursor.execute(sql, [params])来执行SQL语句,使
    用cursor.fetchone()或者cursor.fetchall()来返回记录集。
  
    connection和cursor几乎实现了标准Python DB-API,
    你可以访问` http://www.python.org/peps/pep-0249.html <http://www.python.org/peps/pep-0249.html>`__来获取更多信息。 
    如果你对Python DB-API不熟悉,请注意在cursor.execute() 的SQL语句中使用`` “%s”`` ,而不要在SQL内直接添加参数。 如果你使
    用这项技术,数据库基础库将会自动添加引号,同时在必要的情况下转意你的参数。
    
    不要把你的视图代码和django.db.connection语句混杂在一起,
    把它们放在自定义模型或者自定义manager法中是个不错的主意。 比如,上面的例子可以被整合成一个自定义manager方法,就像这样:
    from django.db import connection, models
    def get_first_name(self, last_name):
        cursor = connection.cursor()
        cursor.execute("""
            SELECT DISTINCT first_name
            FROM books_author
            WHERE last_name = %s""", [last_name])
        return [cursor.fetchone()]
        
        
    然后这样使用:
    >>> Author.objects.get_first_name('Holovaty') 
    [(u'Apress',)]
    
7 与遗留数据库整合
    Django的数据库层从Python代码生成SQL schemas—但是对于遗留数据库,你已经拥有SQL schemas. 
    这种情况,你需要为已经存在的数据表创建model. 为此,Django自带了一个可以通过读取您的数据表结构来生成
    model的工具. 该辅助工具称为inspectdb,你可以通过执行manage.py inspectdb来调用它.


    使用 inspectdb
    python manage.py inspectdb
    inspectdb工具自省你配置文件指向的数据库,针对每一个表生成一个Django模型,然后将这些Python模型的
    代码显示在系统的标准输出里面。
    下面是一个从头开始的针对一个典型的遗留数据库的整合过程。 两个前提条件是安装了Django和一个传统数
    据库。


    1 通过运行django-admin.py startproject mysite (这里 mysite 是你的项目的名字)建立一个Django项目。
    
    2 编辑项目中的配置文件, mysite/settings.py ,告诉Django你的数据库连接参数和数据库名。 
    
    3 通过运行 python mysite/manage.py startapp myapp (这里 myapp 是你的应用的名字)创建一个Django应
    用。 这里我们使用myapp 做为应用名。
    
    4 运行命令 python mysite/manage.py inspectdb。这将检查DATABASE_NAME 数据库中所有的表并打印出为每
    张表生成的模型类。 看一看输出结果以了解inspectdb能做些什么。
    
    5 将标准shell的输出重定向,保存输出到你的应用的 models.py 文件里:
    python mysite/manage.py inspectdb > mysite/myapp/models.py
    
    6 编辑 mysite/myapp/models.py 文件以清理生成的 models 并且做一些必要的自定义。 
    
    清理生成的Models
    
        如你可能会预料到的,数据库自省不是完美的,你需要对产生的模型代码做些许清理。 这里提醒一点关于处理生成 models 的要点:
        
        1) 数据库的每一个表都会被转化为一个model类 (也就是说,数据库的表和model 类之间是一对一的映射)。
        这意味着你需要为多对多连接的表,重构其models 为 ManyToManyField 的对象。


        2)所生成的每一个model中的每个字段都拥有自己的属性,包括id主键字段。 但是,请注意,如果某个model
        没有主键的话,那么Django会自动为其增加一个id主键字段。 这样一来,你也许希望移除这样的代码行。
        id = models.IntegerField(primary_key=True)
        这样做并不是仅仅因为这些行是冗余的,而且如果当你的应用需要向这些表中增加新记录时,这些行会导
        致某些问题。
        
        3)每一个字段类型,如CharField、DateField, 是通过查找数据库列类型如VARCHAR,DATE来确定的。如果
        inspectdb无法把某个数据库字段映射到model字段上,它会使用TextField字段进行代替,并且会在所生成
        model字段后面加入Python注释“该字段类型是猜的”。 对这要当心,如果必要的话,更改字段类型。
        
        4)如果你的数据库中的某个字段在Django中找不到合适的对应物,你可以放心的略过它。
        Django模型层不要求必须导入你数据库表中的每个列。
        
        5)如果数据库中某个列的名字是Python的保留字(比如pass、class或者for等),inspectdb会在每个属性名
        后附加上_field,并将db_column属性设置为真实的字段名(也就是pass,class或者for等)。
        例如,某张表中包含一个INT类型的列,其列名为for,那么所生成的model将会包含如下所示的一个字段:
        for_field = models.IntegerField(db_column='for')
        inspectdb 会在该字段后加注 ‘字段重命名,因为它是一个Python保留字’ 。
        
        6)如果数据库中某张表引用了其他表(正如大多数数据库系统所做的那样),你需要适当的修改所生成model
        的顺序,以使得这种引用能够正确映射。 
        例如,model Book拥有一个针对于model Author的外键,那么后
        者应该先于前者被定义。如果你想创建一个指向尚未定义的model的关系,那么可以使用包含model名的字
        符串,而不是model对象本身。
        
        7)对于PostgreSQL,MySQL和SQLite数据库系统,inspectdb能够自动检测出主键关系。 也就是说,它会在合
        适的位置插入primary_key=True。
        而对于其他数据库系统,你必须为每一个model中至少一个字段插入这样的语句,
        因为Django的model要求必须拥有一个primary_key=True的字段。
        
        8)外键检测仅对PostgreSQL,还有MySQL表中的某些特定类型生效。
        至于其他数据库,外键字段将在假定其为INT列的情况下被自动生成为IntegerField。
        
    

    附录:利用MYSQL数据库的实验结果

Microsoft Windows XP [版本 5.1.2600]
(C) 版权所有 1985-2001 Microsoft Corp.


E:\ChromeDown\depot>E:\ChromeDown\depot>cd E:\CODE_SVN\pysrc


E:\CODE_SVN\pysrc>django-admin.py startproject mysite
E:\CODE_SVN\pysrc\mysite>python manage.py shell
Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.db import connection
>>> cursor = connection.cursor()
>>> exit()


E:\CODE_SVN\pysrc\mysite>python manage.py startapp books
E:\CODE_SVN\pysrc\mysite>python manage.py validate
0 errors found
E:\CODE_SVN\pysrc\mysite>python manage.py sqlall books
BEGIN;
CREATE TABLE `books_publisher` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `name` varchar(30) NOT NULL,
    `address` varchar(50) NOT NULL,
    `city` varchar(60) NOT NULL,
    `state_province` varchar(30) NOT NULL,
    `country` varchar(50) NOT NULL,
    `website` varchar(200) NOT NULL
)
;
CREATE TABLE `books_author` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `first_name` varchar(30) NOT NULL,
    `last_name` varchar(40) NOT NULL,
    `email` varchar(75) NOT NULL
)
;
CREATE TABLE `books_book_authors` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `book_id` integer NOT NULL,
    `author_id` integer NOT NULL,
    UNIQUE (`book_id`, `author_id`)
)
;
ALTER TABLE `books_book_authors` ADD CONSTRAINT `author_id_refs_id_9e7e386` FOREIGN KEY (`author_id`) REFERENCES `books_author` (`id`);
CREATE TABLE `books_book` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `title` varchar(100) NOT NULL,
    `publisher_id` integer NOT NULL,
    `publication_date` date NOT NULL
)
;
ALTER TABLE `books_book` ADD CONSTRAINT `publisher_id_refs_id_3a4d8b45` FOREIGN KEY (`publisher_id`) REFERENCES `books_publisher` (`id`);
ALTER TABLE `books_book_authors` ADD CONSTRAINT `book_id_refs_id_30430d9e` FOREIGN KEY (`book_id`) REFERENCES `books_book` (`id`);
CREATE INDEX `books_book_22dd9c39` ON `books_book` (`publisher_id`);
COMMIT;


E:\CODE_SVN\pysrc\mysite>python manage.py  syncdb
Creating tables ...
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_user_permissions
Creating table auth_user_groups
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table books_publisher
Creating table books_author
Creating table books_book_authors
Creating table books_book


You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): no
Installing custom SQL ...
Installing indexes ...
No fixtures found.






E:\CODE_SVN\pysrc\mysite>python manage.py  shell
Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from books.models import Publisher
>>> p1 = Publisher(name='Apress', address='2855 Telegraph Avenue',city='Berkeley', state_province='CA', country='U.S.A.',website='http://www.apress.com/')
>>> p1.save()
>>> p2 = Publisher(name="O'Reilly", address='10 Fawcett St.',
... city='Cambridge', state_province='MA', country='U.S.A.',
... website='http://www.oreilly.com/')
>>> >>> p2.save()
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
[<Publisher: Publisher object>, <Publisher: Publisher object>]
>>> exit()










E:\CODE_SVN\pysrc\mysite>python manage.py  shell
Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from books.models import Publisher
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
[<Publisher: Apress>, <Publisher: O'Reilly>]


>>> Publisher.objects.filter(country="U.S.A.", state_province="CA")
[<Publisher: Apress>]


>>> Publisher.objects.get(name="Apress")
<Publisher: Apress>


>>> Publisher.objects.order_by("state_province", "address")
[<Publisher: Apress>, <Publisher: O'Reilly>]


>>> Publisher.objects.order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress>]


>>> Publisher.objects.filter(country="U.S.A.").order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress>]


>>> Publisher.objects.order_by('name')[0:2]
[<Publisher: Apress>, <Publisher: O'Reilly>]


>>> Publisher.objects.order_by('name')[0]
<Publisher: Apress>




>>> Publisher.objects.filter(id=4).update(name='Apress Publishing')
1L
>>> Publisher.objects.all().update(country='USA')
2L


>>> Publisher.objects.filter(name='Apress Publishing').delete()
>>> Publisher.objects.all()
[<Publisher: O'Reilly>]












>>> from books.models import Publisher
>>> from books.models import Author
>>> p=Author(first_name='Apress', last_name='Holovaty',email='Apress@sina.com')
>>> p.save()
>>> p.id
1L
>>> p1 = Author(first_name='Jacob', last_name='Kaplan-Moss',email='Jacob@sina.com')
>>> p1.save()
>>> p1.id
2L
>>> Author.objects.all()
[<Author: Apress Holovaty>, <Author: Jacob Kaplan-Moss>]


>>> from books.models import Book
>>> p1=Publisher.objects.get(id=2) 
>>> a1=Author.objects.get(first_name='Apress')  
>>> b1=Book(title='The Django Book',publisher=p1,publication_date='2010-5-5')  
>>> b1.save()  
>>> b1.authors.add(a1)  
>>> b1.save()


>>> p1=Publisher.objects.get(id=2) 
>>> a1=Author.objects.get(first_name='Jacob')   
>>> b1=Book(title='Dive into Python',publisher=p1,publication_date='2010-5-5')  
>>> b1.save()  
>>> b1.authors.add(a1)  
>>> b1.save()  


>>> b = Book.objects.get(id=1)
>>> b.publisher
<Publisher: O'Reilly>


>>> p = Publisher.objects.get(name='O\'Reilly')
>>> p.book_set.all()
[<Book: The Django Book>, <Book: Dive into Python>]


>>> b = Book.objects.get(id=1)
>>> b.authors.all()
[<Author: Apress Holovaty>]


>>> b.authors.filter(first_name='Adrian')
[]
>>> b.authors.filter(first_name='Apress')
[<Author: Apress Holovaty>]


>>> a = Author.objects.get(first_name='Apress', last_name='Holovaty')
>>> a.book_set.all()
[<Book: The Django Book>]
>>> exit()






E:\CODE_SVN\pysrc\mysite>python manage.py shell
Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from books.models import Author
>>> Author.objects.get_first_name('Apress') 
[None]
>>> Author.objects.get_first_name('Holovaty') 
[(u'Apress',)]
>>> 

    
    
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值