模型层
sqlite3数据库对日期格式不是很敏感,处理的时候容易出错
register_time=models.DateTimeField(auto_now=True) #年月日时分秒格式
register_time=models.DateField(auto_now_add=True) #年月日格式
'''
auto_now每次操作数据时,该字段会自动将时间更新为当前时间,
auto_now_add(用的多) 在创建数据的时候自动将创建时间记录下来,之后只要不人为的修改创建时间,就一直不变
'''
测试脚本
- 当只想测试django中某一个py文件内容,可以不用写前后端交互的形式,而是直接写一个测试脚本
- pycharm提供了一个测试环境:python console
import os
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoProject.settings')
# djangoProject是一个项目名称
import django
django.setup()
from app01 import models
models.User.objects.all()
单表操作
#删除主键为5的那条数据,pk=5表示主键为5,不需要知道主键字段是什么,自动去找主键
res=models.User.objects.filter(pk=5).delete()
print(res)
'''
返回的结果是:受影响的数据行数
(1, {'app01.User': 1})
'''
#增加一条数据
#方式一
res=models.User.objects.create(username='landson',password='123',register_time=datetime.datetime.now())
#方式二
user_obj=models.User(username='landson',password='123',register_time=datetime.datetime.now())
user_obj.save()
#修改数据,get方法获取的是对象,如果那个对象不存在,会报错,还是使用filter方法
user_obj = models.User.objects.get(pk=1)
user_obj.username='egon_dsb'
user_obj.save()
取指定的字段
- values()
- values_list()
# values()可以指定要获取哪些字段
res=models.User.objects.values('username','password')
print(res)
'''
列表套字典
<QuerySet [{'username': 'egon_dsb', 'password': '123'}, {'username': 'jason', 'password': '456'}, {'username': 'tank', 'password': '789'}]>
'''
res=models.User.objects.values_list('username','password')
print(res)
#只有QuerySet对象才能通过.query查看内部封装的sql语句
print(res.query)
'''
列表套元组
<QuerySet [('egon_dsb', '123'), ('jason', '456'), ('tank', '789')]>
SELECT `app01_user`.`username`, `app01_user`.`password` FROM `app01_user`
'''
查看内部sql语句的方式
只有QuerySet对象才能通过.query查看内部封装的sql语句
去重排序计数
去重要是一模一样的数据,如果带有主键,因为主键不同,即使其他字段都相同,整体也不完全相同
res=models.User.objects.values_list('id','username','password').distinct()
print(res)
'''
<QuerySet [(1, 'egon_dsb', '123'), (2, 'jason', '456'), (3, 'tank', '789'), (4, 'tank', '789')]>
'''
res=models.User.objects.values_list('username','password').distinct()
print(res)
'''
<QuerySet [('egon_dsb', '123'), ('jason', '456'), ('tank', '789')]>
'''
#按照指定字段排序,默认升序,加-表示降序
res=models.User.objects.order_by('username')
print(res)
res=models.User.objects.order_by('-username')
print(res)
#数据必须先排序然后才能使用reverse()进行反转
res=models.User.objects.order_by('username').reverse()
#计数
res=models.User.objects.filter(password='789').count()
print(res)
#不包含
res=models.User.objects.exclude(username='page')
双下划线查询
#age大于20的
res=models.User.objects.filter(age__gt=20)
print(res)
#age大于等于18的
res = models.User.objects.filter(age__gte=18)
print(res)
#age小于20的
res = models.User.objects.filter(age__lt=20)
print(res)
#age小于等于18的
res = models.User.objects.filter(age__lte=18)
print(res)
#age等于18,或28,或35的
res = models.User.objects.filter(age__in=[18,28,35])
print(res)
#age大于等于18,且小于等于32的,包含头尾
res = models.User.objects.filter(age__range=[18,32])
print(res)
#名字中包含字符e的,默认区分大小写
res = models.User.objects.filter(username__contains='e')
print(res)
#名字中包含字符e的, 不区分大小写,i表示ignore
res = models.User.objects.filter(username__icontains='e')
#register_time在7月的
res = models.User.objects.filter(register_time__month='7')
print(res)
#register_time在2021年的
res = models.User.objects.filter(register_time__year='2021')
print(res)
一对多外键增删改查
# 增:
# 直接写真实字段
# models.Book.objects.create(book_name='三国演义',price=30,publish_id=1)
# 虚拟字段,一个对象
publish_obj=models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(book_name='三国演义',price=30,publish=publish_obj)
#改
# models.Book.objects.filter(pk=2).update(book_name='红楼梦')
#可以一下改一条记录的多个字段
models.Book.objects.filter(pk=3).update(book_name='水浒传',publish=publish_obj)
多对多外键增删改查
就是对虚拟表的增删改查
#增
#方式一
#获得id=2的book对象
book_obj=models.Book.objects.filter(pk=2).first()
#给它增加id=1和2的两个作者
book_obj.author.add(1,2)
#方式二
#获得作者对象
author_obj1=models.Author.objects.filter(pk=1).first()
author_obj2=models.Author.objects.filter(pk=2).first()
book_obj.author.add(author_obj1, author_obj2)
#删
#给它删掉id=1的那个作者
#方式一
book_obj.author.remove(1,2)
#方式二
book_obj.author.remove(author_obj1, author_obj2)
#改
#set的参数必须是可迭代对象,比如列表,先删除,后新增
#清空id=3的book的所有作者
book_obj=models.Book.objects.filter(pk=3).first()
book_obj.author.clear()
正向和反向
正向:看外键字段在哪个表
正向查询按字段,反向查询按表名小写,可能还要加_set.all()
多表查询
子查询(基于对象的跨表查询)
正向
# 一对多
book_obj = models.Book.objects.filter(pk=2).first()
# publish是book表里面的外键字段
res = book_obj.publish
print(res)
print(res.name)
'''
Publish object (2)
北方出版社
'''
# 多对多
book_obj = models.Book.objects.filter(pk=2).first()
#当结果可能有多个的时候,要加.all()
res = book_obj.author.all().first()
print(res)
print(res.name)
'''
Author object (2)
tank
'''
# 一对一
author_obj = models.Author.objects.filter(name='jason').first()
res=author_obj.author_detail
print(res)
print(res.phone)
'''
AuthorDetail object (3)
789
'''
反向
在设计表的字段时,给字段加related_name参数,反向操作时使用的字段名,用于代替原反向查询时的:''表名_set"
# 一对多
# 查询东方出版社的书
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
#当结果可能有多个的时候,要加_set.all()
res=publish_obj.book_set.all()
print(res)
'''
<QuerySet [<Book: Book object (1)>, <Book: Book object (3)>]>
'''
# 多对多
# 查询作者为Jason的书
author_obj = models.Author.objects.filter(name='jason').first()
res=author_obj.book_set.all()
print(res)
'''
<QuerySet [<Book: Book object (1)>, <Book: Book object (3)>]>
'''
# 一对一
# 查询手机号为123的作者姓名
author_detail_obj=models.AuthorDetail.objects.filter(phone=123).first()
res=author_detail_obj.author
print(res.name)
'''
Author object (1)
egon
'''
联表操作(基于双下划线的跨表查询)
# 查询作者jason的手机号
#author_detail__phone表示跨到author_detail这个表里面,取phone字段
res=models.Author.objects.filter(name='jason').values('author_detail__phone')
print(res)
'''
<QuerySet [{'author_detail__phone': '789'}]>
'''
#filter也支持跨表,author__name表示跨到author取name字段
res=models.AuthorDetail.objects.filter(author__name='jason').values('phone')
print(res)
'''
<QuerySet [{'phone': '789'}]>
'''
# 查询id=1的书的出版社名称
#正向
res=models.Book.objects.filter(pk=1).values('publish__name')
print(res)
print(res.query)
'''
<QuerySet [{'publish__name': '东方出版社'}]>
SELECT `app01_publish`.`name` FROM `app01_book` INNER JOIN `app01_publish` ON (`app01_book`.`publish_id` = `app01_publish`.`id`) WHERE `app01_book`.`id` = 1
'''
#反向
res=models.Publish.objects.filter(book__id=1).values('name')
print(res)
'''
<QuerySet [{'name': '东方出版社'}]>
'''
# 查询id=1的书的作者的手机号
#author_detail是一个外键字段,author__author_detail表示跨到了authordetail这个表
res=models.Book.objects.filter(pk=1).values('author__author_detail__phone')
print(res)
聚合查询(聚合函数的使用)
在写sql语句时,一般先分组,后聚合
这里通过aggregate来使用聚合函数
聚合函数:max,min,sum,avg,count
只要是和数据库相关的模块,基本都在django.db.models里面
from django.db.models import Avg,Sum,Max,Min,Count
avg_price=models.Book.objects.aggregate(Avg('price'))
print(avg_price)
res=models.Book.objects.aggregate(Avg('price'),Sum('price'),Max('price'),Min('price'),Count('price'))
print(res)
'''
{'price__avg': Decimal('40.000000'), 'price__sum': Decimal('160.00'), 'price__max': Decimal('55.00'), 'price__min': Decimal('30.00'), 'price__count': 4}
'''
分组查询
mysql分组查询的特点:分组之后,默认只能获取到分组的依据,组内其他字段无法直接获取了
show variables like "%mode";
严格模式:ONLY_FULL_GROUP_BY
如果出现分组查询报错的情况,可能需要去掉数据库严格模式
models.Book.objects.annotate() 默认按照models后面的东西来分组
models.Book.objects.values('publish').annotate() #按照values内部指定的字段来分组
# 统计每本书的作者人数
from django.db.models import Avg,Sum,Max,Min,Count
#以Book作为分组依据,author_count是自定义的字段名字,author__id表示author外键对应的虚拟表的author_id字段
res=models.Book.objects.annotate(author_count=Count('author__id')).values('author_count')
print(res)
print(res.query)
'''
<QuerySet [{'author_count': 1}, {'author_count': 2}, {'author_count': 2}, {'author_count': 2}]>
SELECT COUNT(`app01_book_author`.`author_id`) AS `author_count` FROM `app01_book` LEFT OUTER JOIN `app01_book_author` ON (`app01_book`.`id` = `app01_book_author`.`book_id`) GROUP BY `app01_book`.`id` ORDER BY NULL
'''
#等价语句
res=models.Book.objects.annotate(author_count=Count('author')).values('author_count')
'''
<QuerySet [{'author_count': 2}, {'author_count': 1}, {'author_count': 3}, {'author_count': 0}]>
'''
# 查询每个出版社最便宜的图书的价格
res=models.Publish.objects.annotate(min_price=Min('book__price')).values('min_price')
print(res)
# 查询作者人数大于1的图书
res=models.Book.objects.annotate(author_count=Count('author__id')).filter(author_count__gt=1).values('book_name','author_count')
print(res)
只要orm语句的结果是QuerySet对象,就可以通过 . 的方式来使用QuerySet封装的方法
F查询
获取表中某个字段的数据
在操作字符类型的数据时,F不能直接做字符串的拼接
# 查询卖出大于库存的图书
from django.db.models import F
#把每一条记录的卖出和库存进行比较
res = models.Book.objects.filter(sold_out__gt=F('inventory'))
print(res)
'''
<QuerySet [<Book: Book object (1)>, <Book: Book object (3)>]>
'''
#所有书的价格-5
models.Book.objects.update(price=F('price')-5)
# 在书名后面加文本:(热销)
from django.db.models import F,Value
from django.db.models.functions import Concat
models.Book.objects.update(book_name=Concat(F('book_name'),Value('(热销)')))
Q查询
filter方法内多个参数之间默认是and关系
from django.db.models import Q
#用Q包起来以后,用竖线| 表示或的关系,逗号表示且的关系,~Q(price__gt=40),~表示取反
res=models.Book.objects.filter(Q(price__gt=40)|Q(sold_out__gt=150))
print(res)
q=Q()
# q.connector= 'OR'
q.connector= 'or' #默认是and关系
q.children.append(('price__gt',40))
q.children.append(('sold_out__gt',150))
res = models.Book.objects.filter(q)
print(res)
django中如何开启事务
# 开启事务
from django.db import transaction
try:
with transaction.atomic():
#sql语句
# 在with代码块内的所有orm操作都属于同一个事务
except Exception as e:
print(e)
orm中常用字段类型及参数
AutoField #主键字段,primary_key=True
CharField(max_length=32,verbose_name='xxx') #varchar
DecimalField(max_digits=8, decimal_places=2)
EmailField #varchar(254)
DateField(auto_now_add=True) #date
DateTimeField #datetime
#auto_now 每次修改数据的时候都会更新当前时间
#auto_now_add
BooleanField #该字段传布尔值True/False,数据库里面存0或1
TextField #文本类型,用来存大段文本,没有字数限制
FileField #参数:upload to ='路径',给该字段传一个文件对象,会自动将文件保存到这个路径下,数据库中存的是路径,不是文件本身
#外键字段
#以下两句等价
ForeignKey(unique=True)
OneToOneField()
# db_index=True 参数表示为字段创建索引
还可以自定义字段类型
数据库查询优化
orm语句的特点:
惰性查询:如果仅仅只是写了一条orm语句,在后面根本没有用到该语句查询出来的结果,orm会自动识别这种情况,不执行该语句
#only和defer
# only表示只取括号内的字段(默认带ID字),res的结果是对象,这些对象只有book_name字段
#defer和only相反,
res = models.Book.objects.only('book_name')
for e in res:
print(e.book_name)
# 这里可以取到其他字段的值,是因为重新到数据库中进行了查询
print(e.price)
#select_related和prefetch_related
'''
该查询是inner join,select_related内部将book表和publish表连接起来,
然后一次性将大表里面的全部数据封装给查询出来的对象
之后,不管是取book表还是publish表里面的数据,都不需要去数据库里面查询了
select_related的参数是外键字段,一对一,一对多
'''
res = models.Book.objects.select_related('publish')
print( res.query)
for e in res:
print(e.publish.name)
'''
SELECT `app01_book`.`id`, `app01_book`.`book_name`, `app01_book`.`price`, `app01_book`.`publish_date`, `app01_book`.`publish_id`, `app01_book`.`inventory`, `app01_book`.`sold_out`, `app01_publish`.`id`, `app01_publish`.`name`, `app01_publish`.`addr`, `app01_publish`.`email` FROM `app01_book` INNER JOIN `app01_publish` ON (`app01_book`.`publish_id` = `app01_publish`.`id`)
'''
'''
prefetch_related内部其实是子查询
'''
# 该语句效率低
res = models.Book.objects.prefetch_related('publish')
print(res.query)
for e in res:
print(e.publish.name)
'''
SELECT `app01_book`.`id`, `app01_book`.`book_name`, `app01_book`.`price`, `app01_book`.`publish_date`, `app01_book`.`publish_id`, `app01_book`.`inventory`, `app01_book`.`sold_out` FROM `app01_book`
'''
choices参数
字段的所有取值能够列举出来的,都可以用choices参数
class Author(models.Model):
name = models.CharField(max_length=16)
age = models.IntegerField()
gender_choices=(
(1,'男'),
(2,'女'),
(3,'其他')
)
# 这个字段类型和内部元祖的第一个元素的类型相同
gender=models.IntegerField(choices=gender_choices,null=True)
author_obj=models.Author.objects.filter(pk=1).first()
# print(author_obj.gender) #这样取出来的是编号
print(author_obj.get_gender_display()) ##这样取出来的是编号对应的内容
MTV和MVC模型
多对多的三种创建方式
#全自动
class Book(models.Model):
...
author = models.ManyToManyField(to='Author')
class Author(models.Model):
...
#优点:代码不需要自己写,orm提供操作第三张关系表的方法
#缺点:第三张关系表的扩展性很差,没有办法添加字段
#纯手动
class Book(models.Model):
...
class Author(models.Model):
...
class Book2Author(models.Model):
book_id=models.ForeignKey(to='Book',on_delete=models.CASCADE)
author_id=models.ForeignKey(to='Author',on_delete=models.CASCADE)
...
#优点:第三张关系表的扩展性好
#缺点:不能使用orm提供的操作第三张关系表的方法,比如:add,remove,clear ,以及正反向查询
#半自动(一般使用这种)
class Book(models.Model):
...
# 情况一
author = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('book','author'))
class Author(models.Model):
...
# 情况二
# book = models.ManyToManyField(to='Book',through='Book2Author',through_fields=('author','book'))
'''
through_fields参数中字段先后顺序
通过第三张表查询对应的表 ,需要用到哪个字段,就把它放在前面
'''
class Book2Author(models.Model):
#会自动在字段后面加:_id
book=models.ForeignKey(to='Book',on_delete=models.CASCADE)
author=models.ForeignKey(to='Author',on_delete=models.CASCADE)
...
#可以使用orm的正反向查询,不能使用add,remove,clear,set方法
浙公网安备 33010602011771号