django之模型层ORM操作
1.orm中常用命令
# models.Book.objects
# 增
create()
# 删
delete()
# 改
update()
# 查
filter()
exclude() # 与filter()取反,除条件之外的记录。
all()
get()----># 此命令可直接获取对象本身,但如果查找的记录不存在,则会报错。
# 获取queryset中的对象
first()
last()
# 其他
values() # 获取queryset中指定字段的数据,以字典形式
values_list() # 和values一样,以元组形式
count() # 计数 不接受任何参数
order_by() # 按指定字段进行排序
exists() # 判断queryset是否有数据
reverse() # 将queryset的顺序倒置,一般在order_by后使用。
distinct() # 将queryset去重(每个字段的值都必须完全一样才会被去重)
#################数据库优化查询###############################
# only和defer(only只取部分字段,defer时only的取反)只获取部分字段与可与filter配合使用
res = models.Book.objects.only('title', 'price')
for obj in res:
print(obj.title, obj.price)
res = models.Book.objects.defer('title', 'price')
for obj in res:
print(obj.title, obj.price)
# selected_related与prefetch_related(selected_related返回的queryset的obj中有两表所有的内容,联表查询,一次sql)
res = models.Book.objects.select_related('publish')
for obj in res:
print(obj.title, obj.price, obj.publish.name)
# prefetch_related实际上进行两次sql查询,先从Book表获取,再基于Book获取的对象从publish查询(子查询)
res = models.Book.objects.prefetch_related('publish')
print(res)
# selected_related与prefetch_related在外部看来返回的queryset是一样的,可根据表的实际情况来选择优化。
2.双下划线查询
# 查询id>2的数据
res = models.Book.objects.filter(id__gt=2)
# 查询id>=2的数据
res = models.Book.objects.filter(id__gte=2)
# __lt、__lte 同理
# 查询在每个范围内的数据
res = models.Book.objects.filter(id__in=[1,2,3])
res = models.Book.objects.filter(id__in=range(1,6,2))
# 查询在100-1000价格的数据,包括10和100
res = models.Book.objects.filter(price__range=(100,1000))
# 模糊查询
res = models.Book.objects.filter(title__contains='三')
# 不区分大小写icontains
res = models.Book.objects.filter(title__icontains='三')
# 查询2020年1月出版的电影
res = models.Book.objects.filter(publish_date_year=2020,publish_date_month=1)
3.外键字段的增删改查
# 一对多 在book表增加一条数据
# 方式1
models.Book.objects.create(title='三国演义',price=333,publish_id=1)
# 方式2:通过增加或查找先得到一个obj对象
publish_obj = models.Publish.objects.create(name='东方出版社',addr='东京')
models.Book.objects.create(title='三国演义',price=333,publish=publish_obj)
# 对book表中的外键字段进行修改
models.Book.objects.filter(pk=1).update(publish_id=2)
# 将id为1的book出版社改为东方出版社
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
models.Book.objects.filter(pk=1).update(publish=publish_obj)
在为多对多创建关联记录时,由于django的orm是自动为我们创建的第三张表,我们的models类里也不存在这个类。所以我们不能直接以第三张表为对象去添加book和author的关联记录。
只能通过book _obj和author_obj下的方法来添加关联记录。
# 多对多 add、remove、set、clear
# 1.给书籍绑定作者关系
book_obj = models.Book.objects.filter(pk=1).first()
res = book_obj.author.all() # 可获得book_obj这条记录对应的作者对象。本质上在book_obj.author时进行了与book_author的连表操作,并获取到了结果。
# 为书籍与作者id为2和3进行绑定。
book_obj.author.append(2,3)
# 为某一书籍与作者david4和david3进行绑定
book_obj = models.Book.objects.filter(pk=1).first()
res = book_obj.author.all()
print(res)
print(res.query)
author_obj1 = models.Author.objects.filter(name='David4').first()
author_obj2 = models.Author.objects.filter(name='David3').first()
book_obj.author.add(author_obj1,author_obj2)
#remove方法与add方法同理
#set方法:接收一个元组或列表,有则删除、无则添加。
book_obj.author.set((1,))
# clear方法无需参数
book_obj.author.clear()
4.跨表查询
跨表查询分为子查询和联表查询。
子查询:将一张表的查询结果当做另外一张表的查询条件。
联表查询:先将两张有关联的表合并,然后再进行查询。
子查询和联表查询都有正向查询和反向查询的概念。
正向查询:跨表查询时外键字段在当前数据对象中则是正向,查询时按外键字段.
反向查询:跨表查询时外键字段不在当前数据对象中则是反向,查询时按表名小写_set.
注意:!!!外键字段在不在当前数据对象中以models中为准!!!一对一时无论正反都不需要加_set。
# 基于对象的跨表查询(子查询)
# 1.查询书籍id为1的出版社名称
book_obj = models.Book.objects.filter(pk=1).first()
# res = models.Publish.objects.filter(pk=book_obj.publish_id)
# print(res.first().name)
res = book_obj.publish.name
print(res)
#2.查询书籍id为2的所有作者
book_obj = models.Book.objects.filter(pk=2).first()
res = book_obj.author.all()
print(res)
#3.查询作者id为1的电话号码
author_obj = models.Author.objects.filter(id=1).first()
res = author_obj.author_detail
print(res.phone)
#4.查询出版社名称为东方出版社的书籍
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
res = publish_obj.book_set.all()
print(res)
#5.查询作者为david1的书籍
author_obj = models.Author.objects.filter(name='David1').first()
res = author_obj.book_set.all()
print(res)
#6.查询手机号为110的作者
author_detail_obj = models.AuthorDetails.objects.filter(phone=110).first()
res = author_detail_obj.author
print(res)
# 基于双下划线跨表查询(联表查询)
# django会将__自动识别为联表
# 1、查询书籍id为1的出版社名称
# 正向查询
res = models.Book.objects.filter(id=1).values('publish__name')
print(res)
# 反向查询
res = models.Publish.objects.filter(book__id=1)
print(res)
# 2.查询书籍id为1的作者姓名和年龄
# 正向查询
res = models.Book.objects.filter(id=1).values('author__name','author__age')
# 反向查询
res = models.Author.objects.filter(book__id=1)
print(res)
# 3.查询作者是David1的年龄和手机号
# 正向查询
res = models.Author.objects.filter(name='David1').values('name','age','author_detail__phone')
print(res)
# 反向查询
res = models.AuthorDetails.objects.filter(author__name='David1')
print(res.first().phone)
# 4.查询书籍id为1的作者的手机号
# 正向查询
res = models.Book.objects.filter(pk=1).values('author__author_detail__phone')
print(res)
# 反向查询
res = models.AuthorDetails.objects.filter(author__book__id=1)
print(res.first().phone)
#!!!!! 只要表之间有关系,就可以一直用__进行联表操作。!!!!!!!
5.聚合查询
关键字aggregate
from django.db.models import Max, Min, Avg, Count, Sum
# 查询所有书的平均价格
res = models.Book.objects.aggregate(avg_num=Avg('price'))
print(res)
# 查询最贵的书的价格
res = models.Book.objects.aggregate(max_price=Max('price'))
print(res)
res = models.Book.objects.aggregate(Avg("price"),Max("price"), Min("price"),Count("pk"),Sum('price'))
print(res)
6.分组查询
关键字annotate
from django.db.models import Max, Min, Avg, Count, Sum
# 1.统计每一本数的作者的个数
res = models.Book.objects.all().annotate(author_num=Count('author')).values('title','author_num')
print(res)
# 2.统计出每个出版社卖的最便宜的书的价格
res = models.Publish.objects.all().annotate(min_price=Min('book__price')).values('name','min_price')
print(res)
#3.统计出不止一个作者的书
res =models.Book.objects.annotate(author_num=Count('author__id')).filter(author_num__gt=1)
print(res)
# 4.查询各个作者出的书总价格
res = models.Author.objects.annotate(total_price=Sum('book__price')).values('name','total_price')
print(res)
# 按照表中的一个字段分组
res = models.Book.objects.values('price').annotate()
7.F查询和Q查询
from django.db.models import F,Q
# F可以获取到每条记录对应的字段值
# 1.查询库存数大于销售数的书籍
res = models.Book.objects.filter(storage__gt=F('sales'))
print(res)
# 2. 为所有书籍价格增加100
res = models.Book.objects.update(price=F('price') + 100)
print(res)
# Q的用法
q = Q()
q.connector = 'or' # q对象默认也是and关系 可以通过connector改变or
q.children.append(('title','python入门'))
q.children.append(('price',1000))
res = models.Book.objects.filter(q)
print(res)
8.models中常用字段
AutoField(Field)
- int自增列,必须填入参数 primary_key=True
BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True
注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models
class UserInfo(models.Model):
# 自动创建一个列名为id的且为自增的整数列
username = models.CharField(max_length=32)
class Group(models.Model):
# 自定义自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647
BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
BooleanField(Field)
- 布尔值类型
NullBooleanField(Field):
- 可以为空的布尔值
CharField(Field)
- 字符类型
- 必须提供max_length参数, max_length表示字符长度
TextField(Field)
- 文本类型
EmailField(CharField):
- 字符串类型,Django Admin以及ModelForm中提供验证机制
IPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
GenericIPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
- 参数:
protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"
URLField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证 URL
SlugField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
CommaSeparatedIntegerField(CharField)
- 字符串类型,格式必须为逗号分割的数字
UUIDField(Field)
- 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
FilePathField(Field)
- 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
- 参数:
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
ImageField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)
DateTimeField(DateField)
- 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
DateField(DateTimeCheckMixin, Field)
- 日期格式 YYYY-MM-DD
TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]
DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
FloatField(Field)
- 浮点型
DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度
BinaryField(Field)
- 二进制类型
常用和非常用字段合集
对应关系:
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',
ORM字段与数据库实际字段的对应关系
null 数据库中字段是否可以为空
db_column 数据库中字段的列名
db_tablespace
default 数据库中字段的默认值
primary_key 数据库中字段是否为主键
db_index 数据库中字段是否可以建立索引
unique 数据库中字段是否可以建立唯一索引
unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引
unique_for_month 数据库中字段【月】部分是否可以建立唯一索引
unique_for_year 数据库中字段【年】部分是否可以建立唯一索引
verbose_name Admin中显示的字段名称
blank Admin中是否允许用户输入为空
editable Admin中是否可以编辑
help_text Admin中该字段的提示信息
choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)
error_messages 自定义错误信息(字典类型),从而定制想要显示的错误信息;
字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
如:{'null': "不能为空.", 'invalid': '格式错误'}
validators 自定义错误验证(列表类型),从而定制想要的验证规则
from django.core.validators import RegexValidator
from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
如:
test = models.CharField(
max_length=32,
error_messages={
'c1': '优先错信息1',
'c2': '优先错信息2',
'c3': '优先错信息3',
},
validators=[
RegexValidator(regex='root_\d+', message='错误了', code='c1'),
RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'),
EmailValidator(message='又错误了', code='c3'), ]
)
# chocies参数
choices参数
用户的性别
学历
婚否
在职状态
客户来源
能够被枚举的
class Userinfo(models.Model):
username = models.CharField(max_length=32)
gender_choices = (
(1,'男'),
(2,'女'),
(3,'其他'),
)
gender = models.IntegerField(choices=gender_choices)
# 该字段还是存数字 并且可以匹配关系之外的数字
record_choices = (('checked', "已签到"),
('vacate', "请假"),
('late', "迟到"),
('noshow', "缺勤"),
('leave_early', "早退"),
)
record = models.CharField("上课纪录", choices=record_choices, default="checked", max_length=64)
user_obj = models.Userinfo.objects.get(pk=1)
print(user_obj.username)
print(user_obj.gender)
# 针对choices参数字段 取值的时候 get_xxx_display()
print(user_obj.get_gender_display())
# 针对没有注释信息的数据 get_xxx_display()获取到的还是数字本身
user_obj = models.Userinfo.objects.get(pk=4)
print(user_obj.gender)
print(user_obj.get_gender_display())
9.自定义char字段
from django.db.models import Field
class RealCharField(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 Movie(models.Model):
textField = RealCharField(max_length=64)
10.orm中开启事务
事务的四大特性:ACID
原子性、一致性、隔离性、持久性
from django.db import transaction
from django.db.models import F
with transaction.atomic():
models.Book.objects.filter(pk=1).update(storage=F('storage') - 10,sales=F('sales')+10)
11.MTV与MVC模型
MTV django号称是MTV框架
M:models
T:templates
V:views
MVC
M:models
V:views
C:contronnar 控制器(路由分发 urls.py)
本质:MTV本质也是MVC
12.django多对多表的三种创建方式
# 1.全自动(较为常用)
class Book(models.Model):
title = models.CharField(max_length=32)
authors = models.ManyToManyField(to='Author')
# orm就会自动帮你创建第三张表
class Author(models.Model):
name = models.CharField(max_length=32)
"""
好处:第三张表自动创建
不足之处:第三张表无法扩展额外的字段
"""
# 2.纯手动(了解)
class Book(models.Model):
title = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
class Book2Author(models.Model):
book = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')
create_time = models.DateField(auto_now_add=True)
"""
好处在于第三表可以扩展额外的字段
不足之处:orm查询的时候会带来不便
"""
# 3.半自动(推荐)
class Book(models.Model):
title = models.CharField(max_length=32)
authors = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('book','author'))
class Author(models.Model):
name = models.CharField(max_length=32)
books = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('author','book'))
class Book2Author(models.Model):
book = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')
create_time = models.DateField(auto_now_add=True)
"""
好处在于第三步可以扩展任意的额外字段 还可以利用orm 正反向查询
不足之处:无法利用
add
set
remove
clear
虽然无法使用了 但是你还可以自己直接操作第三表
"""
添加以下设置到settings.py中将自动打印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',
},
}
}
配置测试环境
tests.py
-------------------
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")
import django
django.setup()