一、ORM简介
对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。
简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。
ORM在业务逻辑层和数据库层之间充当了桥梁的作用。
对应关系:
-
类 --> 表
-
对象 --> 数据行(记录)
-
属性 --> 字段
使用ORM
-
在settings中配置数据库的连接sql:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } }
-
在app下的models.py中写类:
class User(models.Model): # 继承models.Model username = models.CharField(max_length=32) # varchar(32) password = models.CharField(max_length=32) # varchar(32) #在templates文件中就生成了一个db.sqlite3
-
然后点击最右边的database
-
双击文件db.sqlite3
-
点击设置
-
-
第一次要下载插件,直接download --> 下载完后
-
-
数据库迁移的命令 (在Teminal下输入下面的命令)
python manage.py makemigrations #检测所有app下的models。py文件有什么变化,将变更记录制作成迁移文件 python manage.py migrate # 数据库的迁移 将变更的记录同步到数据库
-
-
刷新以后就
-
然后双击app01_user,添加数据,添加完后点击DB哪里保存
在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',
},
}
}
ORM测试
from app01 import models
res = models.User.objects.all() #获取表中所有的数据
for i in res:
'''
User object abc 123 <class 'str'>
User object cba 123 <class 'str'>
'''
print(i,i.username,i.password,type(i.username))
ret = models.User.objects.get(username="abc",password="123") #获取一条数据 User object
# ret1 = models.User.objects.get(password="123") # 获取一条数据(获得不到,或者获得多条数据,就会报错)
ret = models.User.objects.filter(password="123") #获取多条数据 ,返回一个列表,如果获取不到返回一个空的列表
print(ret[0].username,ret[1].username,ret) #abc cba <QuerySet [<User: User object>, <User: User object>]>
使用mysql数据库的流程:
-
创建一个mysql数据库
create database login;
-
配置settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'login', # 数据库名称
'HOST': "127.0.0.1",
'PORT': 3306,
'USER': 'root',
'PASSWORD': 'mysql',
}
}
-
使用pymysql的模块连接mysql数据库
习惯的写到与项目同名的文件夹下的’init.py‘中
import pymysql pymysql.install_as_MySQLdb()
-
在app下的models.py写入类
class User(models.Model): #User表名 继承models.Model username = models.CharField(max_length=32) # varchar(32) password = models.CharField(max_length=32) # varchar(32)
-
执行数据库迁移的命令
python manage.py makemigrations #检测所有app下的models。py文件有什么变化,将变更记录制作成迁移文件 python manage.py migrate # 数据库的迁移 将变更的记录同步到数据库
我们生成文件成功后:就点击右边的Datebases ---> 在点击左上角的+号 ----- 选择Data Source --->在选择MYSQL进行设置----》
---》最后在 test Connection测试一下连接,
如果报错这个就是没有在mysql里面设置时区:Server returns invalid timezone. Go to 'Advanced' tab and set 'serverTimezone' property manually.
只需要输入二条命令:show variables like'%time_zone';
set global time_zone = '+8:00';
或者:
在Advanced中的 --> serverTimezone设置哦Hongkong
二、Model
Model常用的字段
AutoField 自增字段 primary_key=True 变成主键。
IntegerField 整形 10位 -2147483648 ~ 2147483647。
CharField 字符串 varchar max_length(长度) 必填参数
DateField 日期类型,日期格式为YYYY-MM-DD(年-月-日)
DatetimeField 日期时间字段,格式为YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] (年-月-日-时-分-秒)
#auto_now:每次修改时修改为当前日期时间。
#auto_now_add:新创建对象时自动添加当前日期时间。
#auto_now和auto_now_add和default参数是互斥的,不能同时设置。
BooleanFiled 布尔类型
TextField 大文本
FloatField 浮点型
DecimalField 10进制小数
#max_digits,小数总长度
#decimal_places,小数位长度
# 更多参数:https://www.cnblogs.com/maple-shaw/articles/9323320.html
实例
class Person(models.Model):
pid = models.AutoField(primary_key=True) #设置主键, 如果没有设置,会自动添加
name = models.CharField(max_length=32) # varchar(32)
age = models.IntegerField() #整形
birth = models.DateTimeField(auto_now_add=True) #新创建对象时自动添加当前时间
birth = models.DateTimeField(auto_now=True) #修改对象的时候改变时间
自定义的字段
-
自定义字段
class MyCharField(models.Field):
"""
自定义的char类型的字段类
"""
def __init__(self, max_length, *args, **kwargs):
self.max_length = max_length
super(MyCharField, self).__init__(max_length=max_length, *args, **kwargs)
def db_type(self, connection):
"""
限定生成数据库表的字段类型为char,长度为max_length指定的值
"""
return 'char(%s)' % self.max_length #char(11)
-
输入数据库迁移命令
python manage.py makemigrations
#因为是添加字段,所以会出现以下图片的内容:
给前面字段添加默认参数,或者添加默认值(default)
-
使用
class Person(models.Model):
#添加自定义的字段
cname = MyCharField(max_length=11) #char(11)
model字段参数
添加或删除字段,记得都要迁移一下数据库
null=True 该字段在数据库可以为空
blank=True 允许用户输入为空(True就是必须输入)
db_column 数据库中字段的列名(别名)
defalut 默认值 (在admin中显示默认)
db_index 建立索引
unique=True 唯一约束(表示不能为重复的,如果有重复的就会报错) 如果新创建一个数据库和unique=True一起迁移,那一定不能有重复的元素,不然就删除了新迁移的数据,在重新迁移
verbose_name显示的字段名 (在admin中显示)
choices 用户选择的参数 models.BooleanField(choices=((True,"男"),(False,"女"))) 下拉选择框
更多更详细的数据:https://www.cnblogs.com/maple-shaw/articles/9323320.html
例:
class Person(models.Model):
pid = models.AutoField(primary_key=True) #设置主键, 如果没有设置,会自动添加
name = models.CharField(verbose_name="姓名",db_column='nick',max_length=32, blank=True,unique=True) # varchar(32)
age = models.IntegerField(null=True,default=18) #整形
birth = models.DateTimeField(auto_now_add=True) #新创建对象时自动添加当前时间
# birth = models.DateTimeField(auto_now=True) #修改对象的时候改变时间
sex = models.BooleanField(choices=((True,"男"),(False,"女"))) #0为True 1为False
#添加自定义的字段
cname = MyCharField(max_length=11) #char(11)
model表的参数
修改表的(要迁移数据库,不要忘记)
class Person(models.Model):
pid = models.AutoField(primary_key=True) #设置主键, 如果没有设置,会自动添加
name = models.CharField(verbose_name="姓名",db_column='nick',max_length=32, blank=True,unique=True) # varchar(32)
age = models.IntegerField(null=True,default=18) #整形
birth = models.DateTimeField(auto_now_add=True) #新创建对象时自动添加当前时间
# birth = models.DateTimeField(auto_now=True) #修改对象的时候改变时间
sex = models.BooleanField(choices=((True,"男"),(False,"女"))) #0为True 1为False
#添加自定义的字段
cname = MyCharField(max_length=11) #char(11)
class Meta:
# 数据库中生成的表名称 默认 app名称 + 下划线 + 类名(右边这个Database里面的数据表)
db_table = "Person"
# admin中显示的表名称
verbose_name = '个人信息'
# verbose_name加s admin中显示
verbose_name_plural = '所有用户信息'
# 联合索引 会和上面的unique=True冲突,所以一定要看好
index_together = [
("name", "age"), # 应为两个存在的字段
]
# 联合唯一索引 会和上面的unique=True冲突,所以一定要看好
unique_together = (("name", "age"),) # 应为两个存在的字段
三、ORM单表增删改查
增
-
save()
-
create()
-
bulk_create()
-
update_or_create()
# 创建记录方式1
student_obj = models.Student(
name='老王',
age=19,
)
student_obj.save() # 刷到表里
# 创建记录方式2
new_obj = models.Student.objects.create(name='小李', age=15) # Student object -- models对象
print(new_obj.name)
print(new_obj.age)
# 创建方式3
批量创建
objs = []
for i in range(20):
obj = models.Student(
name='xiangxi%s' % i,
age=10 + i,
)
objs.append(obj)
models.Student.objects.bulk_create(objs)
# 创建方法4
update_or_create() 有就更新 没有就创建、
models.Student.objects.update_or_create(
name='老王2',
defaults={
'age':48,
}
)
删
-
delete()
# 删除 delete queryset 和 model 都可以调用
models.Student.objects.get(id=2).delete() # model对象调用delete方法
models.Student.objects.filter(name='小小').delete()
models.Student.objects.all().delete() # 删除所有
改
-
update()
# 更新 update() 方法 model对象不能调用更新方法 只能QuerySet调用
models.Student.objects.filter(name='老王').update(age=29)
查
-
.all() 查询所有,返回querySet集合(类似于列表)
-
.filter() 查不到返回一个空集合不报错,返回QuerySet
-
.get() 有且只有1个结果(没有或者多于一个结果报错),返回model对象
# 简单查询
all_obj = models.Student.objects.all() # 查询所有
print(all_obj) # <QuerySet [<Student: Student object>, <Student: Student object>]>--类似于列表 -- QuerySet集合
for i in all_obj:
print(i.name)
# 条件查询 .filter() 返回的也是QuerySet集合,查不到返回一个空集合,不报错
objs = models.Student.objects.filter(id=2)
objs = models.Student.objects.filter(name='老王')
print(objs)
# 条件查询 get() 返回的是model对象,而且get方法有且必须有1个结果
obj = models.Student.objects.get(id=1)
print(obj) # 老王
obj = models.Student.objects.get(name='小小') # 报错1,查询结果多于1个
get() returned more than one Student -- it returned 2!
obj = models.Student.objects.get(name='大大') # 报错2 没有查询到任何内容
# Student matching query does not exist.
print(obj)
查询接口
<1> all(): 查询所有结果,结果是queryset类型
<2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象,结果也是queryset类型 Book.objects.filter(title='linux',price=100) #里面的多个条件用逗号分开,并且这几个条件必须都成立,是and的关系,or关系的我们后面再学,直接在这里写是搞不定or的
models.Student.objects.filter(id=7, name='xiangxi0').update(
name='小小妹妹',
age=18,
)
<3> get(**kwargs): 返回与所给筛选条件相匹配的对象,不是queryset类型,是行记录对象,返回结果有且只有一个,
如果符合筛选条件的对象超过一个或者没有都会抛出错误。捕获异常try。 Book.objects.get(id=1)
<4> exclude(**kwargs): 排除的意思,它包含了与所给筛选条件不匹配的对象,没有不等于的操作昂,用这个exclude,返回值是queryset类型 Book.objects.exclude(id=6),返回id不等于6的所有的对象,或者在queryset基础上调用,Book.objects.all().exclude(id=6)
<5> order_by(*field): queryset类型的数据来调用,对查询结果排序,默认是按照id来升序排列的,返回值还是queryset类型
models.Book.objects.all().order_by('price','id') #直接写price,默认是按照price升序排列,按照字段降序排列,就写个负号就行了order_by('-price'),order_by('price','id')是多条件排序,按照price进行升序,price相同的数据,按照id进行升序
<6> reverse(): queryset类型的数据来调用,对查询结果反向排序,返回值还是queryset类型
<7> count(): queryset类型的数据来调用,返回数据库中匹配查询(QuerySet)的对象数量。
<8> first(): queryset类型的数据来调用,返回第一条记录 Book.objects.all()[0] = Book.objects.all().first(),得到的都是model对象,不是queryset
<9> last(): queryset类型的数据来调用,返回最后一条记录
<10> exists(): queryset类型的数据来调用,如果QuerySet包含数据,就返回True,否则返回False
空的queryset类型数据也有布尔值True和False,但是一般不用它来判断数据库里面是不是有数据,如果有大量的数据,你用它来判断,那么就需要查询出所有的数据,效率太差了,用count或者exits
例:all_books = models.Book.objects.all().exists() #翻译成的sql是SELECT (1) AS `a` FROM `app01_book` LIMIT 1,就是通过limit 1,取一条来看看是不是有数据
<11> values(*field): 用的比较多,queryset类型的数据来调用,返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列
model的实例化对象,而是一个可迭代的字典序列,只要是返回的queryset类型,就可以继续链式调用queryset类型的其他的查找方法,其他方法也是一样的。
<12> values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
<13> distinct(): values和values_list得到的queryset类型的数据来调用,从返回结果中剔除重复纪录
必会必知13条
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")
import django
django.setup()
from app01 import models
# all 查询所有的数据 QuerySet 对象列表 【对象,对象】
ret = models.Person.objects.all() # 光查询这个的话是先调用的__repr__
# get 获取一个有且唯一的数据 对象 没有或者多个就报错(其实就是Id为1的)
ret = models.Person.objects.get(pk=1) # 有具体查询的就是直接调用的__str__
# filter 获取满足条件的数据 对象列表 【对象,对象】 没有会返回一个空的列表
ret = models.Person.objects.filter(pk=1)
# order_by 排序 默认升序 如果字段前加-号,就变成降序排序 【可以多字段排序】
ret = models.Person.objects.all().order_by("-age", "pid")
# reverse 对已经排序的对象列表进行翻转
ret = models.Person.objects.all().order_by("pid").reverse()
# values 不指定字段 获取数据所有的字段和值 QuerySet [{},{},{}] 字典
# 指定字段 获取数据指定的字段名和值 QuerySet [{"pid":5,"name":111}] 字典
ret = models.Person.objects.all().values("pid", "name")
# values_list 不指定字段 获取数据所有的值 QuerySet [(),()] 元组
# 指定字段 获取数据指定字段的值 QuerySet [('111', 5), ('1111', 3)]
ret = models.Person.objects.all().values_list("name", "pid")
# distinct 去重(去除age一样的)
ret = models.Person.objects.values("age").distinct()
# count 计数
ret = models.Person.objects.all().count()
# first 获取第一个元素
ret = models.Person.objects.all().first()
# last 获取最后一个元素
ret = models.Person.objects.all().last()
# exists 判断是否有结果 True,False
ret = models.Person.objects.filter(pk=2).exists()
print(ret)
"""
返回对象列表
all
filter
exclude 取反
order_by
reverse
values [{},{}]
values_list [(),()]
distinct
返回对象
get
first
last
返回数字
count
返回布尔值
exists
"""
基于双下划线的模糊查询
-
in 之中
-
gt 大于等于
-
lt 小于等于
-
range 区间
-
contains / icontains 包含
-
startswidth 开头
-
year 日期 (查找日期的时候要注意时区的问题,在settings.py中改USE_TZ为FALSE)
Book.objects.filter(price__in=[100,200,300]) #price值等于这三个里面的任意一个的对象
Book.objects.filter(price__gt=100) #大于,大于等于是price__gte=100,别写price>100,这种参数不支持
Book.objects.filter(price__lt=100)
Book.objects.filter(price__range=[100,200]) #sql的between and,大于等于100,小于等于200
Book.objects.filter(title__contains="python") #title值中包含python的
Book.objects.filter(title__icontains="python") #不区分大小写
Book.objects.filter(title__startswith="py") #以什么开头,istartswith 不区分大小写
Book.objects.filter(pub_date__year=2012)
# 日期查询
# all_books = models.Book.objects.filter(pub_date__year=2012) #找2012年的所有书籍
# all_books = models.Book.objects.filter(pub_date__year__gt=2012)#找大于2012年的所有书籍
all_books = models.Book.objects.filter(pub_date__year=2019,pub_date__month=2)#找2019年月份的所有书籍,如果明明有结果,你却查不出结果,是因为mysql数据库的时区和咱们django的时区不同导致的,了解一下就行了,你需要做的就是将django中的settings配置文件里面的USE_TZ = True改为False,就可以查到结果了,以后这个值就改为False,而且就是因为咱们用的mysql数据库才会有这个问题,其他数据库没有这个问题。
单表的双下划线
ret = models.Person.objects.filter(pid__lt=5) # 字段__条件 = less than pid小于5的
ret = models.Person.objects.filter(pid__gt=5) # 字段__条件 = less than pid大于5的
ret = models.Person.objects.filter(pid__lte=5) # 字段__条件 = less than equal pid小于等于5的
ret = models.Person.objects.filter(pid__gte=5) # 字段__条件 = less than pid equal 大于等于5的
ret = models.Person.objects.filter(pid__range=[1, 6]) # 范围 在1到6之间的 包括1和6
ret = models.Person.objects.filter(pid__in=[1, 3, 11]) # 成员判断 pid中有没有 为1,3,11的
ret = models.Person.objects.filter(name__contains="李小小") # 表示name里面有没有包含"李小小"的
ret = models.Person.objects.filter(name__icontains="a") # 表示name里面有没有包含"a/A"的 加i了忽略大小写
ret = models.Person.objects.filter(name__startswith="李") # 表示name以“李”开头的
ret = models.Person.objects.filter(name__istartswith="a") # 表示name以“a/A”开头的,忽略大小写
ret = models.Person.objects.filter(name__endswith="小") # 表示name以“小”结尾的
ret = models.Person.objects.filter(name__iendswith="a") # 表示name以“a/A”结尾的,忽略大小写
ret = models.Person.objects.filter(birth__year="2020") # 表示birth里面年为2020的
# ret = models.Person.objects.filter(birth__month="2") # 表示birth里面月为2的
# ret = models.Person.objects.filter(birth__day="21") # 表示birth里面日期为21的
ret = models.Person.objects.filter(birth__contains="-10-") # 表示月份为10的
ret = models.Person.objects.filter(name__isnull=False) # 输出name字段不为空的
三种表关系:一对一、一对多、多对多。
一、创建模型
-
OneToOneField() 一对一
-
ForeignKey() 一对多 多对一
-
ManyToManyField() 多对多
#添加外键 一对多 多对一
class Book(models.Model):
name = models.CharField(max_length=32)
# 添加外键ForeignKey
publishers = models.ForeignKey(Publisher, on_delete=models.CASCADE) #默认是级联删除
"""
on_delete=None, # 删除关联表中的数据时,当前表与其关联的field的行为
on_delete=models.CASCADE, # 删除关联数据,与之关联也删除
on_delete=models.DO_NOTHING, # 删除关联数据,什么也不做
on_delete=models.PROTECT, # 删除关联数据,引发错误ProtectedError
on_delete=models.SET_NULL, # 删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空,一对一同理)
on_delete=models.SET_DEFAULT, # 删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值,一对一同理)
on_delete=models.SET, # 删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
"""
class Author(models.Model):
name=models.CharField( max_length=32)
age=models.IntegerField()
authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE)
# 一对一到详细信息表
# foreign+unique 不写tofield会自动关联主键
# on_delete = models.CASCADE 被关联的键删除后跟它有关联的也都删除了
# on_delete = models.SET_NULL 被关联的删除后,关联的变为空
class AuthorDetail(models.Model):#不常用的放到这个表里面
birthday=models.DateField()
# telephone=models.BigIntegerField()
telephone=models.CharField(max_length=32)
addr=models.CharField( max_length=64)
# 出版社表和书籍表是一对多的关系Forirgn
class Publish(models.Model):
name=models.CharField( max_length=32)
city=models.CharField( max_length=32)
email=models.EmailField() # 实际上是CharField --- 但是后期可以校验格式xx@xx.
class Book(models.Model):
title = models.CharField( max_length=32)
publishDate=models.DateField()
price=models.DecimalField(max_digits=5,decimal_places=2)
# 与Publish建立一对多的关系,外键字段建立在多的一方,字段publish如果是外键字段,那么它自动是int类型
publishs=models.ForeignKey(to="Publish",to_field="id",on_delete=models.CASCADE)
# foreign key(publish) references publish(id)
authors=models.ManyToManyField(to='Author',) # author不是表的字段,知识类的一个属性,用来操作下边的第三张表,写在被关联的那张表里也可以
# manytomany会自动生成下边这样一张表,如果还有其他字段可以手动创建这张表
# class BookToAuthor(models.Model):
# book_id = models.ForeignKey(to="Book",to_field="id",on_delete=models.CASCADE)
# author_id = models.ForeignKey(to="Author",to_field="id",on_delete=models.CASCADE)
Django–admin
-
创建一个超级用户
python manage.py createsuperuser
Username (leave blank to use 'bee'): root #输入用户名,默认为bee
Email address: #电子邮箱
Password: #输入root1234
Password (again): #root1234
Superuser created successfully.
2. 在app下的admin.py中注册model
from django.contrib import admin
from app01 import models
# Register your models here.
admin.site.register(models.Person) # 操作的表名
admin.site.register(models.User) # 操作的表名
-
登录网页http://127.0.0.1:8000/admin/进行操作
登录之后就进入admin页面,就可以操作数据库了
输入刚以输入的用户root和密码root1234
views.py通过models操作
增
from django.shortcuts import render,HttpResponse,redirect
from app01 import models
# Create your views here.
def query(request):
# 增
# 一对一增加
new_author_detail = models.AuthorDetail.objects.create(
birthday='1979-08-08',
telephone='13456789876',
addr='黑龙江哈尔滨',
)
# 方式1
models.Author.objects.create(
name='卡特',
age=18,
authorDetail=new_author_detail,
)
# 方式2 常用
obj = models.AuthorDetail.objects.filter(addr='陕西临汾').first()
models.Author.objects.create(
name='卡特',
age=18,
authorDetail_id=obj.id,
)
# 一对多
# 方式1
models.Book.objects.create(
title='祖安快嘴练习生',
publishDate='2019-08-09',
price=3,
publishs=models.Publish.objects.get(id=1),
)
# 方式2 常用
models.Book.objects.create(
title='祖安快嘴练习生2',
publishDate='2019-08-09',
price=3,
publishs_id=models.Publish.objects.get(id=1).id,
)
# 多对多
# 方式1 常用
book_obj = models.Book.objects.get(id=1)
book_obj.authors.add(*[1, 2])
# 方式2
author1 = models.Author.objects.get(id=1)
author2 = models.Author.objects.get(id=3)
book_obj_ = models.Book.objects.get(id=5)
book_obj_.authors.add(*[author1, author2])
return HttpResponse('ok')
删
# 删除
# 一对一和多对一的情况和单表删除的操作相同
# 一对一
# author也会跟着级联删除
models.AuthorDetail.objects.get(id=2).delete()
# 删除author对AuthorDetail没有影响
models.Author.objects.get(id=1).delete()
# 一对多
# 删出版社,这个出版社下边的书全部被级联删除
models.Publish.objects.get(id=1).delete()
# 删除某本书,不会对出版社有影响
models.Book.objects.get(id=2).delete()
# 多对多
# 删除id为3的那本书的作者里边id为4和5的作者,就是id=3的书没有作者4和5了,书还在,作者也在,知识删掉自动生成的 book_author那张表里的记录
book_obj_1 = models.Book.objects.get(id=3)
book_obj_1.authors.remove(*[4, 5])
# 方式2就是找到作者对象,放进列表去
book_obj_1.authors.clear() # 把id=3的对应的作者记录全删了
book_obj_1.authors.set(['5', '6']) # 先清空,再添加个5和6
改
# 更新
# 一对一
# 把id=2的作者新更改
models.Author.objects.filter(id=2).update(
name='厄加特',
age=18,
# authorDetail=models.AuthorDetail.objects.get(id=5),
authorDetail_id=5, # 两种方式
)
# 一对多
# 把id=4的书的出版社改为3的出版社
models.Book.objects.filter(pk=4).update( # pk表的主键字段
title='B哥往事',
# publishs=models.Publish.objects.get(id=3),
publishs_id=3, # 两种方式
)
# 不会做级联更新,orm没有级联更新,可以在mysql中级联更新,自查
查
def books_list(request):
# 获取数据库里面的内容
all_books = models.Book.objects.all()
for i in all_books:
print(i.publishers.name, i.publishers.id,i.publishers_id) # 书籍所关联的出版社对象nam
return render(request, "books_list.html", {"all_books": all_books})
查询
基于对象的跨表查询-类似于子查询
正向查询和反向查询
通过设立约束关系的表想被约束关系的表查询是正向查询–从大腿想孩子查
反过来查就是反向查询
正向查询:关系属性(字段)卸载哪个类(表)里边,从当前类(表)的数据去查询它的关联类(表)的数据。
反向查询:反之。
# 一对一
# 正向查询
# 查询盖伦的住址
author_obj = models.Author.objects.filter(name='盖伦').first()
print(author_obj.authDetail.addr) # 盖伦的那一条AuthorDetail
# 反向查询
# 查询444这个电话号是谁的?
author_detail_obj = models.AuthorDetail.objects.get(telephone='444')
print(author_detail_obj.author.name)
# Author ---------> AuthorDetail 正向查询 author_obj.authDetail.addr, 对象.关联名称
# Author <--------- AuthorDetail 反向查询 author_detail_obj.author.name,对象.小写类名
# 一对多
# 正向查询
# 查询一下 李帅的床头故事 这本书的出版社是哪个
book_obj = models.Book.objects.get(title='李帅的床头故事')
print(book_obj.publishs.name) # B哥出版社
# 反向查询
# B哥出版社出版了哪些书?
pub_obj = models.Publish.objects.get(name='B哥出版社')
print(pub_obj.book_set.all()) # 一对多,反向找,可能会找到多个,所以加set,结果是QuerySet
# Book -----------> Publish 正向查询 book_obj.publishs.name 对象.属性
# Book <----------- Publish 反向查询 pub_obj.book_set.all() 对象.小写类名_set
# 多对多
# 正向查询
# 李帅的床头故事 这本书是谁写的?
book_obj = models.Book.objects.get(title='李帅的床头故事')
print(book_obj.authors.all())
# 反向查询
# 盖伦写了哪些书?
author_obj = models.Author.objects.get(name='盖伦')
print(author_obj.book_set.all())
# Book ---------------> Author 正向查询 book_obj.authors.all() 对象.属性
# Book <--------------- Author 反向查询 pub_obj.book_set.all() 对象.小写类名_set
基于双下划线的跨表查询
# 基于双下划线的跨表查询 联表 效率高
# 一对一
# 1.查询盖伦的电话号
# 方式1 正向
obj = models.Author.objects.filter(name='盖伦').values('authorDetail__telephone')
print(obj) # QuerySet类型
# 方式2 反向
obj = models.AuthorDetail.objects.filter(author__name='盖伦').values('telephone', 'author__age')
print(obj) # QuerySet类型
# 2.查询哪个老师的电话是444?
# 正向
obj = models.Author.objects.filter(authorDetail__telephone='444').values('name')
print(obj)
# 反向查询
obj = models.AuthorDetail.objects.filter(telephone='444').values('author__name')
print(obj)
# 一对多
# 查询一下 李帅的床头故事 这本书的出版社是哪个?
obj = models.Book.objects.filter(title='李帅的床头故事').values('publishs__name')
print(obj)
obj = models.Publish.objects.filter(book__title='李帅的床头故事').values('name')
print(obj)
# B哥出版社都出版了那些书?
obj = models.Publish.objects.filter(name='B哥出版社').values('book__title')
print(obj)
obj = models.Book.objects.filter(publishs__name='B哥出版社').values('title')
print(obj)
# 多对多
# 李帅的床头故事 这本书是谁写的?
obj = models.Book.objects.filter(title='李帅的床头故事').values('authors__name')
print(obj)
obj = models.Author.objects.filter(book__title='李帅的床头故事').values('name')
print(obj)
# 盖伦写了那些书
obj = models.Book.objects.filter(authors__name='盖伦').values('title')
print(obj)
obj = models.Author.objects.filter(name='盖伦').values('book__title')
print(obj)
进阶的查询
# 进阶的查询
# 查询B哥出版社出版的书的名称以及作者的名字?
obj = models.Book.objects.filter(publishs__name='B哥出版社').values('title','authors__name')
print(obj)
# 手机号以4开头的作者出版过的所有书籍名称以及出版社名称
# Author AuthorDetail Book Publish
obj = models.Author.objects.filter(authorDetail__telephone__startswith='4').values('book__title', 'book__publishs__name')
print(obj)
# models.py里的calss里,外键起别名
# publishs=models.ForeignKey(to="Publish",to_field="id",on_delete=models.CASCADE,related_name='xxx') # 反向查询的时候不用写表名了,直接写xxx即可,再写表名就会报错,正向不影响
# 同样的manytomany也有
聚合查询 aggregate
aggregate()
是QuerySet
的一个终止子句,意思是说,它返回一个包含一些键值对的字典。
键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的
from django.db.models import Max, Min, Count, Sum, Avg
# 聚合aggregate 终止子句
# {'price__max': Decimal('999.00')}
ret = models.Book.objects.all().aggregate(Max("price")) # 获取Book表中price的最大值
# {'price__max': Decimal('999.00'), 'price__min': Decimal('1.00')
ret = models.Book.objects.all().aggregate(Max("price"), Min("price")) # 获取Book表中price的最大,最小值
# {'max': Decimal('999.00'), 'min': Decimal('1.00')}
ret = models.Book.objects.all().aggregate(max=Max("price"), min=Min("price")) # 设置别名
# {'max': Decimal('999.00'), 'min': Decimal('22.00')}
ret = models.Book.objects.filter(id__lte=5).aggregate(max=Max("price"), min=Min("price")) # 添加条件让id<=5的最大最小值
# print(ret)
分组查询group by(annotate)
# 分组 group by
# annotate 注释 过程中使用了分组
# 统计每一本书的作者个数 {'name': '软件工程dsds', 'pub_id': 1} values:表示以什么进行分组
ret = models.Book.objects.annotate(Count("authors")).values("name","pub_id")
# for i in ret:
# print(i)
# 统计出每个出版社卖最便宜的书的价格
#方法一:
ret = models.Publisher.objects.annotate(Min("book__price")).values()
# for i in ret:
# print(i)
#方法二:
ret = models.Book.objects.values("pub","pub__name").annotate(Min("price")) # 按照pub_id pub_name进行分组
# for i in ret:
# print(i)
# 统计不止一个作者的图书
ret = models.Book.objects.annotate(cont=Count("authors")).filter(cont__gt=1) # 两个下划线
# for i in ret:
# print(i)
# 根据一本图书作者数量的多少对查询集 QuerySet进行排序
ret = models.Book.objects.annotate(count=Count("authors")).order_by("-count") #加-是降序
# for i in ret:
# print(i)
# 查询条个作者出的书的总价格
ret = models.Author.objects.annotate(sum=Sum("books__price")).values()
# for i in ret:
# print(i)
ret = models.Book.objects.values("authors","authors__name").annotate(sum=Sum("price"))
# for i in ret:
# print(i)
更多资料:https://www.cnblogs.com/maple-shaw/articles/9403501.html
F和Q
from django.db.models import F,Q
# F
ret = models.Book.objects.filter(kucun__lt=50).values("name","kucun") #kucun小于50的书籍
ret = models.Book.objects.filter(sale__gt=F("kucun")).values("name","sale","kucun") # 销售量大于kucun的书籍
# for i in ret:
# print(i)
models.Book.objects.filter(id__lte=5).update(sale=F("sale")*5+10) # 更新id小于等于5的,让sale数量*5+10
# Q()
'''
| 或
& 与
~ 非
'''
ret = models.Book.objects.filter(Q(id__lt=4)|Q(id__gt=5)).values("name","id") # id小于4或id大于5的
ret = models.Book.objects.filter(Q(Q(id__lt=3)|Q(id__gt=5)) & Q(name__startswith="张"))
ret = models.Book.objects.filter(Q(Q(id__lt=3)|Q(id__gt=5)) &~ Q(name__startswith="张")).values("name","id")
for i in ret:
print(i)
二、外键的操作
1.在models.py创建表,在数据库迁移
class Publisher(models.Model):
name = models.CharField(max_length=32,verbose_name="出版社名称")
def __str__(self):
return "<Publisher object:{}-{}>".format(self.id,self.name)
class Book(models.Model):
name = models.CharField(max_length=32,verbose_name="书名")
pub = models.ForeignKey("Publisher", on_delete=models.CASCADE, related_name="books", related_query_name="book")
def __str__(self):
return "<Book object:{}-{}>".format(self.id,self.name)
基于对象查询
# 正向查询 通过Book表,拿到publisher表
book_obj = models.Book.objects.get(pk=3) # 获取Book表id为3的
print(book_obj)
print(book_obj.pub) # 查询Book表id为3的 对应的出版社名称
print(book_obj.pub_id) # 关系出版社的id
# 反向查询 通过Publisher,拿到Book表
pub_obj = models.Publisher.objects.get(pk=2) # 获取Publisher表id为2的
# 1.没有指定related_name 就是类名小写_set
print(pub_obj)
print(pub_obj.book_set, type(pub_obj.book_set)) # 类名小写_set 关系管理对象
print(pub_obj.book_set.all()) # 获取全部的书
# 2.指定related_name="books" 没有类名小写_set的写法了
print(pub_obj.books.all()) # 直接 对象.别名.all()
基于字段查询
ret = models.Book.objects.filter(pub__name="清华出版社2") # 根据Book表的外键,pub出版社的名字查找对应的书籍
ret = models.Book.objects.filter(pub__name__contains="清华出版社2") # 根据Book表的外键,包含"清华出版社2"的名称
# 不指定related_name="books" 就是类名小写 不指定related_query_name
ret = models.Publisher.objects.filter(book__name="Hadoop大数据挖掘")
# 指定related_name="books" 指定的名字 不指定related_query_name
ret = models.Publisher.objects.filter(books__name="Hadoop大数据挖掘")
# 指定related_query_name="book"
ret = models.Publisher.objects.filter(book__name="Hadoop大数据挖掘")
多对多的操作
# 图书表和作者表的多对多的关系
author_obj = models.Author.objects.get(pk=1) # author-->author_books-->book
print(author_obj.books) # 关系管理对象
print(author_obj.books.all()) # 所关联的对象集合 3-软件工程dsds
book_obj = models.Book.objects.get(pk=4) # book-->author_books-->author 用Book 通过author_books表的book_id=4,查询对应的作者
print(book_obj.author_set.all())
ret = models.Book.objects.filter(author__name="李四") # author--> author_books --> book 4-大数据可视化
ret = models.Author.objects.filter(books__name="大数据可视化") # book-->author_books-->author
# 关系管理对象的方法
# all 查询所有的对象
print(author_obj.books.all())
# set 设置多对多的关系[id,id] [对象,对象]
author_obj.books.set([3,4,5]) #修改了id=1代表作为3,4,5
author_obj.books.set(models.Book.objects.filter(id_in=[1,2]))
# add 添加多对多关系
author_obj.books.add(6) # 给id=添加一个代表作6
author_obj.objects.add(models.Book.objects.filter(id__in=[5,6])) # 添加5,6
# remove 删除多对多关系
author_obj.books.remove(3, 4) # 删除作者1对应的3,4的代表作
author_obj.books.remove(*models.Book.objects.filter(id__in=[5])) #删除5
#clear 清空多对多关系
author_obj.books.clear
# create 新建一个对象和当前的对象建立关系
author_obj.books.create(name="一二三四",pub_id=2)
添加快捷键
File --> Settings --> Editor --> Live Templates --> Django --> 点击右边的+号 --> 选择 Live Templates --> 在Abbreviation里面输入快捷键名字 --> Template text:里面输入内容
三、ORM练习题
import os
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm_homework.settings")
django.setup()
from app01 import models
# 1. 查找所有书名里包含金老板的书
ret = models.Book.objects.filter(title__contains="金老板")
# 2. 查找出版日期是2018年的书
ret = models.Book.objects.filter(publish_date__year=2018)
# 3. 查找出版日期是2017年的书名
ret = models.Book.objects.values("title").filter(publish_date__year=2017)
# 4. 查找价格大于10元的书
ret = models.Book.objects.filter(price__gt=10)
# 5. 查找价格大于10元的书名和价格
ret = models.Book.objects.values("title", "price").filter(price__gt=10)
# 6. 查找memo字段是空的书
from django.db.models import Q # 有多个条件的时候可以用Q
ret = models.Book.objects.filter(Q(memo__isnull=True)|Q(memo=""))
# 7. 查找在北京的出版社
ret = models.Publisher.objects.filter(city="北京")
# 8. 查找名字以沙河开头的出版社
ret = models.Publisher.objects.filter(name__startswith="沙河")
# 9. 查找“沙河出版社”出版的所有书籍
ret = models.Book.objects.filter(publisher__name="沙河出版社")
# 10. 查找每个出版社出版价格最高的书籍价格
from django.db.models import Avg,Max,Min,Count,Sum
ret = models.Book.objects.annotate(max=Max("price")).values("publisher","publisher__name","max") # 通过book找publisher
ret = models.Publisher.objects.annotate(max=Max("book__price")).values("name","max") #通过publisher找book
# 11. 查找每个出版社的名字以及出的书籍数量
ret = models.Publisher.objects.annotate(count=Count("book")).values("name","count")
# 12. 查找作者名字里面带“小”字的作者
ret = models.Author.objects.filter(name__contains="小")
# 13. 查找年龄大于30岁的作者
ret = models.Author.objects.filter(age__gt=30)
# 14. 查找手机号是155开头的作者
ret = models.Author.objects.filter(phone__startswith="155")
# 15. 查找手机号是155开头的作者的姓名和年龄
ret = models.Author.objects.filter(phone__startswith="155").values("name","age")
# 16. 查找每个作者写的价格最高的书籍价格
ret = models.Author.objects.annotate(max=Max("book__price")).values("name","max")
ret = models.Book.objects.values("author","author__name").annotate(max=Max("price"))
# 17. 查找每个作者的姓名以及出的书籍数量
ret = models.Author.objects.annotate(count=Count("book")).values("name","count")
ret = models.Book.objects.values("author","author__name").annotate(count=Count("id"))
# 18. 查找书名是“跟金老板学开车”的书的出版社
ret = models.Book.objects.filter(title="跟金老板学开车").values("publisher__name")
ret = models.Publisher.objects.filter(book__title="跟金老板学开车")
# 19. 查找书名是“跟金老板学开车”的书的出版社所在的城市
ret = models.Book.objects.filter(title="跟金老板学开车").values("publisher__name","publisher__city")
ret = models.Publisher.objects.filter(book__title="跟金老板学开车").values('city')
# 20. 查找书名是“跟金老板学开车”的书的出版社的名称
ret = models.Book.objects.filter(title="跟金老板学开车").values("publisher__name")
ret = models.Publisher.objects.filter(book__title="跟金老板学开车").values('name')
# 21. 查找书名是“跟金老板学开车”的书的出版社出版的其他书籍的名字和价格
pub_obj = models.Publisher.objects.filter(book__title="跟金老板学开车").first()
ret = pub_obj.book_set
ret = models.Book.objects.filter(publisher__book__title="跟金老板学开车").exclude(title="跟金老板学开车").values("title","price")
# 22. 查找书名是“跟金老板学开车”的书的所有作者
ret = models.Author.objects.filter(book__title="跟金老板学开车").values("name")
# 23. 查找书名是“跟金老板学开车”的书的作者的年龄
ret = models.Author.objects.filter(book__title="跟金老板学开车").values("age")
# 24. 查找书名是“跟金老板学开车”的书的作者的手机号码
ret = models.Author.objects.filter(book__title="跟金老板学开车").values("phone")
# 25. 查找书名是“跟金老板学开车”的书的作者们的姓名以及出版的所有书籍名称和价钱
authors = models.Author.objects.filter(book__title="跟金老板学开车")
# for author in authors:
# print(author.book_set.values("title","price"))
ret = models.Book.objects.filter(title="跟金老板学开车").values("author__name","author__book__title","author__book__price")
for i in ret:
print(i)