Django模型(数据库)
Django模型
Django对各种数据库提供了很好的支持,包括:PostgreSQL、MySQL、SQLite、Oracle。Django为这些数据库提供了统一的调用API,可按需选择不同数据库。
Django模型使用自带的ORM(对象关系映射Object Relational Mapping)用于实现面向对象编程语言里不同类型系统的数据之间的转换。
ORM 在业务逻辑层和数据库层之间充当了桥梁的作用。ORM 是通过使用描述对象和数据库之间的映射的元数据,将程序中的对象自动持久化到数据库中。
ORM解析过程:
- ORM将Python代码转换成SQL语句
- SQL语句通过pymysql传送到数据库服务端
- 在数据库中执行SQL语句并将结果返回
- 使用ORM优点:提高开发效率、不同数据库可以平滑切换。
- 使用ORM缺点:ORM代码转换为SQL语句时,需花费一定时间,执行效率会有所降低; 长期写ORM代码,会降低编写SQL语句的能力。
1. MySQL安装
官网:https://dev.mysql.com/
选择安装版下载:https://dev.mysql.com/downloads/mysql/ ,点击No thanks, just start my download.可立即下载。下载完后解压到本地目录,如 E:\soft\mysql8023
初始化配置:
解压后,在目录E:\soft\mysql8023\mysql-8.0.23-winx64下,
- 新建一个Data文件夹,用于mysql数据库的数据存放,
- 新建一个 my.ini 配置文件(新建文本,重命名为my.ini),内容如下:
E:\soft\mysql8023\mysql-8.0.23-winx64\my.ini 文件:
查看代码
[mysqld]
# 设置3306端口
port = 3306
# 设置mysql的安装目录
basedir = E:\\soft\\mysql8023\\mysql-8.0.23-winx64
# 设置mysql数据库的数据存放目录
datadir = E:\\soft\\mysql8023\\mysql-8.0.23-winx64\\Data
# 允许最大连接数
max_connections = 200
# 允许连接失败的次数。这是为了防止有人从该主机试图攻击数据库系统
max_connect_errors = 10
# 服务端使用的字符集默认为UTF8
character-set-server = utf8
# 创建新表时将使用的默认存储引擎
default-storage-engine = INNODB
# 默认使用“mysql_native_password”插件认真
default_authentication_plugin = mysql_native_password
[mysql]
# 设置mysql客户端默认字符集
default-character-set = utf8
[client]
# 设置mysql客户端连接服务端时默认使用的端口
port = 3306
default-character-set = utf8
接下来初始化数据,找到cmd,右键以管理员权限打开cmd:
cd到bin目录: cd /d E:\soft\mysql8023\mysql-8.0.23-winx64\bin
执行命令(初始化数据库):mysqld –initialize –console
执行完成后,会输出root用户的初始密码,如:2021-03-14T10:27:59.584362Z 6 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: YQDfWiiKU0;Z
YQDfWiiKU0;Z就是初始密码,后续登录需要用到,你也可以在登陆后修改密码。
注:要是关掉了或没记住,可删掉初始化的datadir目录(即建的Data文件夹),再执行一遍初始化命令mysqld –initialize –console,又会重新生成。
安装mySQL或执行以上命令时报错找不到vcruntime140_1.dll的解决方案:缺少了动态链接库(.ddl文件), 跟mysql无关, 下载一份即可。下载地址:https://pan.baidu.com/s/1QriqbRAA4l_40Kc0-007jQ 提取码:uyp7
https://cn.dll-files.com/download/6fe223ce568d919f80bea233738d0628/vcruntime140_1.dll.html?c=RE5hVFFwZFVkVk16MlY3dW43NDkyQT09
64位,解压后将vcruntime140_1.dll复制到C:\Windows\System32,而32位版本的需要复制到C:\Windows\SysWOW64下
安装&启动mysql服务
接着在bin目录下执行安装命令:mysqld install
或 mysqld –install mysql
再执行启动命令:net start mysql
通过命令net stop mysql
可停止服务。
登录MySQL
当 MySQL 服务已经运行时, 我们可以通过 MySQL 自带的客户端工具登录到 MySQL 数据库中, 首先打开命令提示符, 输入以下格式的命名:
mysql -h 主机名 -u 用户名 -p
- -h: 指定客户端所要登录的 MySQL 主机名, 登录本机(localhost 或0.0.1)该参数可以省略;
- -u: 登录的用户名;
- -p: 告诉服务器将会使用一个密码来登录, 如果所要登录的用户名密码为空, 可以忽略此选项。
登录本机的MySQL,只需输入:mysql –u root –p
按回车后提示输入密码(若密码存在, 输入密码登录, 不存在则直接按回车登录。)
登录成功后你将会看到 Welcome to the MySQL monitor... 的提示语,然后命令提示符会一直以 mysq> 加一个闪烁的光标等待命令的输入, 输入 exit 或 quit 退出登录。
修改密码(注意命令结尾一定要有;):在MySQL安装目录bin目录下执行命令: 输入前面安装时的密码YQDfWiiKU0;Z 填入即可登录成功,进入mysql交互模式。 在MySQL中执行命令:ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456'; |
2. navicat连接mysql
navicat是一个连接数据库的可视化工具,可以连接mysql和oracle做一些简单增删改查。
navicat安装
版本多,正版收费。链接: https://pan.baidu.com/s/17L_yV9ikcOFUrbVJmiNnFw 提取码: hrah
一路傻瓜式安装,到注册页面,名和组织随便写,注册码网上找
连接mysql
mysql是本地安装的,先启动mysql服务(任务管理器找到mysql启动,或命令 net start mysql )
![]() |
其他机器上就是对应的host地址
配置好之后点击【连接测试】,连接成功后进入。 |
新建表
点mytest,右键-打开连接:展开连接的mysql库 --> 右键-新建数据库:新建数据库testdb
点testdb,右键-打开数据库:展开testdb数据库 --> 点testdb下的【表】,右键-新建表:user
【添加栏位】可添加多个字段
【主键】可设置某个字段为主键,如id
3. django连接mysql
常见的Mysql驱动介绍:
- MySQL-python:即MySQLdb。是对C语言操作MySQL的简单封装。只支持Python2,不支持python3。
- mysqlclient:是MySQL-python的另外一个分支。支持Python3并且修复了一些bug。是一个C扩展模块,编译安装可能会导致报各种错误。
- pymysql:纯Python实现的一个驱动。因为是纯Python编写的,因此执行效率不如MySQL-python。并且也因为是纯Python编写的,因此可以和Python代码无缝衔接。
- MySQL Connector/Python:MySQL官方的纯Python连接MySQL的驱动。因纯Python开发的效率不高。
大型项目用MySQLclient,性能比较优异。中小项目用pyMySQL,Python写的契合度更高。
mysqlclient或pymysql安装:
django连接mysql需要先安装驱动,使用pip安装:
mysqlclient安装:pip install mysqlclient
或 pip install mysqlclient==2.0.3
pymysql安装: pip install pymysql
3.1 创建数据库
(ORM无法操作到数据库级别,只能操作到数据表):
1.使用navicat创建( 见 2. navicat连接mysql )
2.登录mysql命令创建(cd到mysql的bin目录,或将bin目录添加到环境变量PATH里):登录mysql命令:mysql –u root -p
create database 数据库名称;
create database 数据库名称 default charset=utf8; -- 防止编码问题指定为 utf8
CREATE DATABASE [IF NOT EXISTS] <数据库名> -- 创建之前判断,只有该数据库不存在时才能执行
[[DEFAULT] CHARACTER SET <字符集名>] -- 指定数据库的字符集,防止编码问题,指定为 utf8
[[DEFAULT] COLLATE <校对规则名>]; -- 指定字符集的默认校对规则,即排序规则
COLLATE在国内比较常用的是utf8mb4_general_ci(默认)、utf8mb4_unicode_ci、utf8mb4_bin 这三个,建议使用utf8mb4_unicode_ci。
从mysql 8.0开始,mysql默认的CHARSET改为了utf8mb4,默认的COLLATE也改为了utf8mb4_0900_ai_ci。
mysql> CREATE DATABASE IF NOT EXISTS testdb2
-> DEFAULT CHARACTER SET utf8mb4
-> DEFAULT COLLATE utf8mb4_0900_ai_ci;
3.2 django配置数据库
setting.py文件中找到 DATABASES 配置项,django默认连接 sqllite 。
ENGINE:是指连接数据库驱动的名称,有以下几种情况:
- db.backends.postgresql 连接PostgreSQL
- db.backends.mysql 连接mysql
- db.backends.sqlite3 连接sqlite
- db.backends.oracle 连接oracle
这里连接mysql需要账户密码,也就是之前安装mysql的用户名root和设置的密码123456 ,NAME是数据库名称,连接配置如下:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'testdb',
'USER': 'root',
'PASSWORD': '123456',
'HOST': 'localhost',
'PORT': '3306',
}
}
如果使用pymysql,需告诉 Django 使用 pymysql 模块连接 mysql 数据库:
HelloWorld/ HelloWorld /__init__.py # 在与settings.py同级目录下的__init__.py中引入模块和进行配置
import pymysql
pymysql.install_as_MySQLdb()
3.3 创建表,同步到mysql
创建了APP应用(需settings.py 中INSTALLED_APPS添加应用名),应用下有个models.py文件,
修改HelloWorld/helloApp/models.py文件:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=30)
pwd = models.CharField(max_length=50)
mail = models.EmailField(max_length=30)
iphone = models.BigIntegerField(max_length=24)
update_time = models.DateTimeField(auto_now=True)
create_time = models.DateTimeField(auto_now_add=True)
类名代表了数据库表名,且继承了models.Model ,类里面的字段代表数据表中的字段(name),数据类型则由CharField(相当于varchar)、DatetTmeField(相当于datetime),max_length参数限定长度。
先创建表结构,在数据库里面新增一些表。再cd到django的根目录(即项目所在目录,如E:\PyCharm\HelloWorld),执行以下命令进行同步:
$ python manage.py migrate # 创建表结构
$ python manage.py makemigrations helloApp # 让 Django 知道我们在我们的模型有一些变更
$ python manage.py migrate helloApp # 创建表结构
看到几行 "Creating table…" 的字样,你的数据表就创建好了。
表名组成结构为:应用名_类名(如类名为Person,数据库表名为helloapp_person)
注意:尽管我们没有在 models 给表设置主键,但是 Django 会自动添加一个 id 作为主键。
makemigrations 这一步执行后,会在当前app目录下生成一个 migrations 目录,该目录下的内容就是数据库要执行的内容。
migrate 就是执行之前生成的 migrations 文件,这一步才是操作数据库的一步,执行完成后,数据库里会新增一张表helloapp_person。
![]() |
|
3.4 表的字段类型和字段参数
字段类型:
- models.AutoField 自增列=int(11) 如果没有的话,默认会生成一个名称为id的列,如果要显示为一个自定义自增列,必须将列设置为主键 primary_key=True
- models.CharField 字符串字段,必须给max_length参数
- models.TextField 字符串=longtext
- models.BlooleanField 布尔型字段=tinyint(1) 不能为空,blank=True
- models.NullBooleanField 允许为空的布尔类型
- models.CommaSeparatedIntegerField 用逗号分割的数字=varchar。继承CharField,所以必须给max_length参数
- models.DateField 日期类型date,参数:auto_now=True则每次都会更新这个时间;auto_now_add=True则是第一次创建添加,之后的更新不会改变
- models.DateTimeField 日期类型datetime,同DateField的参数
- models.TimeField 时间 HH:MM:[ss[.uuuuuu]]
- models.EmailField 字符串类型(正则表达式邮箱)=varchar 对字符串进行正则表达式
- models.URLField 字符串,地址正则表达式
- models.FloatField 浮点类型=double
- models.IntegerField 整型(-2147483648,2147483647)
- models.BigIntegerField 长整型(-9223372036854775808, 9223372036854775807)
- models.SmallIntegerField 数字(-32768,32767) 数据库中的字段有:tinyint、smallint、int、bigint
- models.PositiveIntegerField 正Integer(0,32767)
- models.PositiveSmallIntegerField 正smallInteger(0, 2147483647)
- models.SlugField 减号、下划线、字母、数字
- models.IPAddressField 字符串类型(ip4正则表达式)
- models.GenericIPAddressField 字符串类型(ip4和ip6是可选的)。参数:protocol可以是:'both' 'ipv4' 'ipv6' 验证时会根据设置报错
- models.DecimalField 十进制小数类型=decimal。必须指定整数位max_digits和小数位decimal_places
- models.BinaryField 二进制
- models.ImageField 图片
- models.FileField 文件
字段参数:
- null=True 数据库中字段是否可以为空
- blank=True django的Admin中添加数据时是否可以允许空值
- primary_key=False 主键,对AutoField设置主键后,就会代替原来的自增id列
- auto_now=True 自动创建—无论添加或修改,都是当前操作时间
- auto_now_add=True 自动创建—永远是创建时的时间
- choice 选项框,定义一个元组变量,参数值取该变量
GENDER_CHOICE = ((u'M',u'Male'),(u'F',u'Female')) gender= models.BigIntegerField(choices=GENDER_CHOICE)
- max_length
- default 默认值
- verbose_name Admin中字段的现实名称
- db_column 数据库中的字段名称
- unique=True 不允许重复
- db_index=True 数据库索引
- editable=True 在Admin里是否可编辑
- error_messages=None 错误提示
- auto_created=False 自动创建
- help_text 在Admin中提示帮助信息
- validators=[]
- upload-to
自主设置 主键primary_key 例子:
django的models新增数据库表时,如果不设置主键,会默认新增一个id为主键,想自己设置一个字段为主键,需加个参数primary_key=True
HelloWorld/helloApp/models.py文件:
from django.db import models
#新增产品表,设置产品名称为主键,让产品名不重复
class Prouct(models.Model):
product_name = models.CharField(max_length=20,primary_key=True)
person_email = models.EmailField(max_length=30)
4. 连接已有的mysql数据库
跟以上一样配置连接数据库,只不过不需要在models.py文件新建表。而是直接集成已有的数据库和应用,生成models,复制到应用app的models.py中。
在项目的manage.py所在路径下使用命令:
python manage.py inspectdb > E:\PyCharm\HelloWorld\helloApp\models.py |
命令结束返回无报错即成功,类名即数据库表名,会自动转大写,继承自models.Model,每个类定义有数据表内的字段名。之后的操作就没区别了。
5. 数据库操作
5.1. 新增数据
在setting.py同一级目录新建一个testdb.py文件
HelloWorld/ HelloWorld /testdb.py
# -*- coding:utf-8 -*-
from django.http import HttpResponse
from helloApp.models import Person
# 数据库操作
def add_user(request):
test1 = Person(name='叶凡' ,age=1000)
test1.save()
return HttpResponse("数据库helloApp_Person用户添加成功")
HelloWorld/ HelloWorld /urls.py 配置访问地址
from django.conf.urls import url
from django.urls import re_path,path
from . import views,testdb
from helloApp import views as view #引入文件名同名,可重命名
urlpatterns = [
url(r'^adduser$',testdb.add_user)
]
浏览器打开http://127.0.0.1:8000/adduser 每访问一次,数据库里面就会新增一条记录
5.2 查询数据
5.2.1 关于查询的几种方法
- 通过objects这个模型管理器的 all() 获取所有数据行,相当于SQL中的select * from
a = Person.objects.all()
res = ''
for var in a:
res = res + var.name + " "
response = res
- filter() 相当于SQL中的where,可设置条件过滤结果, 结果为list
b = Person.objects.filter(name='叶凡')
- get() 获取单个对象
c = Person.objects.get(name='叶凡')
- 索引 限制返回的数据,相当于SQL中的offset 0 limit 2; 如下返回2条
d = Person.objects.order_by('name')[0:2]
- order_by()查询结果排序
e = Person.objects.order_by('id')
- 上面的方法可以连着使用
d = Person.objects.filter(name='帝尊').order_by('id')[0:2]
5.2.2 查询数据
还是在setting.py同一级目录的testdb.py文件
HelloWorld/ HelloWorld /testdb.py
# -*- coding:utf-8 -*-
from django.http import HttpResponse
from helloApp.models import Person
# 查询数据方法
def select_age(request):
# 方法一:通过objects模型管理器的get()获取name='叶凡'的数据行,直接取age的值
ag = Person.objects.get(name='叶凡').age
# 方法二:filter相当于SQL中的where,可设置条件过滤结果,
# 查询结果是list,取下标后,获取age字段的值
test4 = Person.objects.filter(name='叶凡')
ag = test4[0].age
return HttpResponse("<p>查询结果:%s</p>" %ag)
HelloWorld/ HelloWorld /urls.py 配置访问地址
from django.conf.urls import url
from django.urls import re_path,path
from . import views,testdb
from helloApp import views as view #引入文件名同名,可重命名
urlpatterns = [
url(r'^adduser',testdb.add_user),
url(r'^selectuser$',testdb.select_age)
]
浏览器打开http://127.0.0.1:8000/selectuser 每访问一次,就会按里面的方法查询一次。
5.3 更新数据
还是在setting.py同一级目录的testdb.py文件
HelloWorld/ HelloWorld /testdb.py
# -*- coding:utf-8 -*-
from django.http import HttpResponse
from helloApp.models import Person
# 更新数据方法
def update_user(request):
# 方法一:通过objects模型管理器的get()获取指定数据行,修改再save,相当于SQL中的update
test2 = Person.objects.get(name='无始')
test2.age = '120000'
test2.save()
# 方法二:通过objects模型管理器的filter()获取id=2的数据行,再update指定字段
Person.objects.filter(id=2).update(age='3000')
# 修改所有行: 通过objects模型管理器的all()获得所有数据行,相当于SQL中的SELECT * FROM,
Person.objects.all().update(age='100')
return HttpResponse("<p>年龄修改成功</p>")
HelloWorld/ HelloWorld /urls.py 配置访问地址
from django.conf.urls import url
from django.urls import re_path,path
from . import views,testdb
from helloApp import views as view #引入文件名同名,可重命名
urlpatterns = [
url(r'^adduser',testdb.add_user),
url(r'^selectuser$',testdb.select_age),
url(r'^updateuser$',testdb.update_user)
]
浏览器打开http://127.0.0.1:8000/updateuser 每访问一次,就会按里面的方法更新一次。
5.4 删除数据
还是在setting.py同一级目录的testdb.py文件
HelloWorld/ HelloWorld /testdb.py
# -*- coding:utf-8 -*-
from django.http import HttpResponse
from helloApp.models import Person
# 更新数据方法
def delete_user(request):
# 方法一:通过objects模型管理器的get()获取name='叶凡'的数据行,再delete该行
test3 = Person.objects.get(name='叶凡')
test3.delete()
# 方法二:通过objects模型管理器的filter()获取id=1的数据行,再delete该行
Person.objects.filter(id=1).delete()
# 删除所有数据:通过objects模型管理器的all()获取所有数据行,再delete
Person.objects.all().delete()
return HttpResponse("<p>数据删除成功</p>")
HelloWorld/ HelloWorld /urls.py 配置访问地址
from django.conf.urls import url
from django.urls import re_path,path
from . import views,testdb
from helloApp import views as view #引入文件名同名,可重命名
urlpatterns = [
url(r'^adduser',testdb.add_user),
url(r'^selectuser$',testdb.select_age),
url(r'^updateuser$',testdb.update_user),
url(r'^deleteuser$',testdb.delete_user)
]
浏览器打开http://127.0.0.1:8000/deleteuser 每访问一次,就会按里面的方法删除一次。
备注:要是表里没有要删除的数据,会报错:Person matching query does not exist.
5.5 查询表结果详解
django查询数据库的方法很多,不同的方法返回的结果也不太一样,以下13个查询方法:
- 返回对象是对象列表的:all() filter() exclude() order_by() reverse() values() values_list() distince()
- 返回结果是对象的:get() first() last()
- 返回结果是布尔值的:exists()
- 返回结果是数字的:count()
准备数据(之前的helloapp_person表)
get() 从数据看取得唯一一个匹配的结果,返回一个对象。调用者是objects管理器,返回查询到model对象(查询结果有且只有一个才执行)。
查询结果有多个,会报错MultipleObjectsReturned,如果查询结果有0个,会报错DoesNotExist。
r = Person.objects.get(name='叶凡').age()
ret = Person.objects.get(name='叶凡')
r = ret.name + str(ret.age)
first() 和 last() 返回第一条记录和返回最后一条记录,返回的是一个对象。调用者是queryset,返回model对象
#同 fir = Person.objects.all().orderby('age')[0]
fir = Person.objects.all().order_by("age").first()
f = fir.age
fir = Person.objects.all().order_by("age").first().age
las = Person.objects.all().order_by("age").last().age
exists() 如果queryset包含数据,就返回True,否则返回False。调用者是queryset,返回布尔值
is_exist = Person.objects.all().exists()
count() 数数,由queryset对象调用,返回int
ret = Person.objects.all().count()
可迭代对象queryset all()
查询整张表sql:select * from helloapp_person;
django里查询数据库不需写sql语句,而是objects模型管理器,ret=helloapp_person.objects.all() 返回的是整个表的内容,这里返回的是可迭代对象queryset,要取出数据,需要用for循环读取。
HelloWorld/ HelloWorld /testdb.py setting.py同级
from django.http import HttpResponse
from helloApp.models import Person
def select_all(request):
ids,names,ages = '',"",""
ret = Person.objects.all()
for i in ret:
ids += " " + str(i.id)
names += " " + i.name
ages += " " + str(i.age)
return HttpResponse("<p>查询id结果:%s</p><p>查询name结果:%s</p><p>查询age结果:%s</p>"%(ids,names,ages))
HelloWorld/ HelloWorld /urls.py 配置访问地址
url(r'^selectall$',testdb.select_all)
浏览器打开http://127.0.0.1:8000/selectall,访问结果如图一,结果按列汇总展示。当然,也可以读取记录,按每行展示,如图2
for i in ret:
names += "<p>id:" + str(i.id) + ",name:" + i.name + ",age:" + str(i.age) + "</p>"
return HttpResponse(names)
筛选条件 filter()
类似SQL中的where语句:select * from helloapp_person where id=7 and name='叶凡';
filter()查询结果也是可迭代对象,如果只有一个,可以通过下标取值。找不到的时候给个默认值null。
HelloWorld/ HelloWorld /testdb.py setting.py同级
# -*- coding:utf-8 -*-
from django.http import HttpResponse
from helloApp.models import Person
def filter_user(request):
r = ""
ret = Person.objects.filter(id=7,name='叶凡')
try:
r = ret[0].age
except:
r = "null"
return HttpResponse("<p>查询结果:%s</p>" %r)
HelloWorld/ HelloWorld /urls.py 配置访问地址
url(r'^filteruser$',testdb.filter_user)
浏览器打开http://127.0.0.1:8000/filteruser ,找得到展示结果,找不到展示null
可迭代的字典序列 values()
all()和filter()返回的都是可迭代的queryset序列,平常我们习惯获取字典的对象,可用values()方法获取。
Person.objects.all().values(“id”,”name”) 类似于SQL语句:select id,name from helloapp_person
HelloWorld/ HelloWorld /testdb.py setting.py同级
from django.http import HttpResponse
from helloApp.models import Person
def values_user(request):
r = ''
ret = Person.objects.all().values("id","name","age")
for i in ret:
r += str(i)
return HttpResponse("<p>查询结果:%s</p>" %r)
HelloWorld/ HelloWorld /urls.py 配置访问地址
url(r'^valuesuser$',testdb.values_user)
浏览器打开http://127.0.0.1:8000/valuesuser 展示结果:
其他可迭代对象:
exclude() 它包含了与所给筛选条件不匹配的对象。由objects管理器调用,返回queryset。
#查找用户名不是“叶凡”的数据:
ret = Person.objects.exclude(name="叶凡")
order_by() 对查询结果排序。由queryset对象调用,返回值是queryset。默认是降序,在前面加 – 是代表降序。
# 查询所有数据,按age字段排序,默认升序
ret = Person.objects.all().order_by("age")
# 查询name='曹雨生'的数据,按age字段降序排序
ret = Person.objects.filter(name='曹雨生').order_by("-age")
reverse() 对查询结果反向排序。由queryset对象调用,返回值是queryset
# 查询所有数据,按age排序,默认升序,再反向排序,功能与-age一样
ret = Person.objects.all().order_by('age').reverse()
#查询name='曹雨生'的数据,再反向排序
ret = Person.objects.filter(name='曹雨生').reverse()
values_list() 和values()差不多,返回是元组。由queryset对象调用,返回值是queryset
ret = Person.objects.all().values_list("id","name")
distinct() 从返回结果中剔除重复记录。由queryset对象调用,返回值是queryset。 和SQL中的distinct去重一样。
ret = Person.objects.all().values("id","name","age").distinct()
5.6 查询结果转json(serializers)
django查询数据返回的是可迭代的queryset序列,如果不习惯这种数据,可以用serializers方法转成json数据,更直观。
返回json数据,需要用到JsonResponse。django查询数据库返回json有3种方法:
- serializers转json
- model_to_dict转字典
- values()转list (最简单,推荐!)
JsonResponse是HttpResponse的一个子类,用来帮助用户创建JSON编码的响应。源码:
class JsonResponse(data,encoder=DjangoJSONEncoder,safe=True,json_dumps_params=None,**kwargs):
- data: 应该传递一个标准的python字典给它,它将其转换成json格式数据。
- encoder:默认为django.core.serializers.json.DjangoJSONEncoder,用于序列化data。
- safe: 默认为True。如果为True但data不是dict对象,将抛出一个Typeerror;如果为False,可以传递任何对象进行序列化。
- json_dumps_params:可以传递一个python标准的json库中,json.dump()方法处理后的对象给它,用于生成一个响应。
另外:它默认Content-Type头部设置为application/json。以下为JsonResponse类源码:
serializers 转json
django里有个serializers方法可以,直接把查询的结果转换成json数据。
HelloWorld/ HelloWorld /testdb.py setting.py同级
from django.http import HttpResponse,JsonResponse
from helloApp.models import Person
from django.core import serializers
import json
def get_json(request):
data = {}
allPer = Person.objects.all()
data['result'] = json.loads(serializers.serialize("json",allPer))
return JsonResponse(data)
然后在urls.py配置访问地址,如下:
url(r'^get_json$',testdb.get_json)
访问地址http://127.0.0.1:8000/get_json 展示如下:
{"result": [{"model": "helloApp.person", "pk": 6, "fields": {"name": "\u66f9\u96e8\u751f", "age": 5000}}, {"model": "helloApp.person", "pk": 7, "fields": {"name": "\u53f6\u51e1", "age": 2000}}, {"model": "helloApp.person", "pk": 8, "fields": {"name": "\u66f9\u96e8\u751f", "age": 2000}}, {"model": "helloApp.person", "pk": 9, "fields": {"name": "\u5e1d\u5c0a", "age": 120000}}, {"model": "helloApp.person", "pk": 10, "fields": {"name": "\u5e1d\u5c0a", "age": 120000}}]}
serializers方法虽然可以直接转换成json数据,但是上面返回的结果有一些多余的字段model和pk,不是我们想要的。
model_to_dict 转字典
用model_to_dict方法把查询的queryset序列结果转成字典序列。
HelloWorld/ HelloWorld /testdb.py setting.py同级
from django.http import HttpResponse,JsonResponse
from helloApp.models import Person
from django.forms.models import model_to_dict
def to_dict(request):
ret = Person.objects.all()
json_list = []
for i in ret:
json_dict = model_to_dict(i)
json_list.append(json_dict)
return JsonResponse(json_list,safe=False)
然后在urls.py配置访问地址,如下:
url(r'^to_dict$',testdb.to_dict)
访问地址http://127.0.0.1:8000/to_dict 展示如下:
[{"id": 6, "name": "\u66f9\u96e8\u751f", "age": 5000}, {"id": 7, "name": "\u53f6\u51e1", "age": 2000}, {"id": 8, "name": "\u66f9\u96e8\u751f", "age": 2000}, {"id": 9, "name": "\u5e1d\u5c0a", "age": 120000}, {"id": 10, "name": "\u5e1d\u5c0a", "age": 120000}]
values() 转list
第三种:不需要导入方法,先用values()方法获取一个可迭代的dict序列,再用list函数转成list对象。
HelloWorld/ HelloWorld /testdb.py setting.py同级
from django.http import HttpResponse,JsonResponse
from helloApp.models import Person
def json_data(request):
data = {}
ret = Person.objects.all().values()
data["data"] = list(ret)
return JsonResponse(data,safe=False)
然后在urls.py配置访问地址,如下:
url(r'^json_data$',testdb.json_data),
访问地址http://127.0.0.1:8000/json_data 展示如下:
{"data": [{"id": 6, "name": "\u66f9\u96e8\u751f", "age": 5000}, {"id": 7, "name": "\u53f6\u51e1", "age": 2000}, {"id": 8, "name": "\u66f9\u96e8\u751f", "age": 2000}, {"id": 9, "name": "\u5e1d\u5c0a", "age": 120000}, {"id": 10, "name": "\u5e1d\u5c0a", "age": 120000}]}
5.7 JsonResponse返回中文
django查询到的结果,用JsonResponse返回在页面上显示类似于"\u66f9\u96e8\u751f",这是unicode编码,python3默认返回的编码。解决:加上参数 json_dumps_params={'ensure_ascii':False}
HelloWorld/ HelloWorld /testdb.py setting.py同级
from django.http import HttpResponse,JsonResponse
from helloApp.models import Person
def json_data(request):
data = {}
ret = Person.objects.all().values()
data["data"] = list(ret)
return JsonResponse(data,safe=False,
json_dumps_params={'ensure_ascii':False})
这样再访问地址http://127.0.0.1:8000/json_data 展示如下:
{"data": [{"id": 6, "name": "曹雨生", "age": 5000}, {"id": 7, "name": "叶凡", "age": 2000}, {"id": 8, "name": "曹雨生", "age": 2000}, {"id": 9, "name": "帝尊", "age": 120000}, {"id": 10, "name": "帝尊", "age": 120000}]}
5.8 数据库查询延伸
django对数据库进行条件查询格式:字段名+两个下划线+查询方法。这些查询方法get和filter都可以使用,但get每次返回的值是惟一的否则报错。
- all 返回全部数据,为queryset对象 Person.objects.all()
- lt 返回小于目标值的queryset; lte 返回小于等于目标值的queryset。 Person.objects.filter(id__lt=5) Person.objects.filter(id__lte=5)
- gt 返回大于目标值的queryset; gte 返回大于等于目标值的queryset。 Person.objects.filter(id__gt=4) Person.objects.filter(id__gte=4)
- 包含:contains区分大小写; icontains不区分大小写。 Person.objects.filter(name__contains=’ab’) Person.objects.filter(name__icontains=’ab’)
- 精确匹配:exact区分大小写;iexact不区分大小写。 Person.objects.filter(name__exact=’Dog’) Person.objects.filter(name__iexact=’Dog’)
- 起始匹配:startswith区分大小写;istartswith不区分大小写。 Person.objects.filter(name__startswith=’At’) Person.objects.filter(name__istartswith=’At’)
- 末尾匹配:endswith区分大小写;iendswith不区分大小写。 Person.objects.filter(name__endswith=’by’) Person.objects.filter(name__iendswith=’by’)
- startswith、endswith混合:Person.objects.filter(name__startswith=’A’,name__endswith=’y’)
- in:同时查找多个符合条件的数据,相当于批量的精确查找。 Person.objects.filter(id__in=[101,102,103])
- range:取范围内的值(两边都是闭合,两侧包含)只要是包含这个范围内的值否返回。 Person.objects.filter(id__range=[220,290])
- isnull:返回是空值的数据,True为空,False非空。 Person.objects.filter(id__isnull=True) # 返回[] Person.objects.filter(id__isnull=False) # 返回非空数据
- Q:多条件联合查询,使用前需导入Q模块 from django.db.models import Q
q1 = Q(create_time__year__in=[2018,2019])
q2 = Q(create_time__month=2)
blogs = BologModel.objects.filter(q1,q2)
使用Q()传入相应条件,然后把Q传入查询的函数中,可以传多个Q然后进行查询。q1,q2中间是逗号时取交集,中间是 | 时取并集。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!