前言
Django视图层的主要用来处理对应url的请求并且返回响应数据,在这个请求-响应的过程中大致涉及到两点,第一就是视图层会和模型层(数据相关)进行交互,对数据进行增删改查,第二是视图层需要将某些数据传递给前端HTML页面。本文主要介绍第一部分视图层如何与数据库进行交互。
模型层功能介绍
django模型层的主要功能就是与数据库进行交互,使用ORM(对象关系映射)方便的实现数据的增删改查操作,django自带了一个数据库sqlite3
,但是该数据库对日期格式好像不是非常敏感,处理的时候容易出错,因此通常在django框架中使用其他数据库,比如MySQL。
django连接MySQL
在实际开发中通常不使用django自带的sqlite3
,而是换成MySQL或者其他关系型数据库,具体配置方式如下:
第一步需要修改django的配置文件settings.py
中的数据库DATABASES
配置:
# 修改前
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}}
# 修改后
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'practice', // MySQL库的名字
'USER':'root', // MySQL用户名
'PASSWORD':'1026', // MySQL密码
'HOST':'127.0.0.1', // 数据的地址
'PORT':3306,
'CHARSET':'utf8',
}
}
第二步需要进行代码声明,在与项目同名的目录下的__init__.py
文件中书写下述代码,意思是告诉django不要使用默认的mysqldb改为使用pymysql。
# first_django/fist_django/__init__.py
import pymysql
pymysql.install_as_MySQLdb()
django ORM
在python如何操作mysql的文章中提到可以通过在pymysql
模块执行SQL语句从而操作数据库,但是写在Python中书写SQL语句总觉得怪怪的...
django ORM(对象关系映射)的作用就是可以通过python面向对象的代码快捷的操作数据库,不过也有缺点就是封装程度太高,有时候SQL语句的效率偏低。既然ORM是对象关系映射,具体如何映射的,请看下图:
django ORM操作数据库
第一步去应用下的models.py
下创建类,对应到MySQL中的表,类必须继承models.Model
,每个应用用到什么表就创建什么类。
from django.db import models
# Create your models here.
class Info(models.Model):
# id int primary key auto increment
id = models.AutoField(primary_key=True,verbose_name=id)
# username varchar(32)
username = models.CharField(max_length=32,verbose_name=username)
# password int
password = models.IntegerField(verbose_name='password')
但是此时在数据库中并未产生任何数据,如果想要在数据库中产生对应的表就必须执行数据库迁移命令,并且只要修改了models.py
中和数据库相关的代码就必须执行数据库迁移命令,数据库迁移命令有两条:
python manage.py makemigrations // 将数据记录在小本本上(migrations文件夹中会产生一个文件)
python manage.py migrate // 将操作真正的同步到数据库中
django ORM常用字段和字段参数
首先看一下常用的字段:
AutoField: int自增整形字段,一个model不能有两个该字段
primary_key:是否自增,设置为True字段是主键列
IntegerField: 整数类型,不需要额外的其他参数
CharField: 字符串类型
必须提供max_length参数表示接收的字符串的最大长度
TextField: 大段文本类型
DateTimeField:时间类型,年月日时分秒都显示
auto_now:为True时修改某条记录时的最新时间
auto_now_add:为True时新增数据会自动保存当前时间,修改数据不会
DateField:时间类型,只显示年月日
auto_now:为True时修改某条记录时的最新时间
auto_now_add:为True时新增数据会自动保存当前时间,修改数据不会
EmailField: 邮箱格式的字符串数据,在存数据时会校验是否是邮箱格式。
BoolenField:存储布尔值,只有两种情况
ImageField: 图片类型
upload_to:文件图片上传只服务器端的路径
width_field: 限制图片的宽度
height_field:限制图片的高度
FieldField: 文件类型
upload_to:文件上传只服务器端的路径
下面是字段中常用的参数:
null: 表示数据是否非空,默认是False,推荐使用默认即可
blank:默认是False,验证表单数据是否为空,推荐设为True
default:数据库字段设置默认值
unique:表示数据是否唯一
to: 字段与哪张表有外键关系
on_delete:
当等于models.CASCADE是表示级联更新级联删除
当等于models.DO_NOTHING时表示不级联更新,此时还需要另一个参数db_constraint
db_constraint: 当等于True时表示斩断有关联表之间的`联系`,有关联的表只存在逻辑上的外键关系,推荐使用
db_index: 表示字段是否是索引字段
db_column: 字段在数据库中的名字,如果不设置,数据库中的字段就是模型中属性的名字
choices: 类似于MySQL中的枚举enum,具体使用方式如下
gender_choice = (
(0, 'male'),
(1, 'female'),
(2, 'others')
)
gender = models.IntegerField(choices=gender_choice, default=0) # 表示gender字段的值只有三种0,1,2, 默认是0
verbose_name:django admin中显示的表字段的名称
help_text:类似于备注
另外对于一些模型级别的配置即表级别的配置可以在模型中定义一个Meta
类,在这个类中添加一些属性控制表的其他属性,比如联合索引、指定表名而不是使用模型的类型:
class User(models.Model):
id = models.AutoField(primary_key=True, verbose_name='主键')
name = models.CharField(max_length=10, verbose_name='用户名')
age = models.IntegerField(verbose_name='年龄')
class Meta:
db_table = '用户表' # 指定表名而不是使用类名User
index_together = [(name, age)] # name age字段组成联合索引不一定唯一
unique_together = [(name,age)] # name age组成联合唯一索引
django ORM操作表数据 --- 单表增删改查
搭建测试环境
在对数据进行操作时为了方便,不在视图函数中进行操作,而是在django的测试环境中进行操作,因此首先需要搭建测试环境,在django应用中有一个test.py
是django提供的测试文件,当然也可以自己在应用下创建测试的py文件,搭建测试环境需要加载django项目的配置文件,只有加载了django的项目配置文件才能使用测试文件,在测试文件中写入下述代码测试环境即搭建完成:
if __name__ == '__main__':
import os
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'first_django.settings') # 此行代码来自于manage.py-加载配置
django.setup()
# 在这下面写操作数据库的代码,注意缩进哦~
首先创建一张表,基于这张表进行增删改查的基本操作:
# models.py
from django.db import models
class User(models.Model):
id = models.AutoField(primary_key=True) # 不写的话Django会自动创建,且列名为id
username = models.CharField(max_length=8, verbose_name='用户名')
age = models.IntegerField(verbose_name='年龄')
GENDER = (
(0, 'male'),
(1, 'female'),
(2, 'others')
)
gender = models.IntegerField(choices=GENDER, verbose_name='性别')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
alter_time = models.DateTimeField(auto_now=True, verbose_name='最后修改时间')
def __str__(self):
return self.username
千万不要忘记执行数据库迁移命令!
python manage.py makemigrations
python manage.py migrate
去数据库验证是否已经产生了新的表,表名是应用名_类名小写
,发现已经创建好了:
mysql> use practice;
Database changed
mysql> show tables;
+----------------------------+
| Tables_in_practice |
+----------------------------+
| auth_group |
| auth_group_permissions |
| auth_permission |
| auth_user |
| auth_user_groups |
| auth_user_user_permissions |
| django_admin_log |
| django_content_type |
| django_migrations |
| django_session |
| first_user |
+----------------------------+
11 rows in set (0.00 sec)
数据基本增删改查
下面就可以在test.py
中进行数据的增删改查了。需要提到一点,就是在写orm语句时,当使用主键字段当做查询条件时,无论主键字段名是什么都可以使用pk
代替。
# test.py
if __name__ == '__main__':
import os
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'first_django.settings')
django.setup()
from first import models
# 增加数据,create用来增加数据
user_info = {
'username': 'python',
'age': 10,
'gender': 0,
}
user_obj = models.User.objects.create(**user_info) # 返回User的对象
# 也可以写成user_obj = models.User.objects.create(username='python', age=10, gender=0)
print(user_obj)
# 修改数据
# 修改方式1,推荐使用此种方式
alter_info = {
'username': 'java'
}
edit_obj = models.User.objects.filter(pk=1).update(**alter_info)
print(edit_obj)
# 修改方式2这种方式不推荐使用,在字段特别多的时候效率会非常低
# ①首先获取需要修改的对象,filter用来筛选数据得到的是一个类似列表的结构
edit_obj = models.User.objects.filter(id=1).first() # .first()只要第一个数据
# ②使用对象.属性的方式进行修改,
edit_obj.username = alter_info['username']
edit_obj.save() # 不要忘记保存
# 删除数据
# 批量删除
res = models.User.objects.filter(username='python').delete()
# 单一删除
res = models.User.objects.filter(pk=1).first().delete()
# 查询数据,filter的条件可以有多个,以逗号连接多个条件,表示and关系
# 查询单条数据
user_obj = models.User.objects.filter(username='java', age=5)
print(user_obj) # <QuerySet [<User: java>]>得到queryset对象,支持索引取值但是不推荐,推荐使用first方法取值
print(user_obj.first()) # java 获取对象
# 查询所有数据
user_objs = models.User.objects.all()
print(user_objs) # <QuerySet [<User: java>, <User: python>]>
查看ORM语句对应的SQL
如果想要查看ORM语句对应的SQL语句的话,有两种方式:
# 方式一:queryset对象.query可以得到返回值是queryset对象的ORM语句对应的SQL语句
user_objs = models.User.objects.all()
print(user_objs.query)
# 方式二:所有的SQL语句都可以查看,需要配置文件中进行配置,打印ORM语句返回值时会直接显示对应的SQL语句
# 将下述代码配置到配置文件中即可
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}}
ORM语句其他方法
上面所有的增删改查操作都是单表操作,除了上述提到的方法之外,还需要介绍另外的一些方法,下述方法只有QuerySet对象
可以调用。:
last(): queryset对象中的最后一个对象
values('username'):可以获取指定字段的数据,类似于select username, age from user;也可以不指定,返回结果是queryset对象,列表套字典的格式:<QuerySet [{'username': 'python'}, {'username': 'python'}, {'username': 'python'}, {'username': 'python'}]>
value_list('username'):可以获取指定字段的数据,返回的结果是queryset对象,列表套元组,<QuerySet [('python',), ('python',), ('python',), ('python',)]>
distinct():去重操作,去重一定要是一摸一样的数据,如果带有主键去重,肯定无法进行去重比如:models.User.objects.values('name','age').distinct()
order_by():排序操作,默认是升序,在排序规则前加-就是降序
models.User.objects.order_by('age')
models.User.objects.order_by('-age')
reverse():反转操作,反转的前提是数据已经排过序了,必须跟在order_by之后
models.User.objects.order_by('name').reverse()
count():统计数量
res = models.User.objects.count()
exclude():将某条数据排除在外
res = models.User.objects.exclude(name='java')
print(res)
exists(): 判断某个数据是否存在,基本用不到,因为数据本身就自带布尔值,返回的是布尔值
res = models.User.objects.filter(pk=2).exists()
print(res)
查询数据 - 双下划綫
在查询数据的时候,很多时候筛选条件都不是=
,还有很多筛选条件是>=,<=,<,>
还有模糊查询、区间查询等,那么这些又如何进行查询呢?很多小伙伴觉得那就用>=,<=,<,>
这些符号不行吗?这里明确的说明在django ORM的筛选条件中不支持直接写上述符号,而是使用神奇的双下划綫方法
。双下划綫可以在filter()
中实现神奇的操作,灵活的实现数据的过滤查询,基本语法filter(字段名__方法)
,以下面的例子进行说明双下划綫的用法
:
# 大于: __gt
models.User.objects.filter(age__gt=3) # 过滤年龄 > 3的
# 小于:__lt
models.User.objdects.filter(age__lt=3) # 过滤年龄 < 3的
# 大于等于:__gte
models.User.objdects.filter(age__gte=3) # 过滤年龄 >=3 的
# 小于等于:__lte
models.User.objdects.filter(age__lte=3) # 过滤年龄 <=3 的
# 区间过滤
models.User.objects.filter(age__in[1, 2, 3]) # 年龄是其中之一的
models.User.objects.filter(age_range=[1, 10]) # 年两在1-10之间的,包括首尾
# 模糊查询
models.User.objects.filter(name__contains='p') # 区分大小写名字包含p的
models.User.objects.filter(name__icontains='p') # 不区分大小写,名字包含p的
models.User.objects.filter(name__startswith='p') # 名字以p开头的
models.User.objects.filter(name__endswith='a') # 名字以a结尾的
# 时间查询
models.User.objects.filter(register_time__year='2021') # 时间是2021年的
models.User.objects.filter(register_time__month='7') # 时间是7月的
models.User.objects.filter(register_time__day='21') # 时间是21日的