商城的数据模型搭建与使用
Django对各种数据库提供了很好的支持,包括PostgreSQL、MySQL、SQLite和Oracle,而且为这些数据库提供了统一的API方法,这些API统称为ORM框架。
通过使用Django内置的ORM框架可以实现数据库连接和读写操作。
ORM框架是一种程序技术,用于实现面向对象编程语言中不同类型系统的数据之间的转换。
从效果上说,它创建了一个可在编程语言中使用的“虚拟对象数据库”,通过对虚拟对象数据库的操作从而实现对目标数据库的操作,虚拟对象数据库与目标数据库是相互对应的。
定义商城的数据模型
我们已设计了项目babys的数据结构,用户信息表是由Django内置用户管理功能定义,除此之外,项目还需要定义商品信息表、商品类别表、购物车信息表和订单信息表。我们将商品信息表和商品类别表定义在项目应用commodity的models.py;
购物车信息表和订单信息表定义在项目应用shopper的models.py。首先打开项目应用commodity的models.py文件,在文件中定义模型Types和CommodityInfos,它们以类的形式表示,并且继承父类Model,详细的定义过程如下:
最后再打开项目应用shopper的models.py文件,在文件中定义模型CartInfos和OrderInfos,模型的定义过程与模型Types和CommodityInfos相似,如下所示:
从模型的定义过程分析归纳得知,模型定义可以分为三部分,每个部分的功能说明如下:
(1)定义模型字段,每个模型字段对应数据表的某个表字段,字段以aa= models.bb(cc)格式表示,比如id =models.AutoField(primary_key=True),其中id为模型字段名称,它与数据表的表字段相互对应;models.AutoField是设置字段的数据类型,常用类型有整型、字符型或浮点型等;primary_key=True是设置字段属性,例如字段是否为表主键、限制内容长度、设置默认值等。
在实际开发中,我们需要定义不同的字段类型来满足各种开发需求,因此Django划分了多种字段类型,在源码目录django\db\models\fields的__init__.py
和files.py文件里找到各种模型字段,说明如下:
AutoField:自增长类型,数据表的字段类型为整数,长度为11位。
BigAutoField:自增长类型,数据表的字段类型为bigint,长度为20位。
CharField:字符类型。
BooleanField:布尔类型。
CommaSeparatedIntegerField:用逗号分隔的整数类型。
DateField:日期(Date)类型。
DateTimeField:日期时间(Datetime)类型。
Decimal:十进制小数类型。
EmailField:字符类型,存储邮箱格式的字符串。
FloatField:浮点数类型,数据表的字段类型变成Double类型。
IntegerField:整数类型,数据表的字段类型为11位的整数。
BigIntegerField:长整数类型。
IPAddressField:字符类型,存储Ipv4地址的字符串。
GenericIPAddressField:字符类型,存储Ipv4和Ipv6地址的字符串。
NullBooleanField:允许为空的布尔类型。
PositiveIntegerFiel:正整数的整数类型。
PositiveSmallIntegerField:小正整数类型,取值范围为0~32767
。
SlugField:字符类型,包含字母、数字、下画线和连字符的字符串。
SmallIntegerField:小整数类型,取值范围为-32,768~+32,767。
TextField:长文本类型。
TimeField:时间类型,显示时分秒HH:MM[:ss[.uuuuuu]]。
URLField:字符类型,存储路由格式的字符串。
BinaryField:二进制数据类型。
FileField:字符类型,存储文件路径的字符串。
ImageField:字符类型,存储图片路径的字符串。
FilePathField:字符类型,从特定的文件目录选择某个文件。
在不同的字段类型中,我们还可以设置字段的基本属性,比如primary_key=True是将字段设置为主键,每个字段都具有共同的基本属性,如下所示:
verbose_name:默认为None,在Admin站点管理设置字段的显示名称。
primary_key:默认为False,若为True,则将字段设置成主键。
max_length:默认为None,设置字段的最大长度。
unique:默认为False,若为True,则设置字段的唯一属性。
blank:默认为False,若为True,则字段允许为空值,数据库将存储空字符串。
null:默认为False,若为True,则字段允许为空值,数据库表现为NULL。
db_index:默认为False,若为True,则以此字段来创建数据库索引。
default:默认为NOT_PROVIDED对象,设置字段的默认值。
editable:默认为True,允许字段可编辑,用于设置Admin的新增数据的字段。
serialize:默认为True,允许字段序列化,可将数据转化为JSON格式。
unique_for_date:默认为None,设置日期字段的唯一性。
unique_for_month:默认为None,设置日期字段月份的唯一性。
unique_for_year:默认为None,设置日期字段年份的唯一性。
choices:默认为空列表,设置字段的可选值。
help_text:默认为空字符串,用于设置表单的提示信息。
db_column:默认为None,设置数据表的列名称,若不设置,则将字段名作为数据表的列名。
db_tablespace:默认为None,如果字段已创建索引,那么数据库的表空间名称将作为该字段的索引名。注意:部分数据库不支持表空间。
auto_created:默认为False,若为True,则自动创建字段,用于一对一的关系模型。
validators:默认为空列表,设置字段内容的验证函数。error_messages:默认为None,设置错误提示。
(2)重写函数__str__()
,这是设置模型的返回值,默认情况下,返回值为模型名+主键。函数__str__可用于外键查询,比如模型A设有外键字段F,外键字段F关联模型B,当查询模型A时,外键字段F会将模型B的函数__str__返回值作为字段内容。需要注意的是,函数__str__只允许返回字符类型的字段,如果字段是整型或日期类型的,就必须使用Python的str()函数将其转化成字符类型。
(3)重写Meta选项,这是设置模型的常用属性,一共设有19个属性,每个属性的说明如下:
abstract:若设为True,则该模型为抽象模型,不会在数据库里创建数据表。
app_label:属性值为字符串,将模型设置为指定的项目应用,比如将index的models.py定义的模型A指定到其他App里。
db_table:属性值为字符串,设置模型所对应的数据表名称。
db_teblespace:属性值为字符串,设置模型所使用数据库的表空间。
get_latest_by:属性值为字符串或列表,设置模型数据的排序方式。
managed:默认值为True,支持Django命令执行数据迁移;若为False,则不支持数据迁移功能。
order_with_respect_to:属性值为字符串,用于多对多的模型关系,指向某个关联模型的名称,并且模型名称必须为英文小写。比如模型A和模型B,模型A的一条数据对应模型B的多条数据,两个模型关联后,当查询模型A的某条数据时,可使用get_b_order()和set_b_order()来获取模型B的关联数据,这两个方法名称的b为模型名称小写,此外get_next_in_order()和get_previous_in_order()可以获取当前数据的下一条和上一条的数据对象。
ordering:属性值为列表,将模型数据以某个字段进行排序。
permissions:属性值为元组,设置模型的访问权限,默认设置添加、删除和修改的权限。
proxy:若设为True,则为模型创建代理模型,即克隆一个与模型A相同的模型B。
required_db_features:属性值为列表,声明模型依赖的数据库功能。比如['gis_enabled'],表示模型依赖GIS功能。
required_db_vendor:属性值为列表,声明模型支持的数据库,默认支持SQLite、PostgreSQL,MySQL和Oracle。
select_on_save:数据新增修改算法,通常无须设置此属性,默认值为False。
indexes:属性值为列表,定义数据表的索引列表。
unique_together:属性值为元组,多个字段的联合唯一,等于数据库的联合约束。
verbose_name:属性值为字符串,设置模型直观可读的名称并以复数形式表示。
verbose_name_plural:与verbose_name相同,以单数形式表示。
label:只读属性,属性值为app_label.object_name,如index的模型PersonInfo,值为index.PersonInfo。
label_lower:与label相同,但其值为字母小写,如index.personinfo。
综上所述,模型字段、函数__str__和Meta选项是模型定义的基本要素,模型字段的类型、函数__str__和Meta选项的属性设置需由开发需求而定。在定义模型时,还可以在模型里定义相关函数,比如get_absolute_url(),当视图类没有设置属性success_url时,视图类的重定向路由地址将由模型定义的get_absolute_url()提供。
数据迁移创建数据表
数据迁移是将项目里定义的模型生成相应的数据表,首次在项目里定义模型时,项目所配置的数据库里并没有创建任何数据表,想要通过模型创建数据表,可使用Django的操作指令完成创建过程。以项目babys为例,在配置文件settings.py的DATABASES属性配置MySQL数据库连接信息,连接本地的MySQL数据库系统,如下所示:
下一步是注释项目应用index、commodity和shopper的urls.py定义的路由信息,由于网站的路由地址尚未定义相应的视图函数,因此无法使用Django内置指令创建数据表。然后在PyCharm的Terminal输入Django的操作指令,如下所示:
当makemigrations指令执行成功后,在项目应用commodity和shopper的migrations文件夹里分别创建0001_initial.py文件,如果项目里有多个App,并且每个项目应用的models.py文件里定义了模型对象,当首次执行makemigrations指令时,Django就在每个项目应用的migrations文件夹里创建0001_initial.py文件。打开查看项目应用commodity的migrations的0001_initial.py文件,文件内容如图4-1所示。
0001_initial.py文件将models.py定义的模型生成数据表的脚本代码,该文件的脚本代码可被migrate指令执行,migrate指令会根据脚本代码的内容在数据库里创建相应的数据表,只要在PyCharm的Terminal窗口下输入migrate指令即可完成数据表的创建,代码如下:
当指令运行完成后,使用数据库可视化工具打开数据库就能看到新建的数据表,以数据库可视化工具Navicat Premium为例,如图4-2所示。其中数据表commodity_types由项目应用commodity定义的模型Types创建,而其他数据表是Django内置的功能所使用的数据表,分别是会话Session、用户认证管理和Admin后台系统等。
在开发过程中,开发者因为开发需求而经常调整数据表的结构,比如新增功能、优化现有功能等。假如在上述例子里新增模型Payment及其数据表,为了保证不影响现有的数据表,如何通过新增的模型创建相应的数据表?针对上述问题,我们只需在某个项目应用的models.py里定义新的模型Payment,然后再次执行makemigrations和migrate指令即可。第二次执行makemigrations的时候,新定义的模型会在项目应用的migrations文件夹里创建新的py文件,当再次运行migrate指令的时候,Django会执行migrations文件夹中新建的py文件,每次执行migrate的操作记录都会记录在内置数据表django_migrations中,如图4-3所示。
除了新建模型及其数据表之外,makemigrations和migrate指令还支持模型的修改,从而修改相应的数据表结构,比如新增、修改和删除数据表的某个字段。字段的增删改只需在已有的模型中重新定义即可,然后再次执行makemigrations和migrate指令。但在已有模型中新增字段,模型字段必须将属性null和blank设为True或者为模型字段设置默认值(设置属性default),否则执行makemigrations指令会提示字段修复信息,如图4-4所示。
migrate指令还可以单独执行某个.py文件,首次在项目中使用migrate指令时,Django会默认创建内置功能的数据表,如果只想执行项目应用commodity的migrations文件夹的某个.py文件,那么可以在migrate指令里指定文件名,指令如下:
我们知道,migrate指令根据migrations文件夹的.py文件创建数据表,但在数据库里,数据表的创建和修改离不开SQL语句的支持,因此Django提供了sqlmigrate指令,该指令能将.py文件转化成相应的SQL语句。以项目应用commodity的0001_initial.py文件为例,在PyCharm的Terminal窗口输入sqlmigrate指令,指令末端必须设置项目应用名称和migrations文件夹的某个.py文件名,三者之间使用空格隔开即可,指令输出结果如图4-6所示。
除此之外,Django还提供了很多数据迁移指令,如squashmigrations、inspectdb、showmigrations、sqlflush、sqlsequencereset和remove_stale_contenttypes,这些指令在2.5节里已说明过了,此处不再重复讲述。
数据的导入与导出
在实际开发过程中,我们经常对数据库的数据进行导入和导出操作,比如网站重构、数据分析和网站分布式部署等。一般情况下,我们使用数据库可视化工具来实现数据的导入和导出,以Navicat Premium为例,打开某个数据表,单击“导入”或“导出”按钮,按照操作提示即可完成,如图4-7所示。
使用数据库可视化工具导入某个表的数据时,如果当前数据表设有外键字段,就必须将外键字段关联的数据表的数据导入,再执行当前数据表的数据导入操作,否则数据无法导入成功。因为外键字段指向它所关联的数据表,如果关联的数据表没有数据,外键字段就无法与关联的数据表生成数据关系,从而使当前数据表的数据导入失败。
除了使用数据库可视化工具实现数据的导入与导出之外,Django还为我们提供了操作指令(loaddata和dumpdata)来实现数据的导入与导出操作。以项目babys为例,在数据表commodity_types和commodity_commodityinfos中分别添加数据,如图4-8所示。
在PyCharm的Terminal窗口输入dumpdata指令,将整个项目的数据从数据库里导出并保存到data.json文件,其指令如下:
F:\babys>python manage.py dumpdata > data.json
dumpdata指令末端使用了符号“>”和文件名data.json,这是将项目所有的数据都存放在data.json文件中,并且data.json的文件路径在项目的根目录(与项目的manage.py文件在同一个路径),如图4-9所示。
如果只想导出某个项目应用的所有数据或者项目应用里某个模型的数据,那么可在dumpdata指令末端设置项目名称或项目名称的某个模型名称,指令如下:
一般情况下,使用dumpdata指令导出的数据文件都存放在项目的根目录,因为在输入指令时,PyCharm的Terminal窗口的命令行所在路径为项目的根目录,若想更换存放路径,则可改变命令行的当前路径,比如将数据文件存放在D盘,其指令如下:
若想将导出的数据文件重新导入数据库里,则可使用loaddata指令完成,该指令使用方式相对单一,只需在指令末端设置需要导入的文件名即可:
loaddata指令根据数据文件的model属性来确定当前数据所属的数据表,并将数据插入数据表,从而完成数据导入。一般情况下,数据的导出和导入最好以整个项目或整个项目应用的数据为单位,因为数据表之间可能存在外键关联,如果只导入某张数据表的数据,就必须考虑该数据表是否设有外键,并且外键所关联的数据表是否已有数据。