第10章: 数据模型高级进阶

对标题的评论会显示在这里

在第5章里,我们介绍了Django的数据层如何定义数据模型以及如何使用数据库API来创建、检索、更新以及删除记录 在这章里,我们将向你介绍Django在这方面的一些更高级功能。

scarl 08-14 01:40

上一章看到好想睡觉@_@

roger 11-23 11:23

一起加油

liuhao 12-18 06:09

同感,看上一章看得好困

leejaen 03-07 07:44

同ls,上一章直接没看完,后面的标签跳过不看了,用的时候再看

noooop 04-19 16:33

+1 尤其在这个时候

iizax 04-25 07:31

一些不感兴趣的,现在暂时用不上的,可以先只看看标题,以后用到再回来看就好了

erhuabushuo 05-27 01:53

我认为上一章让我很激动,非常有用!

Jason Zhou 06-17 01:45

恭喜大家渡过了上一章~~

swrite jeek 07-08 16:15

我也是,上一章看的好累。发现,不自己亲自动手练习的话,前面的很多就忘记了

chu 07-12 06:41

^O^好了好了,开始第十章~~

imairy 08-06 20:02

被上一张洗脑了

zhayefei 08-09 07:15

看到楼上的那些评论,有些欣慰,上一张后半截,没有认真看,留着以后看吧。

匿名读者 08-19 06:27

勉强看懂了,起码知道以后用到的时候能回来找什么资料,Fighting!

lincy 08-19 10:17

既然大家都来留名了,我也不能落后

imLong 08-19 14:02

上一章躺枪…………同没认真看上一章

danpy5 08-21 04:00

好吧!本来想潜水 没想到这么多 同道中人

zzm88 08-23 06:38

tmd。。。上一章卡了我n天。。。后面包含标签一直没看懂

atlas 10-04 09:43

我擦,上一章看了一半实在看不下去了。。。

gba2z 12-15 07:48

我也看到一半看下去了。。。

debugm 12-24 14:04

卧槽,原来大家都是一个感受啊!!!!

Harry 01-18 07:50

看来大家的感受都一样啊,上一章看到云里雾里,硬着头皮看完的。看来还是多些练习才好,以免学了后面的,忘记前面学习的知识了。

盜盜 01-22 03:15

上一张章躺着中枪了...还是跟着项目来,在用到的时候再回过头来补缺知识点。这已经是我第三次回头看这几个章节了。cool~

kakaxixi 01-26 08:08

原来大家都是一样的感受。。

tianya 02-27 14:37

项目紧急,暂时跳过上一章了,加油!

H 03-05 08:40

上一章阿。

胡杨 03-23 07:36

看书好痛苦啊,还有有大家的注释陪伴,不再是一个人在战斗

qazqwer 05-08 06:16

上一章看的好烦躁

xon 07-01 06:36

上一章算洗粉

曙光旋冰 07-04 05:57

………………原来不止我一个

littlebear 07-22 03:40

终于找到安慰了。

heliar 07-24 03:45

看上一张的时候还去翻源码了。。真是写的很复杂

wind 07-24 12:10

躺枪上一章...看到楼上各位的评论, 终于不迷茫了, 感谢前人解惑

yeahpeng 08-20 08:53

卧槽, 原来大多数都这样啊, 我还含泪看了一天 T T

mihello 08-30 12:25

哈,上一章我断断续续看了一个星期才看完!!!

Timor 09-01 05:55

上一章没学好,能不能把这个学号.....好忐忑。。。。。

ray 09-24 06:43

哦,上一章好痛苦

cyhhao 10-08 06:56

同楼上们,上一章跳过自定义模板标签。。。暂时用不到。。。 留个名~

加油!看了3天看到这,再给自己三天时间看完!! 11-20 13:40

mac 12-13 08:04

加油吧加油吧~慢慢看以后会用到的

jay 01-23 08:42

haha

Johnpang 04-05 07:46

上一章看到困死了 = =

xp 05-20 04:08

加油,认真学没有学不会的.

我擦 07-10 01:42

上一章直接打击我信心了。。

匿名读者 10-02 05:51

上一章确实挺无聊

容嬷嬷 01-06 12:26

我擦,原来大家都一样,哈哈哈

soga 08-15 06:37

直接跳过

cranezhou 09-14 07:42

第9章,看的好吃力,没有看懂中,继续先看下面章节。

bc 01-13 06:12

如果要写 Django 的plugin,上一章非常有用。 我之前看 django_tables2 里面的 {% render_table table %} 就觉得挺神奇。当时不知道 怎么实现。

dddd 06-13 06:26

原来不是我1个人

imbaqian 07-05 08:36

上一章后半部分迷糊,,,,

纸上往事 08-13 08:36

看到你们的评论我就放心了,不然真的以为自己的智商low到没有下限了。

雪雪 10-21 03:07

同上,+1 哭泣 呜~

zgx 06-11 08:14

上一章没看

Lily Paper 11-11 13:02

上一章像摸石头过河--啥也不清楚 哈哈哈

对这一段的评论会显示在这里

相关对象

rcompass 10-31 05:08

test

mihello 08-31 16:22

这一章前半主要讲数据迁移(字段增删,表的删除等),可能由于文章年代久远以至于没有提及——south这个迁移组件,请自行参考: http://www.weiguda.com/blog/2/ 比本文介绍的原始迁移方法方便得多。 后半还没细看,是讲Managers 省略几十字。。。

对这一段的评论会显示在这里

先让我们回忆一下在第五章里的关于书本(book)的数据模型:

rcompass 10-31 05:08

test

对这一段的评论会显示在这里
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
melzg 03-25 06:22

这段代码和以前的一样,看客可直接掠过。

noooop 04-19 16:35

果断先看评注

towersxu 06-24 01:22

对于ManyToManyField表示疑惑

rcompass 10-31 05:11

test

test 10-31 11:06

test

匿名读者 10-31 11:07

test

匿名读者 10-31 11:08

test1111

Harry 01-18 07:51

@ls,怎么一直在 test ?

胡杨 03-23 07:39

情不自禁地点开看大家的评论,哈哈~~~

test 08-21 09:08

test!!!!!!!!!!!..

mihello 08-30 12:29

签到

Django 11-07 02:27

同看过。

我擦 07-10 01:43

+1

kknd li 12-31 17:10

ManyToManyField 表示表与表之间字段的多对多关系,实际操作上是建立一个中间表如table1.column1.ManyToManyField(table2) 就建立一个相关的column1_table2表

对这一段的评论会显示在这里

如我们在第5章的讲解,获取数据库对象的特定字段的值只需直接使用属性。 例如,要确定ID为50的书本的标题,我们这样做:

对这一段的评论会显示在这里
>>> from mysite.books.models import Book
>>> b = Book.objects.get(id=50)
>>> b.title
u'The Django Book'
lincy 08-19 10:51

中文书名乱码咋办呢?

sy 10-23 06:10

在mysql中试试 下边的口令 改下乱码字段的编码方式:ALTER TABLE 表名 MODIFY COLUMN 字段名 VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL;

sy 10-23 07:48

1楼 误解你了 我下边出版商也乱码了 求解

love 03-17 10:37

跟着前面的例子做的话应该是 id= 1 吧 哪有50

PythonAlone 06-03 07:21

情况不明朗~

wangyinlong 09-24 08:47

我的也出现乱码了 按照这个来的 没有sql客户端

wangyinlong 09-24 08:48

我的也出现乱码了 按照这个来的 没有sql客户端,虽然我装了mysql,但是这个用的不是mysql这个啊

对这一段的评论会显示在这里

但是,在之前有一件我们没提及到的是表现为ForeignKeyManyToManyField的关联对象字段,它们的作用稍有不同。

对这一段的评论会显示在这里

访问外键(Foreign Key)值

对这一段的评论会显示在这里

当你获取一个ForeignKey 字段时,你会得到相关的数据模型对象。 例如:

对这一段的评论会显示在这里
>>> b = Book.objects.get(id=50)
>>> b.publisher
<Publisher: Apress Publishing>
>>> b.publisher.website
u'http://www.apress.com/'
ehen 03-30 07:23

这里对应的sql语句是不是"select publisher.website from publisher,book where book.id='50' and book.publisher_id = publisher.id"?

匿名读者 08-16 06:20

id=rownum ?????

glimmer 06-06 07:29

select publisher.website from publisher where publisher.id in (select publisher from book where book.id=50)

对这一段的评论会显示在这里

对于用ForeignKey 来定义的关系来说,在关系的另一端也能反向的追溯回来,只不过由于不对称性的关系而稍有不同。 通过一个publisher 对象,直接获取 books ,用 publisher.book_set.all() ,如下:

对这一段的评论会显示在这里
>>> p = Publisher.objects.get(name='Apress Publishing')
>>> p.book_set.all()
[<Book: The Django Book>, <Book: Dive Into Python>, ...]
Dunboa 11-24 02:33

这里需要先导入Publisher这个对象才能实例化哦~不然会提示没有定义的: from mysite.books.models import Publisher 这样就OK啦~

妖刀 08-30 01:43

此言甚是!

lidawn 04-25 06:08

get方法返回单个对象,其他返回Queryset

heliar 07-24 05:05

book_set这个查询集是有外键关联时候自动产生的么?

cc 11-02 03:23

如果你在设置外键时,不指定外键的反射名ForeignKey.related_name,django orm缺省情况,会用"变量名_set"表示。

Hydra_Code 07-11 12:48

如果设置了related_name好像就不能在使用多端名(小写)加_set 这种形式获取多端的QuerySet对象了?不知道是不是这样

对这一段的评论会显示在这里

实际上,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>]
melody 01-20 13:50

应该为p.book_set.filter(title__icontains='b')

zzm88 08-23 07:39

附议1f,name改为title

PythonAlone 06-03 07:45

把name__icontains 改为title__icontains

容嬷嬷 01-06 12:30

我去看了原文,的确name应改为title http://www.djangobook.com/en/2.0/chapter10.html

对这一段的评论会显示在这里

属性名称book_set是由模型名称的小写(如book)加_set组成的。

对这一段的评论会显示在这里

访问多对多值(Many-to-Many Values)

对这一段的评论会显示在这里

多对多和外键工作方式相同,只不过我们处理的是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')
[]
ethen 03-30 07:54

这段没看懂 不知道对应的sql语句是什么,那个book_author表有什么用,数据是怎样插进去的

chu 07-12 07:00

@楼上:Django对于MTM在自动创建表时会有三张,在此处,即books_book,books_author,books_book_author。以id键关联。那本处的sql可能相当于:select a.first_name,a.last_name from books_authors a,books_book b,books_book_authors c where b.id='50' and c.book_id =b.id and a.id=c.author_id and a.first_name='Adrian'。

www 03-06 14:49

如果用第七章搜素书名的那个例子来查询作者的话应该怎么做呢??

对这一段的评论会显示在这里

反向查询也可以。 要查看一个作者的所有书籍,使用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>]
pShan 02-21 13:35

a.book_set.all()这个Author中没有定义多对多的filed的话 可以直接用 a.book_set吗,上面的模型中是没有定义这个的

匿名读者 08-16 06:40

难道要先import author这个模块?

atlas 10-04 12:40

回楼上,是的

魔坠 03-13 15:41

奇怪,多对多关系应该是对称关系,为何查看书籍的作者用的是模型实例即b.authors.all() 而反向查询用的是book_set?

Staniel 03-25 11:32

不是对称的啊,author 在book表中有直接的记录,还是book的外键,但是author的表中没有book的记录

lidawn 04-25 14:44

在代码中如何给书籍添加作者?

mihello 08-31 04:02

authors = models.ManyToManyField(Author, related_name='books') 使用related_name : a.books.all()

对这一段的评论会显示在这里

这里,就像使用 ForeignKey字段一样,属性名book_set是在数据模型(model)名后追加_set

对这一段的评论会显示在这里

更改数据库模式(Database Schema)

Donald 05-17 15:37

我想知道是否有不重新运行web server就更改模型结构的方法?

在路上 08-29 03:10

更改数据库那节没有看懂,不过用第三方插件south来管理数据库确实非常方便,大家可以看一下:http://blog.chinaunix.net/uid-21633169-id-4356835.html

mihello 08-31 03:22

L正解,可以使用south进行数据migrate。还没正式发布的Django 1.7 已经内置migrate

匿名读者 05-24 03:36

1.7之后的版本使用 python manage.py makemigrations books 生成sql语句,之后用python manage.py migrate books 提交, 很方便 python manage.py makemigrations --empty books

对这一段的评论会显示在这里

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

对这一段的评论会显示在这里

当处理模型修改的时候,将Django的数据库层的工作流程铭记于心是很重要的。

对这一段的评论会显示在这里
  • 如果模型包含一个未曾在数据库里建立的字段,Django会报出错信息。 当你第一次用Django的数据库API请求表中不存在的字段时会导致错误(就是说,它会在运行时出错,而不是编译时)。
  • Django关心数据库表中是否存在未在模型中定义的列。
  • Django关心数据库中是否存在未被模型表示的表格。
phonty 04-13 05:42

编译?好吧,python没有预编译。

fandyst 05-11 07:31

这里应该直接使用数据库的table,而不是将其翻译成“表格”

erhuabushuo 05-27 02:23

这里应该是指得是运行时,而不是启动服务器时。

goat 06-03 09:13

一个解释型语言为啥会出现编译过程?

对这一段的评论会显示在这里

改变模型的模式架构意味着需要按照顺序更改Python代码和数据库。

对这一段的评论会显示在这里

添加字段

徐云东 12-01 13:03

Django1.8有了migrate命令,方便多了。。

对这一段的评论会显示在这里

当要向一个产品设置表(或者说是model)添加一个字段的时候,要使用的技巧是利用Django不关心表里是否包含model里所没有的列的特性。 策略就是现在数据库里加入字段,然后同步Django的模型以包含新字段。

wgzhao 05-20 10:16

现在=》先在

July 03-26 06:46

为啥要先加字段再同步模型?如果先同步模型再添加字段不就没有这个鸡蛋问题了么?

杜浩 05-30 02:02

@楼上 如果先同步模型的话,模型包括未曾在数据库里建立的字段会报错的

richard yang 05-19 02:48

python分行\n html分行<br> 第三行

richard yang 05-19 02:49

对这一段的评论会显示在这里

然而 这里有一个鸡生蛋蛋生鸡的问题 ,由于要想了解新增列的SQL语句,你需要使用Django的 manage.py sqlall命令进行查看 ,而这又需要字段已经在模型里存在了。 (注意:你并 不是非得使用与Django相同的SQL语句创建新的字段,但是这样做确实是一个好主意 ,它能让一切都保持同步。)

chu 07-12 07:03

“鸡生蛋蛋生鸡”

Aaron 09-25 11:54

上段鸡生蛋,这段蛋生鸡

匿名读者 12-04 02:10

看评论笑尿2333333333333

Yufogchan 04-03 15:05

django 1.10以上版本,已经内置migrate,查询说起来语句,可以使用如下命令行python manger.py sqlmigrate theapp 0001,theapp是你创建的app

匿名读者 07-19 05:25

不是先makemigration然后migrate就可以了吗

richard yang 05-19 02:46

在Django2.0里,sqlall已被废弃,新的使用方法如下: manage.py makemigrations appname [,migrations]生成迁移数据(在appname/migrations下) manage.py makemigrations appname migrations 查看sql语句 manage.py migrate appname 执行语句修改数据库

对这一段的评论会显示在这里

这个鸡-蛋的问题的解决方法是在开发者环境里而不是发布环境里实现这个变化。 (你使用的是测试/开发环境,对吧?)下面是具体的实施步骤。

对这一段的评论会显示在这里

首先,进入开发环境(也就是说,不是在发布环境里):

对这一段的评论会显示在这里
  1. 在你的模型里添加字段。
  2. 运行 manage.py sqlall [yourapp] 来测试模型新的 CREATE TABLE 语句。 注意为新字段的列定义。
  3. 开启你的数据库的交互命令界面(比如, psqlmysql , 或者可以使用 manage.py dbshell )。 执行 ALTER TABLE 语句来添加新列。
  4. 使用Python的manage.py shell,通过导入模型和选中表单(例如, MyModel.objects.all()[:5] )来验证新的字段是否被正确的添加 ,如果一切顺利,所有的语句都不会报错。
匿名读者 08-16 06:48

通过导入模型??

匿名读者 08-19 06:40

就是select语句,“导入”这个翻译不好吧,用“查询”比较好

Aaron 09-25 11:56

导入模型就是import,例如你在Books里添加了新字段,就要先把Books导入,才能调用.objects

对这一段的评论会显示在这里

然后在你的产品服务器上再实施一遍这些步骤。

对这一段的评论会显示在这里
  1. 启动数据库的交互界面。
  2. 执行在开发环境步骤中,第三步的ALTER TABLE语句。
  3. 将新的字段加入到模型中。 如果你使用了某种版本控制工具,并且在第一步中,已经提交了你在开发环境上的修改,现在,可以在生产环境中更新你的代码了(例如,如果你使用Subversion,执行svn update
  4. 重新启动Web server,使修改生效。
cjyfff 03-13 14:33

怎么上面是先在模型增加字段再修改数据库,这里是先修改数据库再在模型里增加字段的呢?

July 03-26 06:52

这主要是考虑到在你改模型的同时,可能会有用户查询数据库的问题。上面说了,你先改数据库没问题,但如果你先改模型,数据库还没来得及改,那么这时候如果有用户用新模型查询了旧数据库,就会出错。所以在开发环境下先利用 sqlall 查看改数据库的 sql 命令,再去部署环境里提交这条命令,最后在部署环境里修改模型,这样整个过程都不会出错。

July 03-26 06:59

上面这段是我猜的。看到下面说还要重启 server 才能使修改生效我就迷茫了。如果不重启就不生效,那还刻意地先改数据库干嘛?...

July 03-26 08:08

看下面删除字段的说法,是因为当模型里的列比数据库中的多时,django会立即报出异常。

July 03-26 08:20

怎么不能删评论的呀,重新看了一下,本节开头提到过,这是一个运行时错误,所以最前面的一条评论成立。

对这一段的评论会显示在这里

让我们实践下,比如添加一个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
匿名读者 10-31 11:11

test1111

cold 12-31 10:07

加粗标记(**xx**)没有被正确解析:http://sphinx-doc-zh.readthedocs.org/en/latest/rest.html#inline-markup

nginxs 12-13 04:57

表创建好以后不可以添加外键,有什么办法吗

对这一段的评论会显示在这里

(注意 阅读第六章的“设置可选字段”以及本章下面的“添加非空列”小节以了解我们在这里添加blank=Truenull=True的原因。)

对这一段的评论会显示在这里

然后,我们运行命令manage.py sqlall books 来查看CREATE TABLE语句。 语句的具体内容取决与你所使用的数据库, 大概是这个样子:

jack 06-17 04:40

应该是运行‘python manage.py sqlall books’

aa 07-17 08:47

aa

bb 11-16 00:17

qq

stonycat 08-03 03:16

Django1.9之后就不用了 换成migrate

对这一段的评论会显示在这里
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
);
WildCat 05-14 08:15

CREATE TABLE "books_book" ( "id" integer NOT NULL PRIMARY KEY, "title" varchar(100) NOT NULL, "publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id"), "publication_date" date, "num_pages" integer )

may 06-22 15:12

Django1.8版本以上使用"manage.py sqlall books"这句命令的时候如果在books目录里有migrate文件夹得先删除之。

mr.liu 09-01 08:46

使用新方法咯,migrate,用来迁移数据库。 用法:migrate app makemigrations,用来检测数据库变更和生成数据库迁移文件。 用法:makemigratioins app sqlmigrate,用来把数据库迁移文件转换成数据库语言

对这一段的评论会显示在这里

新加的字段被这样表示:

对这一段的评论会显示在这里
"num_pages" integer NULL
zqz 09-22 02:32

`num_pages` integer 没有null

xepher 12-27 01:55

默认就是null,显示与不显示都是一样的

Aaron 09-25 11:50

argee!

对这一段的评论会显示在这里

接下来,我们要在开发环境上运行数据库客户端,如果是PostgreSQL,运行 psql,,然后,我执行如下语句。

一本正经 02-01 12:56

直接执行 python manage.py dbshell

hekun 08-04 14:58

这语句可以在mysql中执行吧,怎么我一执行就卡住了(光标一直闪,可是没有任何信息输出)。

H 03-06 07:04

这里应该是先同步数据库:python manage.py syncdb 后再运行ALTER TABLE....

牛三金 04-25 14:21

前文是使用SQLite数据库做例子,而这里是以PostgreSQL来举例添加数据库表字段。两者操作不通用,请自行上网查SQLite添加字段的方法。

牛三金 04-25 14:28

登录SQLite官方网站下载GUI程序,直接对db.sqlite3进行修改。 http://www.sqlite.org/

qwe 01-21 06:37

ALTER TABLE books_book ADD COLUMN num_pages integer;

qwe 01-21 06:37

SyntaxError: invalid syntax

vitlint 08-23 10:25

http://www.cnblogs.com/linxiyue/p/4106514.html 1.11版本,添加的具体方法在这里。可以参考来看看

对这一段的评论会显示在这里
ALTER TABLE books_book ADD COLUMN num_pages integer;
对这一段的评论会显示在这里

添加 非NULL 字段

对这一段的评论会显示在这里

这里有个微妙之处值得一提。 在我们添加字段num_pages的时候,我们使用了 blank=True 和 null=True 选项。 这是因为在我们第一次创建它的时候,这个数据库字段会含有空值。

对这一段的评论会显示在这里

然而,想要添加不能含有空值的字段也是可以的。 要想实现这样的效果,你必须先创建 NULL 型的字段,然后将该字段的值填充为某个默认值,然后再将该字段改为 NOT NULL 型。 例如:

对这一段的评论会显示在这里
BEGIN;
ALTER TABLE books_book ADD COLUMN num_pages integer;
UPDATE books_book SET num_pages=0;
ALTER TABLE books_book ALTER COLUMN num_pages SET NOT NULL;
COMMIT;
wyatt 12-29 09:52

我在执行第四句和第五句时不成功

wyatt 12-29 10:11

sqlite> ALTER TABLE books_book ALTER COLUMN num_pages SET NOT NULL; Error: near "ALTER": syntax error

vayn 03-05 23:48

@wyatt Only the RENAME TABLE and ADD COLUMN variants of the ALTER TABLE command are supported. Other kinds of ALTER TABLE operations such as DROP COLUMN, ALTER COLUMN, ADD CONSTRAINT, and so forth are omitted.

lionf 04-04 10:31

mysql添加不能含有空值的字段直接这样就行了:alter table books_book add column num_pages integer not null

roger 11-23 16:02

这里不是添加,是把原来默认为空值的改成非空

roger 11-23 16:10

我搞错了

qq_345421243 05-16 06:31

to lionf 你那样是表里没有记录才能那样,表里面有记录的话你这样不是要报错么

YG 07-09 14:13

用mysql的朋友们,我觉得可以用 alter table books_book add column num_pages not null default 0

mr.liu 09-06 06:01

在添加了字段 num_pages ,该字段是“NULL”,所以在这里设置了一个默认值‘0’,然后改变字段为NOT NULL。就是消除该字段‘NULL’

对这一段的评论会显示在这里

如果你这样做,记得你不要在模型中添加 blank=True 和 null=True 选项。

对这一段的评论会显示在这里

执行ALTER TABLE之后,我们要验证一下修改结果是否正确。启动python并执行下面的代码:

对这一段的评论会显示在这里
>>> from mysite.books.models import Book
>>> Book.objects.all()[:5]
对这一段的评论会显示在这里

如果没有异常发生,我们将切换到生产服务器,然后在生产环境的数据库中执行命令ALTER TABLE 然后我们更新生产环境中的模型,最后重启web服务器。

lincy 08-19 12:52

可不可以理解成其实就是先改数据库,然后再改模型?如果是的话,那可不可以用第三方管理工具直接先改数据库,然后再直接改模型?

njw 12-23 09:00

我也有和楼上一样的理解,希望有人解答!

www 03-03 15:49

好像没那么复杂啊,我就添加字段后,就用了'python manage.py makemigrations'和'python manage.py syncdb',就自动在数据库添加字段了,这段到底是什么意思?

zbli 08-17 09:55

楼上,好像是因为django1.7的新功能,不知道是不是这个教程版本太低的缘故

对这一段的评论会显示在这里

删除字段

lincy 08-19 12:47

编辑字段怎么办?

atlas 10-04 13:05

这里的"删除字段,然后重新启动你的web服务器。"是不是删除模型中定义的字段,然后删除数据库中的?

牛三金 04-25 14:30

先删除models.py里相应的字段,然后再删除数据库里的相应字段。

richard yang 05-19 17:44

改变数据库的话,还是改变模型后执行makemigrations和migrate更加安全吧

对这一段的评论会显示在这里

从Model中删除一个字段要比添加容易得多。 删除字段,仅仅只要以下几个步骤:

对这一段的评论会显示在这里

删除字段,然后重新启动你的web服务器。

对这一段的评论会显示在这里

用以下命令从数据库中删除字段:

对这一段的评论会显示在这里
ALTER TABLE books_book DROP COLUMN num_pages;
zxw 07-27 03:28

不行,sqlite不支持直接删除字段

匿名读者 08-19 06:44

SQLite只能通过一些GUI管理工具来更改字段了(实际上就是删除现有的table重建一个)

njw 12-23 09:06

可以尝试安装Django-South来解决问题

littlebear 07-22 07:30

感觉这边不用执行drop这句,你在model里删除了,然后再用python manage.py sqlall books 查看。字段就删除了,所以不执行也是可以的把

heliar 07-24 07:02

楼上那样数据库的数据都还在的啊

mihello 08-31 16:11

再次建议使用 south 这个来进行数据结构更改: http://www.weiguda.com/blog/2/

对这一段的评论会显示在这里

请保证操作的顺序正确。 如果你先从数据库中删除字段,Django将会立即抛出异常。

对这一段的评论会显示在这里

删除多对多关联字段

对这一段的评论会显示在这里

由于多对多关联字段不同于普通字段,所以删除操作是不同的。

mr.liu 09-01 10:34

删除多对多字段后,使用 python manage.py makemigrations books 生成改动文件时报错:“<class 'books.admin.BookAdmin'>: (admin.E019) The value of 'filter_horizontal[0]' refers to 'authors', which is not an attribute of 'books.Book'.”

mr.liu 09-02 03:21

发现报错原因了,在admin.py方法中,之前跟着实例添加了“filter_horizontal = ('authors',)”,删除即可。

对这一段的评论会显示在这里

从你的模型中删除ManyToManyField,然后重启web服务器。

对这一段的评论会显示在这里

用下面的命令从数据库删除关联表:

对这一段的评论会显示在这里
DROP TABLE books_book_authors;
对这一段的评论会显示在这里

像上面一样,注意操作的顺序。

对这一段的评论会显示在这里

删除模型

对这一段的评论会显示在这里

删除整个模型要比删除一个字段容易。 删除一个模型只要以下几个步骤:

nono 12-21 08:48

从文件models.py中删除你想要删除的模型,然后重启web服务器

对这一段的评论会显示在这里

从文件中删除你想要删除的模型,然后重启web 服务器models.py

pylemon 02-08 08:03

从 models.py 文件中删除你想要删除的模型,然后重启 web 服务器

匿名读者 08-16 07:18

步骤 1、从model文件中删除你想要删除的模型,然后重启web 服务器models.py 2、从数据库中删除对应的表

对这一段的评论会显示在这里

然后用以下命令从数据库中删除表:

对这一段的评论会显示在这里
DROP TABLE books_book;
对这一段的评论会显示在这里

当你需要从数据库中删除任何有依赖的表时要注意(也就是任何与表books_book有外键的表 )。

regsbbs 02-27 06:51

这句话翻译的不对!原话是Note that you might need to remove any dependent tables from your database first — e.g., any tables that have foreign keys to books_book. 翻译后意思应该让人理解成是:注意,你在删除某个模型的时候,如果这个模型(相应的,模型从数据库角度而言就是一个数据库。)有外键,也就是有其他模型(数据库)与之关联,那么应该先删除相关的模型先,再删除本来想删除的模型。

brightne 12-13 07:43

我觉得应该是, 如果要删除一个模型,先注意是不是有其它的模型引用它作为外键,如果有的话,请先删除引用它的模型,再删除你想要删除的模型 。。。不过,我想应该很少有人这么做, production环境中这个就是灾难

Wing Shine 11-29 08:34

注意:你可能首先需要从数据库中删除任何(与你要删除的表)有依赖关系的表,例如,任何有指向books_book(表)外键的表。

对这一段的评论会显示在这里

正如在前面部分,一定要按这样的顺序做。

needle 10-21 07:50

修改总结: django在运行时, 模型所表示的结构必须实时是数据库实际结构的子集,否则会立即抛异常。

匿名读者 04-29 06:20

在新版本中(我的1.8),更改数据库这些操作,好像先修改models,然后makemigrations和migrate就可以了。

对这一段的评论会显示在这里

Managers

对这一段的评论会显示在这里

在语句Book.objects.all()中,objects是一个特殊的属性,需要通过它查询数据库。 在第5章,我们只是简要地说这是模块的manager 。现在是时候深入了解managers是什么和如何使用了。

qq_345421243 05-16 06:42

第5章你有跟我讲过manager吗?我怎么不记得呢,我可是从头看过来的啊

老三 12-17 12:36

我也不记得有讲。

胡杨 03-23 08:30

别太认真了,之前确实没有讲过Manager.

July 03-26 13:55

是 models.manager 吧

nono 12-26 15:08

之前说的是objects属性是models的「管理器」,把这里的「manager」给翻译了,这里指的不是manager.py

虎头蔓 01-22 08:39

只能说提到过就是讲过!

对这一段的评论会显示在这里

总之,模块manager是一个对象,Django模块通过它进行数据库查询。 每个Django模块至少有一个manager,你可以创建自定义manager以定制数据库访问。

对这一段的评论会显示在这里

下面是你创建自定义manager的两个原因: 增加额外的manager方法,和/或修manager返回的初始QuerySet。

邓鹏 04-04 08:54

和/或修manager->和/或修改manager

zhouyi 02-20 10:58

和/或修 改为 或者修改

对这一段的评论会显示在这里

增加额外的Manager方法

对这一段的评论会显示在这里

增加额外的manager方法是为模块添加表级功能的首选办法。 (至于行级功能,也就是只作用于模型对象实例的函数,一会儿将在本章后面解释。)

对这一段的评论会显示在这里

例如,我们为Book模型定义了一个title_count()方法,它需要一个关键字,返回包含这个关键字的书的数量。 (这个例子有点牵强,不过它可以说明managers如何工作。)

对这一段的评论会显示在这里
# 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
Allan 09-15 03:49

出现了AttributeError: type object 'Book' has no attribute 错误,求解答

chain 03-08 08:43

@Allan 以下测试时,需要from mysite.books.models import Book

soda 08-14 02:47

一定注意BookManager与Book两个类的先后顺序

zlleah 01-18 04:48

@Allan ,确实需要注意两个累的顺序,要把定义的管理器方法放在调用之前,这样就不会报BookManager未定义的错误了

qq_345421243 05-16 06:56

确实要注意顺序,又测试了一遍

Aaron 09-25 12:13

agree!

Rhysol 09-08 03:41

我注意了顺序为什么还是报错 has no attribute

匿名读者 10-15 10:38

NameError: name 'BookManager' is not defined 啥情况

对这一段的评论会显示在这里

有了这个manager,我们现在可以这样做:

对这一段的评论会显示在这里
>>> Book.objects.title_count('django')
4
>>> Book.objects.title_count('python')
18
xepher 12-27 02:29

注意重启django服务器

qq_345421243 05-16 06:56

django1.5版本无须重启服务器

hekun 08-05 02:54

AttributeError: 'Manager' object has no attribute 'title_count'

sy 10-23 09:47

从新 import一下

newpython 03-12 14:33

退出交互模式,在重新python manage.py shell 进入交互模式

ttcmk 11-22 02:04

AttributeError: 'BookManager' object has no attribute 'fliter'

jay 01-26 07:02

@ls 你的‘filter’拼错了

qwe 01-21 07:22

FieldError: Cannot resolve keyword 'title_icontains' into field. Choices are: authors, id, num_pages, publication_date, publisher, publisher_id, title

对这一段的评论会显示在这里

下面是编码该注意的一些地方:

对这一段的评论会显示在这里
  • 我们建立了一个BookManager类,它继承了django.db.models.Manager。这个类只有一个title_count()方法,用来做统计。 注意,这个方法使用了self.filter(),此处self指manager本身。
  • 我们把BookManager()赋值给模型的objects属性。 它将取代模型的默认manager(objects)如果我们没有特别定义,它将会被自动创建。 我们把它命名为objects,这是为了与自动创建的manager保持一致。
匿名读者 01-19 21:51

所以说Book.objects.all()方法已经不能用了么?

cyril 04-04 13:15

应该还能用,新的Manager是继承django.db.models.Manager,所以应该继承了它的所有方法

huhu 06-29 08:58

不能用了,所以这里重新定义 objects = models.Manager()

chg_self 07-18 02:11

chg_self 07-18 02:11

Book.objects.all()必须可用啊 这是最基本的功能

对这一段的评论会显示在这里

为什么我们要添加一个title_count()方法呢?是为了将经常使用的查询进行封装,这样我们就不必重复编码了。

对这一段的评论会显示在这里

修改初始Manager QuerySets

对这一段的评论会显示在这里

manager的基本QuerySet返回系统中的所有对象。 例如,Book.objects.all() 返回数据库book中的所有书本。

zxw 07-27 04:32

A manager’s base QuerySet returns all objects in the system. For example, Book.objects.all() returns all books in the book database

胡杨 03-23 09:04

“A manager’s base QuerySet returns all objects in the system. For example, Book.objects.all() returns all books in the book database" 这么翻译将更加容易理解: Manager类内置的查询游标(QuerySet)将会返回数据库系统中的所有结果。比如,Book.objects.all()这个游标将返回数据库中所有的书本。

胡杨 03-23 09:06

Manager类内置的查询记录集(QuerySet)将会返回数据库系统中的所有结果。比如,Book.objects.all()这个游标将返回数据库中所有的书本。 【不好意思更正一下】

aida 09-10 09:25

objects属性为django自动添加

对这一段的评论会显示在这里

我们可以通过覆盖Manager.get_query_set()方法来重写manager的基本QuerySet。 get_query_set()按照你的要求返回一个QuerySet。

aida 09-10 09:26

Book.objects.all()调用的是Manager.get_query_set()方法

对这一段的评论会显示在这里

例如,下面的模型有 两个 manager。一个返回所有对像,另一个只返回作者是Roald Dahl的书。

对这一段的评论会显示在这里
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.**
vayn 03-06 00:37

注意,之前书里所用的Book model是这样的:authors = models.ManyToManyField(Author)。但现在例子里变成了 author = models.CharField(max_length=50)。所以调用DahlBookManager的all方法会报错。

Lang 09-09 09:26

楼上正解。如果不想改字段来测试的话,可以修改author='Roald Dah1'为authors="1",1是books_author表中ID为1的那一行。

melzg 03-25 08:06

还真没注意到楼上说的代码发生变化了。不过,照2楼说的改了,还是不行。报下面的错,没搞明白原因: >>> Book.dah1_objects.all() Traceback (most recent call last): File "<console>", line 1, in <module> AttributeError: type object 'Book' has no attribute 'dah1_objects'

weetao 05-17 07:08

@melzg:改成 self.dahl_objects=DahlBookManager()试试

s 07-06 02:04

重新启动

arnold 07-09 06:33

filter(authors=?),?应该是author对象或是代表author的int型id,填字符串参数会报错

trueyao 03-28 02:43

除了1L说的问题外,注意到django1.6中,get_query_set()方法变成了get_queryset().

木易水 09-25 03:19

filter(author__first_name = '')

freesquirrel 10-21 15:59

1L的问题和get_query_set被废弃掉,改为get_queryset class DahlBookManager(models.Manager): def get_queryset(self): # 添加下面这句,其实就是先查出author实例 author=Author.objects.get(name='Roald Dahl') # 把author赋值给authors return super(DahlBookManager, self).get_query_set().filter(author=author)

ifaint 01-04 14:06

尼玛,改了数据model....

mr.liu 09-09 03:08

super(DahlBookManager,self).get_query_set().filter(author='Roald Dahl'),这段代码意思:调用‘DahlBookManager’父类的get_query_set.filter方法

mr.liu 09-09 03:24

@freesquirrel 你确定你的方法行得通?你有实际操作过?别忽悠人!

huhu 06-29 09:06

既然DahlBookManager继承了它的父类,那它应该就有父类的方法啊,为什么还要调用它的父类呢?

对这一段的评论会显示在这里

在这个示例模型中,Book.objects.all()返回了数据库中的所有书本,而Book.dahl_objects.all()只返回了一本. 注意我们明确地将objects设置成manager的实例,因为如果我们不这么做,那么唯一可用的manager就将是dah1_objects。

宾临城下 05-16 11:05

而Book.dahl_objects.all()只返回了一本,这句话错了,是只返回作者为 Roald Dahl 的书。

ifaint 01-04 13:55

这里把dahl写成了dah1,害死人不贘命啊嘿嘿

匿名读者 01-19 21:54

你们都...好严谨.好认真..佩服!!!

feng 11-11 05:52

在Django2.2中,Book.dahl_objects.all()返回的与Book.objects.all()一样,而Book.dahl_objects.get_qurey_set()才仅返回dahl的书,无论DahlBookManager的过滤器改了好几种表达方式还是同样的结果,authors__id,authors__first_name

对这一段的评论会显示在这里

当然,由于get_query_set()返回的是一个QuerySet对象,所以我们可以使用filter(),exclude()和其他一切QuerySet的方法。 像这些语法都是正确的:

对这一段的评论会显示在这里
Book.dahl_objects.all()
Book.dahl_objects.filter(title='Matilda')
Book.dahl_objects.count()
horizonshd 09-19 13:47

在Django-1.11.3中,不知为什么 Book.dahl_objects.all() 还是返回了所有的书籍

horizonshd 09-19 14:00

可以正常的输出了,在更改了models.py文件后,需要退出 shell ,重新开启再测试

horizonshd 09-19 14:01

在Django-1.11.3中,是 get_queryset()

匿名读者 10-15 13:25

type object 'Book' has no attribute 'dahl_objects'不对啊

对这一段的评论会显示在这里

这个例子也指出了其他有趣的技术: 在同一个模型中使用多个manager。 只要你愿意,你可以为你的模型添加多个manager()实例。 这是一个为模型添加通用滤器的简单方法。

horizonshd 09-19 14:03

这个用法应该在实际应用中很有用

对这一段的评论会显示在这里

例如:

对这一段的评论会显示在这里
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()
Rex.Re 01-15 04:46

这个真的很方便,很强大。

winkidney 06-06 04:21

此处get_query_set方法是覆盖了models.Manager默认方法吗?,为何调用men= MaleManager()就能执行get_query_set呢?

zxw 07-27 04:51

super()的用法?

wishout 10-16 12:36

能不能把super(MaleManager, self).去掉?

wishout 10-16 12:39

super的用法 http://bestchenwu.iteye.com/blog/1044848

lll 06-24 16:20

真的很强大啊

admin 10-26 06:58

对初学者来说,还是别介绍这些花样,增加学习负担,和继续下去的难度

wangyue 11-16 02:55

http://djangobook.py3k.cn/appendixC/ 附录C 解释了QuerySet

对这一段的评论会显示在这里

这个例子允许你执行Person.men.all()Person.women.all()Person.people.all() 查询,生成你想要的结果。

对这一段的评论会显示在这里

如果你使用自定义的Manager对象,请注意,Django遇到的第一个Manager(以它在模型中被定义的位置为准)会有一个特殊状态。 Django将会把第一个Manager 定义为默认Manager ,Django的许多部分(但是不包括admin应用)将会明确地为模型使用这个manager。 结论是,你应该小心地选择你的默认manager。因为覆盖get_query_set() 了,你可能接受到一个无用的返回对像,你必须避免这种情况。

winkidney 06-06 04:34

**# 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')** 此处用super调用了父类的方法,同时使用get_query_set()返回一个queryset对象 queryset对象,可以使用filter(),exclude()等一系列针对queryset的方法。 .还有 all() 方法。这个方法返回返回数据库中所有的记录。 尽管这个对象 看起来 象一个列表(list),它实际是一个 QuerySet 对象, 这个对象是数据库中一些记录的集合。 附录C将详细描述QuerySet。 现在,我们就先当它是一个仿真列表对象好了。 问题: def get_query_set(self): """Returns a new QuerySet object. Subclasses can override this method to easily customize the behavior of the Manager. """ return QuerySet(self.model, using=self._db) 此处get_query_set方法是覆盖了models.Manager默认方法吗?,为何调用men= MaleManager()就能执行get_query_set呢? 根据以上这段取自django的源码可以发现,get_query_set方法是原本存在的 ,此处被覆盖了,调用类的行为被默认为调用了get_query_set方法,所以可以直接调用Person.men.all(),因为all()方法中包含了get_query_set的执行

wishout 10-16 12:57

谢谢楼上~

haobo 12-17 08:37

是调用all()方法时会默认调用get_query_set()

haobo 12-17 08:41

所以要在Model中明确地将objects设置成manager的实例

imbaqian 07-05 10:03

上一章后半部分迷糊,,,,

对这一段的评论会显示在这里

模型方法

对这一段的评论会显示在这里

为了给你的对像添加一个行级功能,那就定义一个自定义方法。 有鉴于manager经常被用来用一些整表操作(table-wide),模型方法应该只对特殊模型实例起作用。

lvshiting 07-17 17:31

不太明白这个"行处理方法"

Rex.Re 01-15 05:05

可以这样理解: 修改【Manager方法】是覆盖了基类中的get_query_set方法或者是对基类进行扩展,因此,所有继承自models.Model的类都会受到影响。 【模型方法】只是对当前类进行了修改和扩展,因此只会影响到当前模型。

qq_345421243 05-16 09:02

我觉得行级功能其实就是一个自定义方法用来自定义显示字段内容而已 没啥功能

沈世军 10-21 06:18

不同意二楼观点:上面明明是一个实例方法,况且Python里面没有更改宇哥类的方法,那么所有子类的方法就改变的说法。

匿名读者 11-12 08:14

2L的说法貌似 从语法上来说就有问题

匿名读者 02-25 02:45

django model的表级功能,增加额外的Manager方法,这个方法的操作属于是对整表的操作,比如查询,是传入参数给方法对整表查询得出结果。 django model的行级功能,增加模型方法,它是赋予在你的查询结果上面的,就如下面例子,根据查询条件得到结果,根据结果使用模型方法,这属于对行记录的操作。

对这一段的评论会显示在这里

这是一项在模型的一个地方集中业务逻辑的技术。

对这一段的评论会显示在这里

最好用例子来解释一下。 这个模型有一些自定义方法:

对这一段的评论会显示在这里
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 "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)
lll 06-24 16:27

'IL', 'WI', 'MI', 'IN', 'OH', 'IA', 'MO'是美国中西部的州的缩写吗?

mihello 09-03 06:06

django 1.6: django.contrib.localflavor 已经移除了,现在是作为一个独立的包 django-localflavor 参考: https://django-localflavor.readthedocs.org/en/latest/

freesquirrel 10-22 03:12

>django1.6要更改代码 ubuntu:pip install django-localflavor,然后from localflavor.us.models import USStateField,下以代码才能运行,因为loalflavor独立成第三方开发包了

freesquirrel 10-22 03:13

settings要设置安装localflavor,具体参考2L给的链接

mr.liu 09-09 08:25

property 方法相当于将类中_get_full_name方法当作属性使用,Person实例化后,p=Person(),p.full_name就可获取_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'
对这一段的评论会显示在这里

执行原始SQL查询

对这一段的评论会显示在这里

有时候你会发现Django数据库API带给你的也只有这么多,那你可以为你的数据库写一些自定义SQL查询。 你可以通过导入django.db.connection对像来轻松实现,它代表当前数据库连接。 要使用它,需要通过connection.cursor()得到一个游标对像。 然后,使用cursor.execute(sql, [params])来执行SQL语句,使用cursor.fetchone()或者cursor.fetchall()来返回记录集。 例如:

对这一段的评论会显示在这里
>>> 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']
anhuizf 02-18 05:59

这里WHERE last_name=%s""",['Lennon']) 无法执行, 应该为WHERE last_name=?""",("Lennon")) 才能执行,我用的是adodbapi

Aaron 09-25 13:01

@anhuizf 官方文档中第二个参数是列表类型

harvey 05-10 14:06

可以执行啊,需要表中存在last_name = 'Lennon'

对这一段的评论会显示在这里

connectioncursor几乎实现了标准Python DB-API,你可以访问[http://www.python.org/peps/pep-0249.html](http://www.python.org/peps/pep-0249.html) <[http://www.python.org/peps/pep-0249.html](http://www.python.org/peps/pep-0249.html)>__来获取更多信息。 如果你对Python DB-API不熟悉,请注意在cursor.execute() 的SQL语句中使用“%s” ,而不要在SQL内直接添加参数。 如果你使用这项技术,数据库基础库将会自动添加引号,同时在必要的情况下转意你的参数。

匿名读者 08-16 07:47

请注意在cursor.execute() 的SQL语句中使用`` “%s”`` ,而不要在SQL内直接添加参数

foo 05-06 17:14

"转意"应为"转义"

对这一段的评论会显示在这里

不要把你的视图代码和django.db.connection语句混杂在一起,把它们放在自定义模型或者自定义manager方法中是个不错的主意。 比如,上面的例子可以被整合成一个自定义manager方法,就像这样:

对这一段的评论会显示在这里
from django.db import connection, models

class 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()
vayn 03-06 10:51

return [row[0] for row in cursor.fetchone()] 只会返回结果的第一个字母

Lang 09-09 10:40

应该是return [row[0] for row in cursor.fetchall()]

soda 08-14 06:07

其实吧[0]去掉就行了 ,,get只能有一个返回值 否则会报错,所以可以省去[0] 不过楼上的也是好方法

sytuzhouyong 11-27 11:17

cursor.execute(""" SELECT DISTINCT title FROM books_book WHERE title LIKE '%%%s%%'""" % keyword) 这样就可以实现模糊查询了

徐云东 12-01 14:13

这个%s前后要加单引号吧,要不然SQL认不得啊。。

徐云东 12-01 14:13

"Unknown column 'Tsui' in 'where clause'" %s前后没有加单引号出现的错误。。

徐云东 12-01 14:19

fetchone返回的是一个元组,所以需要row[0],不过不懂为什么要返回一个列表,这个函数的功能,不就是获取一个名字吗,返回一个字符串就好啦!

对这一段的评论会显示在这里

然后这样使用:

对这一段的评论会显示在这里
>>> Person.objects.first_names('Lennon')
['John', 'Cynthia']
Ode2free 05-10 18:23

没有动手做,印象不深脑袋迷糊了

hekun 08-05 07:53

AttributeError: 'Manager' object has no attribute 'first_names'

xavier 07-31 14:30

这一节还挺短。

glimmer 06-06 11:11

打卡

imbaqian 07-05 12:15

都18年了~

对这一段的评论会显示在这里

接下来做什么?

对这一段的评论会显示在这里

下一章 我们将讲解Django的通用视图框架,使用它创建常见的网站可以节省时间。

对这一段的评论会显示在这里