在之前的文章中,我们对Django的MTV模式进行了介绍,相信大家对于Models已经有了初步了解。在这篇文章中,我们将深入探索Models,以求深刻理解Django中的数据操作。
动态网站最重要的部分,毫无疑问非数据库莫属了。把所有数据通过数据库系统维护在一些数据表中,在需要的时候再以条件式查询的方式取出,然后送到网页显示出来,或者通过新增指令存储新的数据记录,或针对特定的数据项进行修改等。由数据库维护所有内容可以提高数据存取的效率,也可以增加网站提供信息的能力。
数据库(Database,简称DB),简单地说就是一个系统组织过的数据格式,通过特定的接口存取的数据集合。存取这些数据内容的系统叫做数据库管理系统(Database Management System,DBMS)。不同的数据库系统有不同的执行程序和操作方法,定义数据的方式也有可能非常不一样。所幸的是,网页服务器所使用的数据库系统大多为关系数据库系统,而它的主流系统为MySQL,因此大部分数据库系统都会和MySQL兼容,包括Django默认的SQLite文件类型数据库也是如此。
典型情况下,网站系统要存取数据库需要先有一个正确的数据库驱动接口,通过此接口才可以使用该数据库(以MySQL为例)接受的查询命令(SQL查询语言),并在使用之前进行数据库的连接工作。数据库系统通常都是以独立的服务程序运行在某一台主机上,所以连接时,除了要指定连接操作用的账号和密码外,也必须指定主机位置的相关信息,大部分情况下网页服务器和数据库系统都位于同一台主机上,所以在指定主机位置时,只要使用localhost就可以了。事实上,数据库系统是可以独立于网络上任何一台主机的,而许多主机供应商也提供了数据库主机的服务。
由于网站中最重要的内容是数据库,因此开始设计网站的第一步不是,马上开始编写程序,而是根据网站的需求先设计数据库,确定数据库的所有细节,并做过正规化步骤之后,才能够开始网站的程序设计。因为数据库的内容如果在开始设计程序时发现不符合实际需求或数据表之间的关系有误,要再回去修改数据里的格式以及结构,往往会造成程序内容极大的变动,会花费更多精力,而且可能会冒出许多不该发生的程序错误,因而不得不慎之又慎。
ORM(Object Relational Mapper)是一种面向对象的程序设计技术,以对象的方式来看待每一笔数据,可以解决底层数据库兼容性的问题。也就是把数据库的操作方式抽象化为统一在Python中习惯的数据操作方式,如果把这些指令对应到实际每一种数据库的内部操作,就由元数据以及Django内部去处理,开发网站的人员不用去担心这个部分。
Model是Django表示数据的模式,以Python的类为基础在models.py中设置数据项与数据格式,基本上每个类对应一个数据库中的数据表。因此,定义每个数据项时,除了数据项名称外,也要定义此项目的格式以及这张表和其他表格相互之间的关系(即数据关联)。定义完毕后,网站的其他程序就可以使用Python语句来操作这些数据内容,不用关心实际使用的SQL指令以及使用的是哪一种数据库。
简单的定义语言如下:
class Post(models.Model):
# 字段定义
title = models.CharField(max_length=200)
slug = models.CharField(max_length=200)
body = models.TextField()
pub_date = models.DateTimeField(default=timezone.now)
# 其他设置
class Meta:
ordering = ('-pub_date',)
def __str__(self):
return self.title
通过这样一个类,我们就可以定义一张数据库的表,通过对表和字段的一些设置,实现在抽象层面上对数据库结果的操作。
在django\db\models\fields\__init__.py
中我们能看到这样一段代码
__all__ = [
'AutoField', 'BLANK_CHOICE_DASH', 'BigAutoField', 'BigIntegerField',
'BinaryField', 'BooleanField', 'CharField', 'CommaSeparatedIntegerField',
'DateField', 'DateTimeField', 'DecimalField', 'DurationField',
'EmailField', 'Empty', 'Field', 'FieldDoesNotExist', 'FilePathField',
'FloatField', 'GenericIPAddressField', 'IPAddressField', 'IntegerField',
'NOT_PROVIDED', 'NullBooleanField', 'PositiveIntegerField',
'PositiveSmallIntegerField', 'SlugField', 'SmallAutoField',
'SmallIntegerField', 'TextField', 'TimeField', 'URLField', 'UUIDField',
]
列出了Django中自带的一些数据类型,我们介绍一些常用的:
数据字段 | 参数说明 | 数据内容 |
---|---|---|
BigIntegerField | 64位的大整数 | |
BooleanField | 布尔值,只有True/False两种 | |
CharField | max_length:指定可接受的字符串长度 | 用来存储较短数据的字符串,通常使用于单行的文字数据 |
DateField | auto_now:每次对象被存储时就自动加入当前日期 auto_now_add:只有在对象被创建时才加入当前日期 | 日期格式,可用于datetime.date |
DataTimeField | 同上 | 日期时间格式,对应到datetime.datetime |
DecimalField | max_digits:可接受的最大位数 decimal_places:在所有位数中,小数占几个位 | 定点小数数值数据,适用于Python的Decimal模块的实例 |
EmailField | max_length:最大字符长度 | 可接受电子邮件地址格式的字段 |
FloatField | 浮点数字段 | |
IntegerField | 整数字段,是通用性最高的整数格式 | |
PostiveIntegerField | 正整数字段 | |
SlugField | max_length:最大字符长度 | 和CharField一样,通常用来作为网址的一部分 |
TextField | 长文字格式,一般用在HTML窗体的Textarea输入项目中 | |
URLField | max_length:最大字符长度 | 和CharField一样,特别用来记录完整的URL网址 |
在django\db\models\fields\__init__.py
第132行,所有字段类的基类Field的构造函数的形参中我们还能看到这样一段代码:
def __init__(self, verbose_name=None, name=None, primary_key=False,
max_length=None, unique=False, blank=False, null=False,
db_index=False, rel=None, default=NOT_PROVIDED, editable=True,
serialize=True, unique_for_date=None, unique_for_month=None,
unique_for_year=None, choices=None, help_text='', db_column=None,
db_tablespace=None, auto_created=False, validators=(),
error_messages=None):
这段代码描述了字段定义时的一些选项,我们对常用的进行介绍:
选项 | 含义 |
---|---|
null | 此字段是否接受存储空值NULL,默认值是False |
blank | 此字段是否接受存储空白内容,默认值是False |
choices | 以选项的方式(只有固定内容的数据可以选用)作为此字段的候选值 |
default | 输入此字段的默认值 |
help_text | 字段的求助信息 |
primary_key | 把此字段设置为数据表中的主键KEY,默认值为False |
unique | 设置此字段是否为唯一值,默认值为False |
db_column | 在数据库中的字段名 |
verbose_name | 在键入此信息时展示的字段名 |
max_length | 最大字符长度 |
在views.py中对数据进行操作时常用以下函数或修饰词:
函数及修饰词 | 含义 |
---|---|
filter() | 返回符合指定条件的QuerySet |
exclude() | 返回不符合指定条件的QuerySet |
order_by() | 串接到QuerySet之后,针对某一指定的字段进行排序 |
all() | 返回所有的QuerySet |
get() | 获取指定符合条件的唯一元素,如果找不到或有一个以上符合条件,都会产生exception |
first()/last() | 获取第1个和最后1个元素 |
aggregate() | 可以用来计算数据项的聚合函数 |
exists() | 用来检查是否存在某指令条件的记录,通常附加在filter()后面 |
update() | 用来快速更新某些数据记录中的字段内容 |
delete() | 删除指定的记录 |
iexact | 不区分大小写的条件设置 |
contains/icontains | 设置条件为含有某一字符串就符合,如SQL语句中的LIKE和ILIKE |
in | 提供一个列表,只要符合列表中的任何一个值均可 |
gt/gte/lt/lte | 大于/大于等于/小于/小于等于 |
有一些函数(如reverse()、exists())可以串接在另一些函数后面,用于进一步过滤信息,修饰词则是放在参数中,在字段名后面加上两个下划线之后再串接,可以为条件设置增加更多的弹性。
同样是查询数据,使用filter会返回一个列表,而使用get会返回一个唯一的值。如果在设置的条件下找不到任何数据,使用filter就会返回一个空列表,而使用get会产生一个DoesNotExit例外,如果设置的条件有一个以上的元素符合条件,那么get也会产生例外。因此,get通常在明确知道该数据只有一笔的情况下才会使用,而且使用的时候也要以try/exception做好例外处理工作。
在Django中使用数据库的步骤如下:
在本地使用MySQL的好处是在开发的时候就可以享受高效率的数据库系统,而且可以充分使用phpMyAdmin观察数据库的现状,需要调整数据库内容的时候也非常方便,坏处是同一台计算机中当前使用中的数据如果想要放到另一台计算机使用时,就要使用导入导出SQL的方式迁移数据,在操作上并不方便。而且,要部署网站的时候,之前输入的数据库也都要重新迁移一次才行。如果直接使用的是网络主机上的MySQL服务器,那么不管在哪一台计算机使用的都是同一个数据库,这样就不会有数据不同步的情况了。