模型层
与数据库相关的,用于定义数据模型和数据库表结构。 在Django应用程序中,模型层是数据库和应用程序之间的接口,它负责处理所有与数据库相关的操作,例如创建、读取、更新和删除记录。Django的模型层还提供了一些高级功能
首先准备工作:切换mysql数据库
DATABASES = {
'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
'ENGINE': 'django.db.backends.mysql',
'NAME': 'db2',
'USER': 'root',
'PASSWORD': '12345',
'HOST': '127.0.0.1',
'PORT': 3306,
'CHARSET': 'utf8'
}
测试脚本
接下来我们可以使用测试脚本来直接运行,注意后面的Django代码必须写在测试脚本下面
把测试脚本放进一个py文件即可
import os
import sys
import django
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "项目名称.settings")
import django
django.setup()
django.setup()
from app01 import models
常用的关键字
create | 描述 |
---|---|
filter | 创建数据并直接获取当前创建的数据对象 |
first/last | 根据条件筛选数据 结果是QuerySet [数据对象1,数据对象2] |
update | 拿queryset里面第一个元素/拿queryset里面最后一个元素 |
delete | 删除数据(批量删除) |
all | 查询所有数据 结果是QuerySet [数据对象1,数据对象2] |
values | 根据指定字段获取数据 结果是QuerySet [{}},{},{},{}] |
values_list | 根据指定字段获取数据 结果是QuerySet [(),(),(),()] |
distinct | 去重 数据一定要一模一样才可以 如果有主键肯定不行 |
order_by | 根据指定条件排序 默认是升序 字段前面加负号就是降序 |
get | 根据条件筛选数据并直接获取到数据对象 一旦条件不存在会直接报错 不建议使用 |
exclude | 取反操作 |
reverse | 颠倒顺序(被操作的对象必须是已经排过序的才可以) |
count | 统计结果集中数据的个数 |
exists | 判断结果集中是否含有数据 如果有则返回True 没有则返回False |
示例
如果使用get,建议加上try
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoProject.settings")
import django
django.setup()
from app01 import models
try:
res = models.Userinfo.objects.filter(pk=1).get()
except Exception as f:
print(f)
value与value_list
res = models.Userinfo.objects.values('id', 'username', 'password')
for i in res:
print(i.get('id')) # <QuerySet [{'id': 1, 'username': 'kk', 'password': '1234'}, {'id': 2, 'username': 'cc', 'password': '232'}, {'id': 3, 'username': 'rer', 'password': '323'}]>
# 1 2 3 索引0为id,1为username
print(res) # <QuerySet [(1, 'kk', '1234'), (2, 'cc', '232'), (3, 'rer', '323')]>
查看内部sql语句
只有返回的结果是queryset对时才能用query查看
res = models.Userinfo.objects.values('id', 'username', 'password')
for i in res:
print(i.get('id'))
print(res.query)
"""SELECT `app01_userinfo`.`id`, `app01_userinfo`.`username`, `app01_userinfo`.`password` FROM `app01_userinfo`"""
distinct 去重
res = models.Userinfo.objects.all().values('password').distinct()
print(res)
order_by 排序
res1 = models.Userinfo.objects.all().order_by('username') # <QuerySet [<Userinfo: Userinfo object (2)>, <Userinfo: Userinfo object (1)>, <Userinfo: Userinfo object (3)>]
print(res1)
"""默认升序"""
基于双下划线的查询
查询年龄大于xx
"""查看年龄大于或小于32的数据"""
res = models.Userinfo.objects.filter(age__gt=32)\(age__lt=32)
print(res)
"""查看大于等于或小于等于32的数据"""
res = models.Userinfo.objects.filter(age__gte=32)\(age__lte=32)
print(res)
"""查看18-32之间的数据,收尾都要"""
res = models.Userinfo.objects.filter(age__range=[18, 32])
print(res)
"""查看带s字段的数据"""
res = models.Userinfo.objects.filter(username__contains='s')
print(res) # <QuerySet [<Userinfo: Userinfo object (2)>]>
"""姓名以c开头或结尾startwith\endwith"""
res = models.Userinfo.objects.filter(username__startswith='c')
print(res)
"""查看注册时间
两个参数
auto_now = false 当我们更新数据时,只要更新就会更新时间
auto_add = False 当auto_add=True时,添加数据时会自动添加当前时间
"""
register_time = models.DateTimeField(auto_now=True, auto_now_add=True)
多对多三种创建方式
全自动
使用ManyToManyField自动创建第三张表:(不推荐)
"""models文件"""
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name='书名')
price = models.CharField(max_length=32, verbose_name='价格')
"""通过orm自带的ManyToManyField自动创建第三张表"""
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name='姓名')
addr = models.CharField(max_length=32, verbose_name='地址')
authors = models.ManyToManyField(to='Book', related_name='author')
优点:较为简单,自动创建表,可以使用四种方法:add,set,remove,clear
缺点:拓展性差
手动
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name='书名')
price = models.CharField(max_length=32, verbose_name='价格')
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name='姓名')
addr = models.CharField(max_length=32, verbose_name='地址')
"""创建第三张表"""
class Author2Book(models.Model):
authors = models.ForeignKey(to='Author', on_delete=models.CASCADE)
books = models.ForeignKey(to='Book', on_delete=models.CASCADE)
优点:拓展性高
缺点:不能使用orm查询方法,add,set,remove,clear不能使用
半自动
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name='书名')
price = models.CharField(max_length=32, verbose_name='价格')
"""在book表中"""
books = models.ManyToManyField(to='Book', through='Author2Book', through_fields=('book', 'author'))
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name='姓名')
addr = models.CharField(max_length=32, verbose_name='地址')
"""在author表中"""
authors= models.ManyToManyField(to='Author', through='Author2Book', through_fields=('author', 'book'))
class Author2Book(models.Model):
authors = models.ForeignKey(to='Author', on_delete=models.CASCADE)
books = models.ForeignKey(to='Book', on_delete=models.CASCADE)
优点:还可以使用orm的方法及正反向查询,但是不能使用add set remove clear方法
多表查询(跨表查询)
orm外键字段的创建
和mysql外键关系一样的判断规律
1.一对多 外键字段建在多的一方
2.多对多 外键字段统一建在第三张关系表
3.一对一 建在任何一方都可以,但是建议建在查询频率较高的表中
注意:目前关系的判断可以采用表与表之间换位思考原则
基础表的准备
-
创建基础表(书籍表、出版社表、作者表、作者详情)
-
确定外键关系
-
orm的创建
一对一 ORM与MySQL一致 外键字段建在查询较高的一方
一对多 ORM与MySQL一致 外键建在多的一方
多对多 ORM比MySQL有更多的变化
1.外键字段可以直接建在某张表中(查询频率较高的)
内部会自动帮你创建第三张关系表
2.自己创建第三张关系表并创建外键字段
4. 针对一对多和一对一同步到表中之后自动_id的后缀,如book中的外键字段publish,会自动变成publish_id。
建表
class Book(models.Model):
name = models.CharField(max_length=32) # 书名
price = models.DecimalField(max_digits=8, decimal_places=2) # 价格
publish_date = models.DateField(auto_now_add=True) # 出版日期
maichu = models.IntegerField(2000) # 卖出数量
kucun = models.IntegerField(3000) # 库存
"""建立外键"""
# 一对多
publish = models.ForeignKey(to='Publish', on_delete=True) # publish不需要加_id,当为外键时自动会加
# 多对多
authors = models.ManyToManyField(to='Author')
class Publish(models.Model):
name = models.CharField(max_length=32) # 出版社名称
addr = models.CharField(max_length=32) # 出版社
def __str__(self):
return self.name
class Author(models.Model): #作者表
name = models.CharField(max_length=32)
age = models.IntegerField()
# 一对一
author_list = models.OneToOneField(to='AuthorList', on_delete=True)
class AuthorList(models.Model): # 作者详情表
phone = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
注意事项
1.创建一对多关系
和sql语句一样,外键建立到多的那张表上,不同的是,我们可以不讲究关联表和被关联表的建立顺序。字段类为ForeignKey
在django2.x版本以上,建立一对多关系时需要指定on_delete参数为CASCADE,不加会报错,不过也不一定就是CASCADE,可能为其他实参,这里不展开。
建立外键时,系统会自动加上_id后缀作为字段名。
2.创建多对多关系
sql中是将两张表建立好后,将外键字段创建在第三张表中,而django为我们省去了这一步骤,我们可以在多对多关系双方的一个模型表中直接建立一个虚拟外键,ManyToManyField
在底层,sql依旧创建了第三张表来存储两表的多对多关系,但是在orm操作中我们就可以将模型表中的外键当做实实在在的联系,因为在查询时,我们感受不到第三张的表的存在。
多对多关系的外键没有on_delete关键字参数。
3.创建一对多关系
一对一的字段类为OneToOneField,建议建立在查询频率高的一方。
建立一对一关系时需要指定on_delete参数,否则报错。
一对多的外键增删改查
增加
models.Book.objects.create(name='三味书屋', price=1500, publish_date='2022-1-1', maichu=3500, kucun=2000, publish_id=1)
"""第二种方式通过对象来添加"""
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.create(name='三味书屋', price=1500, publish_date='2022-1-1', maichu=3500, kucun=2000,publish=publish_obj)
删除
"""删除"""
models.Publish.objects.filter(pk=1).delete() # 级联删除时,会删除出版社里所有的书籍,所以不建议这样删除
models.Book.objects.filter(pk=1).delete() # 只删除图书即可
修改
"""修改"""
models.Book.objects.filter(pk=1).update(publish_id=3)
"""第二种方式"""
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.filter(pk=1).update(publish_id=publish_obj)
多对多的外键增删改查
增加(add)
book_obj = models.Book.objects.filter(pk=3).first()
print(book_obj.authors) # 打印结果为dj5.Author.None,说明已经跳转到第三张表了
book_obj.authors.add(1)
使用对象添加
book_obj=models.Book.objects.filter(pk=2).first()
authors_obj = models.Author.objects.filter(pk=3).filter()
authors_obj1 = models.Author.objects.filter(pk=4).filter()
book_obj.authors.add(authors_obj,authors_obj1)
修改(set)
修改作者
"""修改"""
book_obj.authors.set([2, 4]) # 将d为2的书籍绑定给主键为2,4的作者
"""使用对象修改"""
book_obj.authors.set(authors_obj)
删除(remove)
"""删除"""
book_obj.authors.remove(1,2,3) # 删除主键id为1,2,3的书籍
"""使用对象删除"""
book_obj.authors.remove(authors_obj)
清空(creal)
清空i书籍id为1 的所有作者
使用clear(清空)直接清理即可
book_obj.authors.clear()
直接清空所有id
查询之正反向
正向:外键字段在我这边,那么我查询你就是正向
反向:外键字段不在我这边,那么我查询你就是反向
图书与出版社比较,图书为多,出版社为一,那么外键字段就在我这边,使用图书查询出版社就是正向查询
book------>外键字段再我这(正向查询)-------->publish,按(外键)字段
publish----->外键字段不在我这(反向查询)------>book,按表名小写
或加上_set
出版社查询图书,出版社为一,图书为多,那么外接字段就不在我这边,使用出版社查询图书就是反向查询
子查询例题
查询书籍主键为3的出版社
res = models.Book.objects.filter(pk=3).first()
"""1.首先筛选出id=3的书籍
2.判断正反向:图书差出版社,一对多为正向
3.直接打印出结果,如果需要地址则可以使用。addr
"""
print(res.publish)
print(res.publish.addr)
查询书籍主键为3的作者
# 查询书籍主键为3的作者
res = models.Book.objects.filter(pk=3).first()
print(res.authors.all())
查询作者鲁迅的电话号码
# 3.查询作者鲁迅的电话号码
res = models.Author.objects.filter(name='鲁迅').first()
print(res.author_list.phone)
查询出版社是浓浓出版社出版的书
查询出版社是东方出版社出版的书
publish_obj = models.Publish.objects.filter(name='浓浓出版社').first()
res = publish_obj.book_set
print(res.all())# <QuerySet [<Book: Book object (3)>, <Book: Book object (4)>]>
查询作者是鲁迅写过的书
publish_obj = models.Author.objects.filter(name='鲁迅').first()
res = publish_obj.book_set.all()
print(res)
查询手机号是110的作者姓名
查询手机号是110的作者姓名
publish_obj = models.AuthorList.objects.filter(phone=110).first()
res = publish_obj.author
print(res.name)
联表查询
基于双下划线的查询
1.查询鲁迅的手机号和作者姓名
res = models.Author.objects.filter(name='鲁迅').values('author_list__phone', 'name')
print(res)
2.查询书籍主键为3的出版社名称和书的名称
查询书籍主键为3的出版社名称和书的名称
res = models.Book.objects.filter(pk=3).values('publish__name', 'name')
print(res) # <QuerySet [{'publish__name': '浓浓出版社', 'name': '三味书屋'}]>
3.查询书籍主键为1的作者姓名
查询书籍主键为3的作者姓名
res = models.Book.objects.filter(pk=3).values('authors__name')
print(res)
查询书籍主键为3的作者手机号
查询书籍主键是3的作者的手机号
res = models.Book.objects.filter(pk=3).values('authors__author_list__phone')
print(res)
聚合查询
通常聚合都是通过分组使用,只要和数据库相关的模块基本都在Django.db.models里,如果没有可能在Django.db中
在orm中,聚合函数的关键字:aggregate
from django.db.models import Avg, Sum, Max, Min, Count
res = models.Book.objects.aggregate()
查询所有书籍的金额
sql语句:
select avg(price)from book
orm语句:
from django.db.models import Avg, Sum, Max, Min, Count
res = models.Book.objects.aggregate(agv_price=Avg('price'), sum_price=Sum('price'))
分组查询
严格模式:ONLY_FULL_GROUP_BY
set global sql_mode='ONLY_FULL_GROUP_BY'
案例:
1.统计每一本书的作者个数
# 统计每个书的作者
res = models.Book.objects.annotate(authors_num=Count('authors')).values('name', 'authors__name')
print(res)
事物
事物是mysql数据库中一个重要的概念,
目的:是为了保证多sql语句执行成功,或执行失败,前后保持一致,保证数据安全
开启事物
导入模块
from django.db import transaction
try:
with transaction.atomic():
#sql语句
"""编写sql语句,同一个with语句中,都是属于一个事物,要么同时成功,要么同时失败"""
except Exception as e:
print(e)
transaction.rollback()
常见的orm字段
AutoField
int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列。
IntegerField
一个整数类型,范围在 -2147483648 to 2147483647。
CharField
字符类型,必须提供max_length参数, max_length表示字符长度。
DateField
日期字段,日期格式 YYYY-MM-DD,相当于Python中的datetime.date()
BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True
SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
BooleanField(Field)
- 布尔值类型
True/False 1/0
name varchar(1024)
content text;
TextField(Field)
- 文本类型
FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
ImageField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]
FloatField(Field)
- 浮点型
DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度
BinaryField(Field)
- 二进制类型
orm字段的参数
null
用于表示某个字段可以为空。
**unique**
如果设置为unique=True 则该字段在此表中必须是唯一的 。
**db_index**
如果db_index=True 则代表着为此字段设置索引。
**default**
为该字段设置默认值。
DateField和DateTimeField
auto_now_add
配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
auto_now
配置上auto_now=True,每次更新数据记录的时候会更新该字段。
关系字段
ForeignKey
外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 ‘一对多’中’多’的一方。
ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系。
to
设置要关联的表
related_query_name
反向查询操作时,使用的连接前缀,用于替换表名。
on_delete
当删除关联表中的数据时,当前表与其关联的行的行为。
models.CASCADE
删除关联数据,与之关联也删除
models.DO_NOTHING
删除关联数据,引发错误IntegrityError
models.PROTECT
删除关联数据,引发错误ProtectedError
models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
OneToOneField(一对一)
一对一字段。
通常一对一字段用来扩展已有字段。
一对一的关联关系多用在当一张表的不同字段查询频次差距过大的情况下,将本可以存储在一张表的字段拆开放置在两张表中,然后将两张表建立一对一的关联关系。
ManyToManyField(多对多)
用于表示多对多的关联关系。在数据库中通过第三张表来建立关联关系
自定义关系字段
class MyCharField(models.Field):
def __init__(self,max_length,*args,**kwargs):
self.max_length=max_length
super().__init__(max_length=max_length,*args,**kwargs)
def db_type(self,connection):
return 'char(%s)' % self.max_length
class User(models.Model):
name=models.CharField(max_length=32)
info=MyCharField(max_length=64)
choices参数使用
choices的使用场景
用于定义在命令行中使用的选项、参数和值。它主要用于创建命令行界面的菜单和交互式命令行工具。使用choices模块,可以定义命令行选项的名称、缩写、描述等信息,还可以定义选项的类型和默认值。可以将多个选项组织成子命令,从而实现复杂的命令行界面
例如我们的学历,小学,初中,高中,大学,硕士,博士
客户的来源:微信,QQ,广告,内推,贴吧 等等
性别等等
在数据库中我们并不能直接存入小学,大学等字符串,我们一个在数据库存入数字(占取的资源较少)使数据库中的数字与我们的学历一一对应:1:小学,2:初中等等,以上我们使用的都是choices参数
示例:
models文件
添加数据
from django.db import models
class Userinfo(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
gender_choices = (
(1, '男'),
(2, '女'),
(3, '未知')
)
score_choices = (
(A,'优秀')
(B,'良好')
(C,'中等')
(D,'及格')
)
gender = models.IntegerField(choices=gender_choices)
score = models.IntegerFidld(choices=score_choices)
"""字段存储的范围,还是取决于原来的字段"""
tests文件
import os
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django60.settings')
import django
django.setup()
from app01 import models
models.Userinfo.objects.create(username='kk', password=123, gender=1)
models.Userinfo.objects.create(username='cc', password=123, gender=2)
models.Userinfo.objects.create(username='xx', password=123, gender=3)
models.Userinfo.objects.create(username='oo', password=123, gender=4)
"""打印结果"""
res = models.Userinfo.objects.filter(pk=1).first()
# print(res) # Userinfo object (1)
print(res.gender) # 1
print(res.get_gender_display()) # 男
print(res.get_score_display()) # 优秀
固定方法:get_字段名_display(),如果超出存入的数字时,则会返回当前数字
MVC和MTV模式
MVC:
Web服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型M(model)层,控制器(C)和视图(V)三层,他们之间以一种插件式的、松耦合的方式连接在一起,模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互(页面),控制器接受用户的输入调用模型和视图完成用户的请求,其示意图如下所示:
当我们的项目非常大的时候,还会再分一层:服务层(services)
MTV
Django的MTV模式本质上和MVC是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django的MTV分别是值:
- M 代表模型(Model): 负责业务对象和数据库的关系映射(ORM)。
- T 代表模板 (Template):负责如何把页面展示给用户(html)。
- V 代表视图(View): 负责业务逻辑,并在适当时候调用Model和Template。
除了以上三层之外,还需要一个URL分发器,它的作用是将一个个URL的页面请求分发给不同的View处理,View再调用相应的Model和Template,MTV的响应模式如下所示: