ORM 映射
类映射为表
类属性映射为表字段
类的对象实例映射为一行记录
优点
- 避免写SQL语句,使用面向对象的方式来操作数据库,使用比较方便。
- 实现项目与数据库解耦,切换数据库只需简单配置。
缺点
- 针对复杂的业务,操作比较繁琐
- 性能没有SQL效率高
Django配置数据库
在配置文件settings.py中
DATABASES = {
"default":{
"ENGINE":"django.db.backends.mysql",
"HOST":"localhost",
"PORT":"3306",
"USER":"lauf",
"PASSWORD":"lauf123",
"NAME":"my_db",
"CHARSET":"utf8",
}
}
注意
django连接mysql使用python的mysqlclient库,需要安装:sudo pip3 install mysqlclient
若报错mysql_config,则需在linux系统环境中安装如下依赖:
sudo apt-get install libmysqlclient-dev
sudo apt-get install default-libmysqlclient-dev
sudo apt-get install python3-dev
#查看是否安装成功
sudo apt list --installed | grep -E "libmysqlclient-dev|python3-dev"
查看pip3是否安装成功
pip3 freeze | grep -i "mysqlclient"
另外也可以使用pymysql代替mysqlclient,操作方式为在settings.py 中配置:
import pymysql
pymysql.install_as_MySQLdb()
创建模型类
- 创建user应用;
- python manager.py startapp user
- 在user应用中,展示创建User模型类
#在项目目录下,创建user应用, linux环境中需要使用python3;windows下仅仅python
python3 manage.py startapp user
# 安装应用 settings.py
INSTALLED_APPS = [
...,
"user",
]
#为应用定义模型类 user>models.py
from django.db import models
# 表结构 user_t
# username varchar(50)
# password char(64) not null
# score decimal(4, 2)
# is_delete bool default false
# created_time datetime
# udpated_time datetime
class User(models.Model):
username = models.CharField("姓名",max_length=50,primary_key=True)#必须给max_length
password = models.CharField("密码",max_length=50,null=False)
score = models.DecimalField("分数",max_digits=4,decimal_places=2) #小数点位置
is_delete = models.BooleanField("是否删除",default=False)
created_time = models.DateTimeField("注册时间",auto_now_add=True)
updated_time = models.DateTimeField("更新时间",auto_now=True)
def __str__(self):
"""打印对象时,显示的信息"""
return self.username # 必须返回字符串
class Meta:
db_table = "user_table" #更改表名字
managed = True
# app_label = "default" # 控制使用的数据库,需要结合database_router.py一起使用,否则无法生成迁移文件、无法迁移
# 迁移模型类,到数据库中
python3 manage.py makemigrations
python3 manage.py migrate
在没有指定主键时,Django自动创建一个id作为主键。
练习为user表添加一个hobby字段
#在模型类中,增加类属性
hobby = models.CharField("爱好",max_length=50,default="") #防止之前的数据没有这个字段而报错,必须给一个default
#重新生成迁移文件, 迁移到数据库
模型类的字段类型
- BooleanField()
对应数据库中的tinyint,0/1; Django中为True/False - CharField()
对应数据库中的varchar,必须指定max_length - DateField()
对应数据库中的date,
参数,auto_now = True, 更新时间
auto_now_add = True,第一次插入时间
default 给一个默认值 - DateTimeField() 使用同DateField()
- FloatField() 对应数据库中的double
- DecimalField() 精确的小数,对应数据库decimal
必需参数:max_digits=5总位数,decimal_places=2 小数点位置 - EmailField() 对应数据库varchar
- IntegerField() -->int
- ImageField() —>varchar(100) 存储路径
- TextField —>longtext 长文本
类属性的约束
primary_key, bool, 是否为主键
blank,bool,是否为空,admin后台管理的时候可以不写内容的字段
null,bool,该列是否可为null空值
default,默认值
db_index,bool,是否在该列设置索引
unique,bool, 该列的值是否唯一,唯一索引
db_column列的名称,默认使用类属性作为列名
verbose_name, 后台显示的字段名字
练习
创建一个Book模型类,类属性如下:
title, 书名,字符类型,最大长度50,且值唯一
price, 价格,精确小数,最大5位数,一位小数
publish,出版社,字符类型,最大长度50,不能为 null
打印对象时,输出书名,且修改表名为book_table
每次修改模型类,都要迁移;迁移失败,则删除migrations下的迁移文件,除了__init.__py,删除库,重建库,重新迁移
模型类的CRUD
增加数据
#使用模型类的对象管理器objects.create
User.objects.create(username=xxx,password=xxx,email=xxx,phone=xxx)
#或者
user = User(username=xxx,password=xxx,email=xxx,phone=xxx)
user.save()
可以在Django shell中尝试,代码更改后需重新进入
python3 manage.py shell
>>from user.models import User
查询
User.objects.all() #查询所有数据-->QuerySet对象,遍历获取每个数据对象
#按照字段取值,返回的QuerySet为字典列表
User.objects.values('col1',"col2",...)
#返回的QuerySet为元组列表
User.objects.values_list("col1","col2")
#排序
User.objects.order_by("-age") #降序,默认升序
User.objects.values("username",'age').order_by("age",'-xx') #按照age升序
QuerySet对象.query #拿到sql语句
练习,
配置一个路由/user/all,
请求方式GET,
视图,show_all
#views.py
def show_all(request):
users = User.objects.all()
return render(request,"index.html",locals())
#locals()函数局部变量的字典
让前端以表格形式显示所有的用户数据
条件查询
User.objects.filter(username="jack",age=12) #多个条件and 连接,返回QuerySet对象
#排除查询
User.objects.exclude(username="jack")#排除该条件的数据,返回QuerySet
#获取一个对象,返回对象
User.objects.get(username="jack")
#查询到多个对象,报错
#没有数据也报错,注意异常捕获
#查询谓词
字段__谓词
User.objects.filter(id__exact=1) #id为1
User.objects.filter(id__gt=1) #id>1
User.objects.filter(id__lte=5) #id<=5
User.objects.filter(username__exact="jack") #username 为jack
User.objects.filter(username__contains="jack")#username中包含jack
User.objects.filter(username__startswith="j") #以j开头的
User.objects.filter(username__endswith="k")
User.objects.filter(username__in=["jack","tom"])#是否在范围内
User.objects.filter(age__range=(25,35))
更新
#更新一个对象
obj = User.objects.get(username="jack")
obj.age = 30
obj.save()
#批量更新
queryset = User.objects.filter(age__gt=5)
queryset.update(age=20)
在以表格形式显示用户信息的基础上,完成用户信息的更新,删除两个按钮的功能。
<a href=“book/update/{{ obj.id }}”>更新
<a href=“book/delete/{{ obj.id }}”>删除
input输入组件:
不可编辑,disabled=“disabled”
不提交,不设置name属性
删除
#删除单个对象
obj = User.objects.get(username="jack")
obj.delete()
#批量删除
queryset = User.objects.filter(age__lt=5)
queryset.delete()
#伪删除,不真正删除数据
is_delete = models.BooleanField("是否删除",default=False)
#查询数据时,只查询is_delete为False的数据
聚合查询与原生sql
F对象和Q对象
F对象,表示一行记录的某个字段的信息,而不获取数据。
from django.db import models
#在高并发处理中,防止操作错误
user = User.objects.get(username="jack")
user.age = models.F("age") + 1 #原来的值加1
#同一行数据的字段之间的比较
user = User.objects.filter(age__gt=models.F("oage"))
Q对象,包裹查询条件
Q(age__gt=5)&Q(age__lt=10) #且
Q(age__gt=5)|Q(username__exact="jack") #或
~Q(age__exact=5) #非
#查询姓名为‘jack’或者年龄不为20的用户
from django.db import models
queryset = User.objects.filter(models.Q(username="jack")|~models.Q(age__exact=20))
聚合
对查询结果,进行统计分析
from django.db.models import Sum,Avg,Min,Max,Count
#整表聚合
d = User.objects.aggregate(f=Count("id"))
#{"f":100}
#queryset聚合
User.objects.filter(username__contains="jack").aggregate(r=Count(id))
#分组聚合
#先拿到一列的值
c = User.objects.values("addr",...)
#分组,聚合
c.annotate(r=Count("id"))
#<QuerySet [{"addr":"China","r":5},{"addr":"B","r":8}]>
#例子
User.objects.values("email").annotate(r=Count("username"))
Out[35]: <QuerySet [{'email': 'test email', 'r': 1}, {'email': '1000000', 'r': 1}, {'email': 'g1', 'r': 1}, {'email': 'g2', 'r': 3}, {'email': 'g3', 'r': 1}, {'email': '[email protected]', 'r': 1}, {'email': '123', 'r': 1}, {'email': '123456', 'r': 1}]>
原生是sql
User.objects.raw(sql,[ parameter,])
返回RawQuerySet,只能查询,遍历取数据,不能做其他操作
不建议使用
User.objects.raw("select * from user_table where id=%s"%("5 or true"))
#以上查询出所有的数据,自己拼接的方式
#select * from user_table where id=5 or true;
#参数化的方式,防止sql注入
User.objects.raw("select * from user_table where id=%s",["5 or true"])
#django 拼接的结果 select * from user_table where id='5 or true'
#查询时,只取第一个5转为整数
#游标方式
from django.db import connection
with connection.cursor() as cur:
cur.execute(sql,[]) #写入
cur.execute("insert into user_table(username,password,email,phone,is_delete,created_time,updatd_time) values(%s,%s,%s,%s,%s,%s,%s)",["lucy","123","qq","234567",False,"2019/11/9","2019/11/9"])
#原生sql插入数据时,自己处理所有的字段
问题总结
- 使用ORM操作数据库时,尽量避免手动操作库表
- 模型类的级联删除、default默认值等,只有通过模型类操作db时才生效。
- 数据库中的表一旦生成,就不能手动删除。
- 局部迁移
python manage.py makemigrations app1
python manage.py migrate app1
- 继承的BaseModel需第一次就设计好,并迁移成功。
- User.objects.update_or_create()
# 更新 或者 创建
# 根据user字段查询,有则使用defaults更新;否则创建
User.objects.update_or_create(defaults={"token": token}, user=user)
迁移问题
- no migration to apply
删除django_migration表中的迁移记录;重新迁移