Django的数据库与创建模型
一、数据库的配置
settings.py文件下的配置
前面讲到settings.py文件包括了整个Django项目中的大多配置的,其中,数据库的配置就在这里面,具体配置如下:
settins.py
...
DATABASES - {
'default':{
'ENGINE': 'django.db.backends.mysql', # 配置使用的数据库,这儿使用mysql数据库
'NAME': '', # 数据库名字
'HOST': '', # IP(本地地址可以为localhost)
'PORT': '', # 端口号,一般默认为3306
'USER': '', # 数据库连接用户名
'PASSWORD': '', # 连接数据库密码
'TIME_ZONE': '', # 数据库时区
}
}
...
注意:所有的属性都是大写的,不要写成小写了。
配置数据库链接
安装pymysql
1、在pycharm的终端中执行:
pip install pymysql
2、在django1(默认生成的文件夹)下的__init__.py文件下配置:
import pymysql
pymysql.install_as_MySQLdb()
之前说过Python3没有MySQLDB连接数据库的驱动,所以必须在这个文件下进行连接配置。
3、创建数据库:
进入mysql
mysql -u root -p
创建数据库
create datebase XXX charset=utf-8;
迁移
1、第一次迁移(在没有创建任何模型的时候)
pyhton manage.py migrate
会自动生成Django需要的表
2、 之后的迁移(自己创建模型之后)
# 生成迁移文件
python manage.py makemigrations
# 执行迁移文件
python manage.py migrate
3、插入管理员账号
python manage.py createsuperuser
二、创建应用
1、创建第一个应用
在Django中,基本上每一个应用就是一个模块,在这儿,我们用学生管理系统来举例。
创建应用语句:
python manage.py startapp 应用名称
在这里,我们按照惯例,创建一个叫app的应用。执行完创建语句后,我们会发现Django在项目(根目录)文件夹下为我们创建了一个叫app的文件夹,这就说明我们的应用已经创建成功了。
2、应用的默认文件
我们看一下Django为我们创建的文件:
app
migrations
__init__.py
__init__.py
接下来,我们看一下这些文件会给我们提供什么功能或着各个文件(文件夹)是干嘛的。
3、migrations文件夹
依次从上往下来看,首先是名为migrations的文件夹,在这个文件夹下有一个默认生成的__init__.py文件,说明这是一个python的文件夹,这个文件夹的作用是啥呢?估计看这个名字能大概猜到一点吧,migrations——和我们迁移时候的指令是不是很像。对,这个文件夹存储的就是我们在迁移的时候生成的迁移文件,在我们执行生成迁移文件的命令时,Django会根据模型在哪个应用下,在相应应用的migrations文件夹中生成对应的迁移文件。(注意:migrations文件夹下的迁移文件最好不要删!)
4、models.py文件
在剩下的文件中,我们需要关注的是两个文件:models.py和views.py。
models.py文件,前面我们讲过Django框架的模式为MVT模式,这个models.py文件对应着的正是模式中的M,models.py文件为我们提供的是创建模型的地方,在这个地方,我们编写创建模型的代码,具体写法在后面进行介绍。
5、views.py文件
views.py文件对应Django的MVT模式中的V,即视图界面,这个文件主要就是渲染我们的视图,已经逻辑代码等在这个文件下完成,在这儿,就先了解一下主要功能,具体的作用后面一一介绍。
三、创建模型(建表)
Django的创建模型
Django有一套专门的建表语句,所以,在使用Django进行数据库相关的操作时,就免去了使用SQL语句(这就照顾了SQL语句不熟练朋友,但是也会导致SQL语句更加不熟练了,不过Django里面的配套语句还是很好用的),直接进入正题:
前面介绍了Django的迁移,这就是将Django中的模型与数据库中的字段进行相互映射,因为Django已经为我们封装好了,我们直接使用即可。
首先,我们在应用app下的models.py文件中创建学生管理系统中所需要的四个表:
from django.db import models
# 创建班级表
class Grade(models.Model):
g_name = models.CharField(max_length=10, unique=True)
class Meta:
db_table = 'grade'
# 创建学生表
class Student(models.Model):
# 定义姓名字段,类型为字符串,最大长度为20个字符,不能重复,不能为空
s_name = models.CharField(max_length=20, unique=True, null=False)
# 定义性别字段,类型为布尔,并给默认值为1(男)
s_gender = models.BooleanField(default=1)
# 定义年龄字段,类型为整型,并给默认值为20
s_age = models.IntegerField(default=20)
# 定义创建时间字段,默认赋值为当前时间,并只在创建时赋值
create_time = models.DateTimeField(auto_now_add=True)
# 定义更新时间字段,默认赋值为当前时间,在每次更新后赋值
update_time = models.DateTimeField(auto_now=True)
# 定义数学成绩字段,设置为固定精度的十进制数,并设置精度为,总共三个数字,并且其中一位是小数,可以为空
math = models.DecimalField(max_digits=3, decimal_places=1, null=True)
# 定义物理成绩字段,设置为固定精度的十进制数,并设置精度为,总共三个数字,并且其中一位是小数,可以为空
physics = models.DecimalField(max_digits=3, decimal_places=1, null=True)
# 一对多的外键定义,定义在多的一方
g = models.ForeignKey(Grade, on_delete=models.CASCADE, null=True, related_name='stu')
# 给表命名为student
class Meta:
db_table = 'student'
# 创建学生信息表
class StudentInfo(models.Model):
s_no = models.CharField(max_length=10, null=False)
phone = models.CharField(max_length=11, null=True)
name = models.CharField(max_length=10, null=True)
# 定义一对一关联关系
stu = models.OneToOneField(Student, on_delete=models.CASCADE, related_name='info')
# 创建学科表
class Course(models.Model):
c_name = models.CharField(max_length=10, unique=True)
# ManyToManyField 定义多对多关联关系,字段定义在任何一方都可以
stu = models.ManyToManyField(Student, null=True, related_name='cou')
class Meta:
db_table = 'course'
在这儿,我们创建了四个表,不知道大家有没有发现,这里面我们都没有定义主键。
Django在创建模型的时候,会自动为我们创建一个自动增长的主键id字段,所以,我们在创建表的时候无须创建主键字段,在这四个表中,我们创建了基本的一些字段,以及互相有关联关系的字段,接下来,我们将四个表分开来看一下。
student - 学生表
在这个表中,我们创建了8个字段,分别用了不同的类型去创建:
s_name:学生姓名字段,是一个字符串类型的字段,在这个字段里面,我们定义了字段的最大长度,当然,相对也可以有罪小的字段长度,使用属性:min_length=长度,即可;然后定义该字段为不可重复并且不能为空。
s_gender:学生性别字段,性别非男即女(其他情况不计),所以布尔类型将是一个不二之选,在这个字段中,我们赋给它默认值为1;
s_age:学生年龄字段,因为年龄一般为整数,且不会超过3位数,所以整型是比较合适的,最后再给它赋默认值为20;
create_time和update_time:这两个字段用的都是用一个类型,用于存储时间和日期,在赋值时,我们讲创建时间赋的是在创建时赋当前时间,更新时间字段,赋的是只要更新就赋上当前时间;
math和physics:数学成绩和物理成绩字段,成绩一般范围是0到100之间,且不会大于一位小数,所以在这儿定义为固定精度的小数,并有max_digits(有效位数)和decimal_places(小数点后面)两个必要的参数;
g:这是一个关联关系的字段,关联的是班级表,一个班级有多个学生,正常情况下,一个学生只属于一个班,即学生对班级为多对一的关系,我们将这个字段定义为班级表的外键;
在这个表中,我们设计了一对多的关联关系,并且,在Django中,当有关联关系时,迁移后,Django会自动将我们的关联字段名后面添加_id,比如此处的g字段,在数据库中对应的字段为g_id。
一对多关联关系的设计:
存储:
在给学生的外键g(班级)赋值时,有两种方式:
- 1、学生对象.关联字段 = 关联模型对象
- 例如:stu.g = Grade.object.get(id=1)
- 2、学生对象.关联字段_id = 关联表的主键id值
- 例如:stu.g_id = 1
查询:
在定义模型时,我们可以给关联关系字段赋related_name参数,这个参数相当于在使用时给这个字段一个别名;
没有定义related_name参数时:
- 学生查询班级(多查一):多的一方(学生)对象.关联字段
- 例如:grade = stu.g # stu为获取的学生对象
- 班级查询学生(一查多):一的一方(班级).关联模型名的小写_set(后面可以增加过滤的条件,如filter()、all()等)
- 例如:stus = grade.student_set.all() # grade为班级对象
当定义了related_name参数时(参数为‘stu'):
- 学生查询班级(多查一):多的一方(学生)对象.关联字段
- 例如:grade = stu.g # stu为获取的学生对象
- 班级查询学生(一查多):一的一方(班级).ralated_name参数(后面可以增加过滤的条件,如filter()、all()等)
定义:
在定义一对多模型时,ForeignKey定义在多的一方,其中的参数:
- 关系表定义时的类名,如这个地方是Grade;
- on_delete参数:删除时的约束条件,部分常用的属性:
- models.CASCADE:删除主表,从表也会被删
- models.PROTECT:删除时,不删除主表
- models.SET_NULL:删除主表,从表的关联自也会被删
- related_name参数:反向查询名称,要是不理解可以把它当做是一个别名,在查询的时候,我们可以让关联字段没有的那张表的对象通过点这个属性的操作去获取关联字段所在表的对象。
Grade - 班级表
这个表中我们只定义了一个字段,加上Django为我们自动生成的主键字段,所以,该表中总共两个字段:
id:Django自动生成的自增字段,为该表的主键,学生表在对班级表记性一对多关联时,学生表中的g字段就是关联了班级表的主键id字段,所以在学生表中g字段各行的值必须是班级表中id字段有的值;
g_name:班级名称字段,是一个字符串类型,长度小于10个字符并且不能重复。
StudentInfo - 学生信息表
一个数据库中的字段不宜太多,不然查询起来会有一些其他的潜在问题。因此,我们定义学生信息表,为学生表添加字段。该表中除了Django默认生成的主键外,定义了4个字段,并且在这四个字段中,有一个字段为具有关联关系的字段:
s_no:学生学号字段,一般来说学生的学号都不会太短,大多都在8位以上,所以在这儿用字符串是比较合适的,我们定义一个长度小于10并且不能为空的字符串,用来存储学生学号;
phone:学生电话号码字段,电话号码与上面的学号一样,基本上我们的移动电话号码都是11位,出去前面的区号什么的,所以,在这儿我们定义一个长度不大于11的可以为空的字符串,用来存储学生电话;
name:学生紧急联系人姓名,同student表中的姓名参数;
stu:该字段可以说是这个表中最重要的一个字段了,学生的信息对应的是一个学生,而正常来说,一个学生的信息也值对应一套吧,就比如每个学生在学信网上有自己的信息,而学信网上每一个人的信息都对应着一个学生。所以,在这儿我们将StudentInfo表与Student表进行一对一关联关系连接。
在这个表中,我们设计了关系模型中的一对一关联关系,对应的一对一关联关系的相关操作如下:
存储:
在给关联的字段StudentInfo表中的stu字段进行赋值时
- 注:在这儿的stu_info = StudentInfo.object.get(pk=1),是一个StudentInfo表的一个对象
- 方法一:stu_info.关联字段 = 关联对象
- 例如:stu_info.stu = Student.object.get(id=1)
- 方法二:stu_info.关联字段_id = 主键id
- 例如:stu)info.stu_id = 1
查询:
当两个表之间相互查询:
没有设置related_name参数时:
- 学生对象查询信息表对象:学生对象.学生信息模型名称的小写
- 例如:stu_info = stu.studentinfo
- 学生信息表查询学生对象:学生信息表对象.OneToOneField对象
- 例如:stu = stu_info.Student
当设置了related_name参数时:
- 学生对象查询信息表对象:学生对象.related_name参数属性
- 例如:stu_info = stu.info
- 学生信息表查询学生对象:学生信息表对象.OneToOneField对象
- 例如:stu = stu_info.Student
定义:
在进行一对一关系模型的定义时,OneToOneField定义的关联字段可以在两个表中的任何一个表中进行定义,存值和取值的时候都是一样的。
Course - 学科表
在学科表中,我们定义了两个字段,其中有一个字段为关系字段,与student之间建立多对多关系,为什么这么定义呢?众所周知,学生在学校选课的时候,一个学生可以同时选择修多门课程,同样的,一门课程也可以同时被多个学生选择,在这两者之间就建立了一种多对多的关系,那我们来看看,在Django中,这种多对多的关系是怎么实现的:
c_name:学科名称,此处定义的是一个长度小于10且不可重复的字符串字段;
stu:这个字段便是我们的学科表与学生表之间建立多对多关联关系的字段。
在数据库中,当我们想创建多对多关联关系的表时,都会选择创建一张中间表,由这张中间表对两张表分别进行一对多的关联关系进行连接,从而间接实现多对多的关联关系;而在Django中,当我们定义多对多的关联关系时,Django会自动为我们创建一张中间表,并将两张表各自的主键作为中间表的字段,在此,我们进行多对多关联关系定义后,数据库中为我们创建了一张名为:course_stu的中间表(因为我们是在course表中定义的字段,所以命名时,是以course表名_字段名定义的),接下来,我们看一下这张中间表中的字段:
course_stu - 中间表:
id | course_id | student_id |
---|---|---|
在这张表中,三个字段:id字段为该表的主键,course_id字段映射course表的主键,student_id字段映射student表的主键;可以看出中间表与两张表分别组成一对多关联关系。两个表之间相互操作如下:
存储:
- stu.关联字段.add(关联模型对象)
- 注:stu = Student.object.get(pk=1)
- cou = Course.object.get(pk=1)
- 例如:stu.cou.add(cou)
查询:
查询的时候和一对一、一对多一样,区分是否有related_name:
没有设置related_name参数:
- 课程查询学生:课程对象.关联字段.filter()
- 例如:cou.stu.filter(s_name='').first()
- 学生查询课程:学生对象.关联模型名的小写_set(后面可以接filter()之类的语句)
- 例如:stu.course_set
当设置了related_name参数:
- 课程查询学生:课程对象.关联字段.filter()
- 例如:cou.stu.filter(s_name='').first()
- 学生查询课程:学生对象.related_name参数.filter()(后面可以接filter()之类的语句)
- 例如:stu.cou_set
定义:
因为多对多关联关系,在模型定义中,和一对一一样,没有限制,可以在两张表中的任意一张定义。
部分模型定义参考
Django模型的字段类型:
字段类 | 说明 |
---|---|
AutoField | 自增ID字段 |
BigIntegerField | 64位有符号整数 |
BinaryField | 存储二进制数据的字段,对应Python的bytes类型 |
BooleanField | 存储True或False |
CharField | 长度较小的字符串 |
DateField | 存储日期,有auto_now和auto_now_add属性 |
DateTimeField | 存储日期和日期,两个附加属性同上 |
DecimalField | 存储固定精度小数,有max_digits(有效位数)和decimal_places(小数点后面)两个必要的参数 |
DurationField | 存储时间跨度 |
EmailField | 与CharField相同,可以用EmailValidator验证 |
FileField | 文件上传字段 |
FloatField | 存储浮点数 |
ImageField | 其他同FileFiled,要验证上传的是不是有效图像 |
IntegerField | 存储32位有符号整数 |
GenericIPAddressField | 存储IPv4或IPv6地址 |
NullBooleanField | 存储True、False或null值 |
PositiveIntegerField | 存储无符号整数(只能存储正数) |
SlugField | 存储slug(简短标注) |
SmallIntegerField | 存储16位有符号整数 |
TextField | 存储数据量较大的文本 |
TimeField | 存储时间 |
URLField | 存储URL的CharField |
UUIDField | 存储全局唯一标识符 |
字段属性:
属性 | 说明 |
---|---|
null | 数据库中对应的字段是否允许为NULL,默认为False |
blank | 后台模型管理验证数据时,是否允许为NULL,默认为False |
choices | 设定字段的选项,各元组中的第一个值是设置在模型上的值,第二值是人类可读的值 |
db_column | 字段对应到数据库表中的列名,未指定时直接使用字段的名称 |
db_index | 设置为True时将在该字段创建索引 |
db_tablespace | 为有索引的字段设置使用的表空间,默认为DEFAULT_INDEX_TABLESPACE |
default | 字段的默认值 |
editable | 字段在后台模型管理或ModelForm中是否显示,默认为True |
error_messages | 设定字段抛出异常时的默认消息的字典,其中的键包括null、blank、invalid、invalid_choice、unique和unique_for_date |
help_text | 表单小组件旁边显示的额外的帮助文本 |
primary_key | 将字段指定为模型的主键,未指定时会自动添加AutoField用于主键,只读 |
unique | 设置为True时,表中字段的值必须是唯一的 |
verbose_name | 字段在后台模型管理显示的名称,未指定时使用字段的名称 |
ForeignKey属性
- limit_choices_to:值是一个Q对象或返回一个Q对象,用于限制后台显示哪些对象。
- related_name:用于获取关联对象的关联管理器对象(反向查询),如果不允许反向,该属性应该被设置为’+’,或者以’+'结尾。
- to_field:指定关联的字段,默认关联对象的主键字段。
- db_constraint:是否为外键创建约束,默认值为True。
- on_delete:外键关联的对象被删除时对应的动作,可取的值包括django.db.models中定义的:
- CASCADE:级联删除。
- PROTECT:抛出ProtectedError异常,阻止删除引用的对象。
- SET_NULL:把外键设置为null,当null属性被设置为True时才能这么做。
- SET_DEFAULT:把外键设置为默认值,提供了默认值才能这么做。
ManyToManyField属性
- symmetrical:是否建立对称的多对多关系。
- through:指定维持多对多关系的中间表的Django模型。
- throughfields:定义了中间模型时可以指定建立多对多关系的字段。
- db_table:指定维持多对多关系的中间表的表名。
模型元素数据选项
选项 | 说明 |
---|---|
abstract | 设置为True时模型是抽象父类 |
app_label | 如果定义模型的应用不在INSTALLED_APPS中可以用该属性指定 |
db_table | 模型使用的数据表名称 |
db_tablespace | 模型使用的数据表空间 |
default_related_name | 关联对象回指这个模型时默认使用的名称,默认为<model_name>_set |
get_latest_by | 模型中可排序字段的名称。 |
managed | 设置为True时,Django在迁移中创建数据表并在执行flush管理命令时把表移除 |
order_with_respect_to | 标记对象为可排序的 |
ordering | 对象的默认排序 |
permissions | 创建对象时写入权限表的额外权限 |
default_permissions | 默认为(‘add’, ‘change’, ‘delete’) |
unique_together | 设定组合在一起时必须独一无二的字段名 |
index_together | 设定一起建立索引的多个字段名 |
verbose_name | 为对象设定人类可读的名称 |
verbose_name_plural | 设定对象的复数名称 |
查询参考
按字段查找可以用的条件:
- exact / iexact:精确匹配/忽略大小写的精确匹配查询
- contains / icontains / startswith / istartswith / endswith / iendswith:基于like的模糊查询
- in:集合运算
- gt / gte / lt / lte:大于/大于等于/小于/小于等于关系运算
- range:指定范围查询(SQL中的between…and…)
- year / month / day / week_day / hour / minute / second:查询时间日期
- isnull:查询空值(True)或非空值(False)
- search:基于全文索引的全文检索
- regex / iregex:基于正则表达式的模糊匹配查询
Q对象
当需要使用与或非时,就需要使用到Q对象,例如:
from django.db.models import Q
stu.object(Q(s_name='') & Q(s_age lg 10)) # 与
stu.object(Q(s_name='') & Q(s_age lg 10)) # 或
stu.object(~Q(s_age lg 10)) # 非