Django Book 2.0 笔记——Model

梦想的初衷 提交于 2019-12-04 19:56:24

配置数据库:

settings 文件内:


   
DATABASES = { ' default ' : { ' ENGINE ' : ' django.db.backends.postgresql_psycopg2 ' , ' NAME ' : ' mydatabase ' , ' USER ' : ' mydatabaseuser ' , ' PASSWORD ' : ' mypassword ' , ' HOST ' : ' 127.0.0.1 ' , ' PORT ' : ' 5432 ' , } }

如果是 SQLite,则只需填写 engine 和 name,SQLite 的 name 应当包含可靠路径(因为没有 server):


   
DATABASES = { ' default ' : { ' ENGINE ' : ' django.db.backends.sqlite3 ' , ' NAME ' : ' mydatabase ' , } }

测试数据库配置:


   
>>> from django.db import connection >>> cursor = connection.cursor()

 

创建表

django 的 ORM 示例,定义在 app 下的 models 文件内,具体支持的字段参考官方文档:


   
from django.db import models class Book(models.Model): title = models.CharField(max_length = 100 ) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField()

django 使用额外一张单独的表来管理多对多联系。

 

部署模型

在部署模型前,首先应保证相应的 app 是已安装状态,这在 settings 文件内的 INSTALLED_APPS 字段内进行设置。

 

验证模型有效性:


   
python manage.py validate

 

生成 CREAT TABLE 语句(打印出来给你看看,并不执行):


   
python manage.py sqlall books

这里的 books 是 app 的名称。运行后输出如下:


   
BEGIN ; CREATE TABLE "books_publisher" ( "id" serial 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" serial 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" ( "id" serial NOT NULL PRIMARY KEY , "title" varchar ( 100 ) NOT NULL , "publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id") DEFERRABLE INITIALLY DEFERRED, "publication_date" date NOT NULL ) ; CREATE TABLE "books_book_authors" ( "id" serial NOT NULL PRIMARY KEY , "book_id" integer NOT NULL REFERENCES "books_book" ("id") DEFERRABLE INITIALLY DEFERRED, "author_id" integer NOT NULL REFERENCES "books_author" ("id") DEFERRABLE INITIALLY DEFERRED, UNIQUE ("book_id", "author_id") ) ; CREATE INDEX "books_book_publisher_id" ON "books_book" ("publisher_id"); COMMIT ;

注意:

  • 这里自动生成的表名是:app名称( books )和模型的小写名称 ( publisher , book , author )的组合。除非你显式指定一个表名。
  • Django为每个表格自动添加加了一个 id 主键, 你可以重新设置它。
  • 按约定,Django添加 "_id" 后缀到外键字段名,这个同样是可自定义的
  • 外键是用 REFERENCES 语句明确定义的(books 的 publisher 外键)
  • 这些 CREATE TABLE 语句会根据你的数据库而作调整

 

同步数据库(仅添加,不会修改和删除表):


   
python manage.py syncdb

 

INSERT & UPDATE:

在 python manage.py shell 命令行内执行 INSERT 操作的方法是实例化一个模型对象,并调用其 .save() 方法


   
>>> 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()

不过这个 save() 方法在用于 UPDATE 功能时,实际执行的是更新整行而不仅是修改过的属性。这可能会引起竞态条件,如果想只更新一个属性,应当调用结果集的 .update() 方法:


   
>>> Publisher.objects.filter(id = 52 ).update(name = ' Apress Publishing ' )

update() 方法返回一个整数,表示受影响的行数。

 

DELETE:

.delete() 方法与 .update() 类似,都是作用于结果集的方法。但出于安全考虑,在删除表中所有元组时,需要显示调用 .all() 方法:


   
>>> Publisher.objects.delete() Traceback (most recent call last): File " <console> " , line 1 , in < module > AttributeError: ' Manager ' object has no attribute ' delete ' >>> Publisher.objects.all().delete()

 

SELECT:

如果想 print 出 Publisher 对象的内容,需要在定义模型时写好 __str__() 方法。


   
>>> publisher_list = Publisher.objects.all() >>> publisher_list [ < Publisher: Publisher object > , < Publisher: Publisher object > ]

 

WHERE:


   
>>> Publisher.objects.filter(name = ' Apress ' [, *** ]) [ < Publisher: Apress > ]

 

LIKE:

注意这里是双下划线 __contains


   
>>> Publisher.objects.filter(name__contains = " press " ) [ < Publisher: Apress > ]

filter() 返回的总是一个容器对象,这个对象实现了列表的操作方法(一如既往不包括负索引),因此总是可以使用比如 for 循环来迭代其中的元素。对应的有一个 get() 方法,这个方法要求满足条件的元素有且只有一个,否则引发异常。

 

给结果排序:


   
>>> Publisher.objects.order_by( " state_province " , " address " )

第二个参数仅在第一个参数相同时起作用,排序还可以在属性前加“-”来实现逆序:


   
>>> Publisher.objects.order_by( " -name " )

.ordered_by() 可作用于结果查询结果对象集,所以 filter() 返回的结果也可以支持。

或者,直接在模型定义的地方指定默认排序方式:


   
class Publisher(models.Model): name = models.CharField(max_length = 30 ) . . . class Meta: ordering = [ ' name ' ]

 

访问外键(Foreign Key)值:

当访问外键字段时,会得到相应的模型对象:


   
>>> b = Book.objects.get(id = 50 ) >>> b.publisher < Publisher: Apress Publishing > >>> b.publisher.website u ' http://www.apress.com/ '

而当通过外键反向追溯时,则会获得一个模型对象的 QuerySet,这个对象与前面一样也支持过滤和分切操作。实现这类访问使用的属性名是由模型名称的小写 + _set 组成的:


   
>>> 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 > ]

 

访问多对多值:

多对多和外键工作方式相同,只不过处理的是QuerySet而不是模型实例:


   
>>> 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 ' ) []

反向查询也可以。 要查看一个作者的所有书籍,使用author.book_set ,就如这样:


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

 

更改数据库模式(DB schema):

先前提到过,syncdb 命令只添加新表,并不会把已有模型的更新同步到数据库中。实际上,django 对已有 模型 与 数据库 之间的对应关系仅作最低限度的检查——即:模型中的字段在数据库中必须有,否则会引发运行时错误(在用户进行查询的时候);至于数据库中是否存在模型里并未定义的字段,django 并不关心,因为数据库中多余的字段并不影响 django 依据模型做正常的查询。(django 在做 orm 到 sql 语句的转化时,总是会明确指出要查询的字段)

基于这种逻辑,想要修改数据库模式应该采用的方法就是:在不引发运行时错误的前提下,按特定顺序分别修改数据库和模型。具体顺序为:

  • 添加字段时:先在开发者环境下修改模型,并通过 sqlall 命令查看修改模型需要使用的 sql 语句,修改数据库与模型并测试通过。然后去部署环境提交这条语句以修改数据库,之后在部署环境修改模型,最后重启 server,使修改生效。这里利用 sqlall 刻意使用了 django 生成的 sql 语句,但这并不是必须的,这只是一个好习惯,如果命令简单,也完全可以自己写这条 sql 命令,但在开发环境下进行测试还是不可少的
  • 添加非 NULL 字段时:因为新添加的字段总是空的,所以如果需要添加非 NULL 字段,就要先添加如 IntegerField(blank=True,null=True) 这样的允许空值字段,然后给他们赋值,再把它们改成非 NULL 字段。
  • 删除字段时:这时的操作顺序与添加字段相反,必须先修改模型,再修改数据库。
  • 删除多对多联系时:先在模型中删除多对多字段,然后在数据库中删除多对多关系表(前面提到过,django 用一张单独的表管理多对多联系)。语句形如:DROP TABLE books_book_authors; (最前面的 books 是 app 名)
  • 删除模型时:从 models 文件中删除模型,然后从数据库中删除表:DROP TABLE books_book; 另外,如果其他表有指向这张表的外键,那么你得先把那些表删了...

 

Managers

在形如 Book.objects.all() 的语句中,objects 作为 models.Model 类的一个特殊属性,他是 models.Manager 的实例。有些时候我们可以通过自定义这个属性来实现一些特别的方法,或修改 manager 返回的 QuerySets 。

 

增加额外的Manager方法:

这需要子类化一个 Manager 对象,再把它绑定到具体的模型中,比如:


   
# 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 >>> Book.objects.title_count( ' python ' ) 18

 

修改初始Manager QuerySets:

manager的基本QuerySet返回系统中的所有对象。 例如,`` Book.objects.all()`` 返回数据库book中的所有书本。我们可以通过覆盖Manager.get_query_set()方法来重写manager的基本QuerySet。 get_query_set()按照你的要求返回一个QuerySet。


   
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.

在这个示例模型中,Book.objects.all()返回了数据库中的所有书本,而Book.dahl_objects.all()只返回了Roald Dahl的书。

这个例子也说明了另外一件事情,就是一个模型可以有多个 manager 属性。你只需要给他们做好命名就可以了(不要再用 objects)。

注意:django 会把模型中定义的第一个 manager 作为默认 manager 使用,如果你重写了任何一个 manager 的话。所以当你给模型添加 manager 的时候,一定记得在最前面把 models.Manager 绑定给 objects 。

 

模型方法:

模型方法就是模型里定义的方法,跟普通类里定义的方法没什么不同。

 

执行原始 SQL 查询:

使用 DB-API 即可。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!