假如, 我们在 views.py 中写如下代码:
from django.shortcuts import render_to_response
import MySQLdb
def book_list(request):
db = MySQLdb.connect(user='me', db='mydb', passwd='secret', host='localhost')
cursor = db.cursor()
cursor.execute('SELECT name FROM books ORDER BY name')
names = [row[0] for row in cursor.fetchall()]
db.close()
return render_to_response('book_list.html', {'names': names})
这个方法可用, 但很快一些问题将出现在你面前:
1.我们将数据库连接参数硬行编码于代码之中。 理想情况下,这些参数应当保存在 Django 配置中.
2.我们不得不重复同样的代码: 创建数据库连接、创建数据库游标、执行某个语句、然后关闭数据库。 理想情况下,我们所需要应该只是指定所需的结果
3.它把我们栓死在 MySQL 之上。 如果过段时间,我们要从 MySQL 换到 PostgreSQL,就不得不使用不同的数据库适配器(例如 psycopg 而不是MySQLdb ),改变连接参数,根据 SQL 语句的类型可能还要修改SQL 。 理想情况下,应对所使用的数据库服务器进行抽象,这样一来只在一处修改即可变换数据库服务器。 (如果你正在建立一个开源的Django应用程序来尽可能让更多人使用的话,这个特性是非常适当的。
数据库的配置 settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'my_db',
'USER': 'Leon',
'PASSWORD': '123',
'HOST': 'localhost',
'PORT': 3306,
}
}
设置好后, 可以通过 python manager.py shell 看看能否打开 shell 来判断是否连接成功.
Django模型
django 模型是用python代码形式表述的数据在数据库中的定义, 对数据层来说比如 create table 语句执行的是python代码而不是SQL.
Django 用模型在后台执行SQL代码并把结果用python数据结构来描述(完全抽象出了SQL代码)
Django 能够让你不写SQL语句, 而不是像常规模式 写会 java -> SQL -> java -> SQL
另外, 还有一个好处就是可以扩展到不同的数据库平台. 因为 表, 列的定义, 都是来自于 Django 的 models的同步.
缺点: python代码和数据库表的同步问题, 如果你修改了一个Django 模型, 你要自己来修改数据库来保证和模型同步. (如果是传统方法, 我们只要修改数据库一个地方就可以了)
提示: Django 提供了实用工具来从现有的数据库表自动扫描生成模型. 这对已有数据库来说, 非常便捷.
举例
我们来假定下面的这些概念、字段和关系:
一个作者有姓,有名及email地址。
出版商有名称,地址,所在城市、省,国家,网站。
书籍有书名和出版日期。 它有一个或多个作者(和作者是多对多的关联关系[many-to-many]), 只有一
个出版商(和出版商是一对多的关联关系[one-to-many],也被称作外键[foreign key])
在 models.py中
每个数据类型是 django.db.models.Model 的子类, 它的父类Model包含所有必要的和数据库交互的方法.
每个模型相当于数据库表. 每个属性相当于一个字段. 属性名就是字段名, 它的类型(例如 CharField) 相当于数据库字典中的 varchar 类型, 例如 Publisher 这个模型, 它相当于在DB中创建了一个表:
create table publisher(
id number not null primary key,
name varchar(30) not null,
address varchar(30) not null,
city varchar(60) not null,
state_province varchar(30) not null,
country varchar(50) not null,
website varchar(200) not null);
每个数据库表对应一个类, 这个规则的例外情况是多对对关系,在我们的例子中, 有一个多对多字段, authors 表明一本书的有一个或多个作者, ManyToManyField, foreign key 类似这种类型, 个人都不推荐.
最后需要注意的是, 我们并没有显示的为每个模型定义任何主键, 除非你单独指明, 否则Django会自动为每个模型生成一个自增长的整数主键字段, 每个Django模型都要求有单独的主键 id.
模型安装
python manage.py validate 验证模型的有效性 (没执行成功)
python manage.py sqlall first 没有执行成功, 其中 first 是 app 的名字, 并没有真正的创建表, 只是把创建表的语句打印出来
python manage.py syncdb 同步, 这才真正的创建表 (并不能讲模型的修改或删除同步到数据库)
基本数据访问
一旦你创建好了模型, 接下来就可以访问, 运行 python manage.py shell, 输入以下内容
save 方法是将对象保存到数据库中
而每创建一个对象, 相当于往 DB 中插入一条记录.
p1 = Publish.objects.create(name='Apress',...) 类似这种的定义方法, 可以直接写入DB, 不用再调用save()方法
当我们想要打印整个列表时, 我们没有得到我们想要的信息, 原因是程序无法把对象区分开来.
我们可以简单的解决这个问题, 只需要为 Publisher 类添加一个方法 __unicode__(), 告诉Python如何将对象以unicode的方式显示出来
比如在一个类中, 增加
def __unicode__(self):
return self.title # 告诉Python, 输出什么.
当你修改这个对象的属性的值时, 实际上就相当于在做 update操作
p.name = 'Apress Publishing' # update table set name = 'Apress Publishing' where id = '1'; 这个id是主键
数据过滤
filter(name ='Apress', state_province='CA') 类似 where 语句, 多条件也支持
name__contains ='press' 表示sql 语句时 name like ('%press%')
name__icontains 大小写无关
startwith, endwith, range 等很多
返回单个对象, 而不是列表, 用 get, 也就是一条记录
Publisher.objects.get(country='U.S.A'), 假如返回了多条, 会报错, 没查到也会返回异常, 所以, 你最好还是用 DoesNotExist
try:
p = Publisher.objects.get(name='Apress')
except Publisher.DoesNotExist:
print 'Apress isn't in the database yet.'
else:
print 'Apress in the database'
排序, Publisher.objects.order_by('name', '-country') # 前面带-号是倒排序的意思
内部类, class Meta, 你可以在任意一个模型类中使用 Meta类, 来设置一些与特定模型相关的选项.
例如, 我们现在关注 ordering 这个选项. 如果你设置了这个选项, 那么除非你检索时特意额外的使用了 order_by(), 否则, Publisher对象的相关返回值都会默认的按照 name 字段排序.
class Meta:
ordering = ['name']
链式查询 Publisher.objects.filter(country='U.S.A').order_by('-name')
这个有点类似 linux 的 shell, 前边一个命令的结果集(QuerySet)是后一个命令的参数.
限制返回的条数, Publisher.objects.order_by('name')[0], 因为返回的是一个 对象, 数据行的List, list[0], 就表示第一个对象, 也就是第一行
注意, 这个是可以切片的, 比如
Publisher.objects.order_by('name')[0:2] # 这种切片不支持负索引. 其实虽然不支持, 但是我们自己可以变通, order_by('-name') 就是一个变通的方法
有条件(除了id之外的)的 update语句, Publisher.objects.filter(id=52).update(name='Apress Publishing')
update()方法会返回一个整型数值, 表示受影响的记录条数.
删除, 一个道理, Publisher.objects.filter(country='USA').delete()
允许字段为空, 在设计字段时, email = models.EmailField(blank=True),
Django 字段, 没有特殊说明的情况下, 全部默认为 Not NULL
如果你想让 日期和数字也可以有空值, 那么你必须同时制定 blank=True 和 Null=True
自定义字段标签. 类似comment, email = models.EmaiField(blank=True, verbose_name='e-mail')
MySql 数据类型 与 models 对应关系
varchar 对应 charField (EmailField 是在 CharField 的基础上, 增加了判断 email 合法性的判断) URLFild 也是
blob BooleanField
int/integer IntegerField 整数
double/float FloatField
datetime DateTimeField
Text TextField 超大文本
ImageField/ FileField
Field选项(参数)
null 如果为真, 表示可以存储空值. 通常不用在字符的字段你上, 字符型字段如果没有值, 会返回空字符而不是Null.
blank 字段是否可以为空
choices 类似 check, 例如 SEX_CHOICES=('M', 'F') 也就是说, 字段的值, 只能在这里边选
default 缺省值
db_index 如果为 True, 会为字段创建索引
db_column 指定该列在数据库中的名字, 如果不指定自动采用model的字段名
editable, True/False 如果为假, admin 不能修改
unique, 是否有唯一性检查
verbose_name, 别名 类似 comment
高级
添加一个字段的方法 (因为添加不能再同步了)
1. django类中添加一个属性, 然后运行 python mange.py sqlall books 来查看 create table语句
2. 根据 create table 语句, 直接到 DB 中, 添加该列
这样, 就添加字段成功了
删除字段也是一样, 需要分别在 Django 和 DB 进行两次删除. (一定要先修改 Django, 再修改DB)
执行原始SQL
有时候你会发现 Django数据库API带给你的只有这么多, 那你可以写一些自定义的SQL查询.可以通过 django.db.connection对象轻松实现. 它代表当前数据库连接. connection.cursor,得到游标对象, 然后使用 cursor.execute(sql, [params])来执行SQL语句, 使用cursor.fetchone()或者cursor.fetchall()返回记录集.
%s 会自动添加引号, 所以, 你不要为参数加引号, 参数什么就是什么.
而且貌似不需要关闭连接, 因为还有别的在用. 另外, 这个SQL语句肯定是在 models 中使用.
多表查询
使用 prefetch_related