模型层
Django 模型是与数据库相关的,与数据库相关的代码一般写在 models.py 中。在前面了解了对MySQL数据库的基本操作,下面学习对数据库的更多的查询操作,首先创建表模型,进行数据库迁移,并添加对应的数据。
# 单表查询表
class User(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
register_time = models.DateField()
def __str__(self):
return '对象的名字:%s'%self.name
多表查询表
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8,decimal_places=2)
publish_date = models.DateField(auto_now_add=True)
# 外键关系
publish = models.ForeignKey(to='Publish')
authors = models.ManyToManyField(to='Author') # 虚拟字段,信号字段
def __str__(self):
return '书籍对象的名字:%s'%self.title
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
email = models.EmailField() # 对应就是varchar类型
def __str__(self):
return '出版社对象的名字:%s'%self.name
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
authordetail = models.OneToOneField(to='AuthorDetail')
def __str__(self):
return '作者对象的名字:%s'%self.name
class AuthorDetail(models.Model):
phone = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
单表查询
单表查询数据基本操作:
查询数据
- all(): 查询所有结果
- filter(**kwargs): 它包含了与所给筛选条件相匹配的对象
res = models.User.objects.filter(name='linwow',age=23)
filter内可以放多个限制条件但是需要注意的是多个条件之间是and关系
- get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
- exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象
res = models.User.objects.exclude(name='linwow')
- order_by(*field): 对查询结果排序
res = models.User.objects.order_by('age') # 默认是升序
res = models.User.objects.order_by('-age') # 可以在排序的字段前面加一个减号就是降序
- reverse(): 对查询结果反向排序,前面要先有排序才能反向
res = models.User.objects.order_by('age').reverse()
- count(): 返回数据库中匹配查询(QuerySet) 的对象数量。
res = models.User.objects.count()
res = models.User.objects.all().count()
- first(): 返回第一条记录
res = models.User.objects.all().first()
res = models.User.objects.all()[0] # 不支持负数的索引取值
- last(): 返回最后一条记录
res = models.User.objects.all().last()
- exists(): 如果QuerySet包含数据,就返回True,否则返回False
res = models.User.objects.all().exists()
res = models.User.objects.filter(name='linwow',age=3).exists()
- values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列,model的实例化对象,而是一个可迭代的字典序列
res = models.User.objects.values('name') # 列表套字典
res = models.User.objects.values('name','age') # 列表套字典
- values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
res = models.User.objects.values_list('name','age') # 列表套元祖
- distinct(): 从返回结果中剔除重复纪录 去重的对象必须是完全相同的数据才能去重
res = models.User.objects.values('name','age').distinct()
字段名称字段中不能有 __(双下划线,因为在Django QuerySet API中有特殊含义(用于关系,包含,不区分大小写,以什么开头或结尾,日期的大于小于,正则等)。
下面是利用双下划线查询的操作:
查询年轻大于44岁的用户
res = models.User.objects.filter(age__gt=44)
查询年轻小于44岁的用户
res = models.User.objects.filter(age__lt=44)
查询年轻大于等于44岁的用户
res = models.User.objects.filter(age__gte=44)
查询年龄是44或者22或者73的用户
res = models.User.objects.filter(age__in=[44,22,73])
查询年龄在22到44范围内
res = models.User.objects.filter(age__range=[22,44])
查询名字中包含字母w的用户,sqlite数据库演示不出来大小写的情况!!!
res = models.Author.objects.filter(name__contains='w')
res = models.User.objects.filter(name__icontains='w') # 无视大小写
查询名字以l开头的用户
res = models.User.objects.filter(name__startswith='l')
查询名字以w结尾的用户
res = models.User.objects.filter(name__endswith='w')
查询注册是在2019年的用户,使用mysql数据库,sqlite对日期格式不太精准
res = models.User.objects.filter(register_time__year=2019)
多表新增
直接写id:
models.Book.objects.create(title='红楼梦',price=66.66,publish_id=1)
传数据对象:
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='三国演义',price=199.99,publish=publish_obj)
多表修改
queryset修改:
models.Book.objects.filter(pk=1).update(publish_id=3)
# 传数据对象
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.filter(pk=1).update(publish=publish_obj)
对象修改:
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.publish_id = 3 # 点表中真实存在的字段名
book_obj.save()
# 传数据对象
publish_obj = models.Publish.objects.filter(pk=2).first()
book_obj.publish = publish_obj # 点orm中字段名 传该字段对应的表的数据对象
book_obj.save()
多表删除
queryset删除:如果有多个数据会一起删除
models.Book.objects.filter(pk=1).delete()
外键的删除:会将book中对应的数据一起删除
models.Publish.objects.filter(pk=1).delete()
对象删除:
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.delete()
绑定关系
添加关系 add:支持传数字或对象,并且都可以传多个
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.add(1)
book_obj.authors.add(2,3)
# 传数据对象
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=3).first()
book_obj.authors.add(author_obj)
book_obj.authors.add(author_obj,author_obj1)
修改书籍与作者的关系set():传入的必须是可迭代对象!!!
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.set((1,)) 可以传数字和对象,并且支持传多个
book_obj.authors.set((1,2,3))
# 传数据对象
author_list = models.Author.objects.all()
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.set(author_list)
删除书籍与作者的绑定关系remove():
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.remove(1)
book_obj.authors.remove(2,3)
# 传数据对象
author_obj = models.Author.objects.all().first()
author_list = models.Author.objects.all()
book_obj.authors.remove(*author_list) 需要将queryset打散
清空clear():清空的是你当前这个表记录对应的绑定关系
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.clear()
对于ForeignKey对象,clear()和remove()方法仅在null=True时存在。
多表查询
表与表之间的关系:
- 一对一(OneToOneField):一对一字段无论建在哪张关系表里面都可以,但是推荐建在查询频率比较高的那张表里面
- 一对多(ForeignKey):一对多字段建在多的那一方
- 多对多(ManyToManyField):多对多字段无论建在哪张关系表里面都可以,但是推荐建在查询频率比较高的那张表里面
正向与反向
正向查询按字段,反向查询按表名小写
一对一:
正向:author---关联字段在author表里--->authordetail 按字段
反向:authordetail---关联字段在author表里--->author 按表名小写
查询某个作者的手机号 正向查询
查询地址是山东的作者名字 反向查询
一对多:
正向:book---关联字段在book表里--->publish 按字段
反向:publish---关联字段在book表里--->book 按表名小写_set.all() 因为一个出版社对应着多个图书
多对多:
正向:book---关联字段在book表里--->author 按字段
反向:author---关联字段在book表里--->book 按表名小写_set.all() 因为一个作者对应着多个图书
正向
查询书籍是三国演义的出版社邮箱
book_obj = models.Book.objects.filter(title='三国演义').first()
print(book_obj.publish.email)
查询书籍是西游记的作者的姓名
book_obj = models.Book.objects.filter(title='西游记').first()
print(book_obj.authors.all())
查询作者为linwow电话号码
user_obj = models.Author.objects.filter(name='linwow').first()
print(user_obj.authordetail.phone)
反向
查询出版社是东方出版社出版的书籍 一对多字段的反向查询
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
print(publish_obj.book_set.all())
查询作者linwow写过的所有的书 多对多字段的反向查询
author_obj = models.Author.objects.filter(name='linwow').first()
print(author_obj.book_set.all())
查询作者电话号码是188的作者姓名 一对一字段的反向查询
authordetail_obj = models.AuthorDetail.objects.filter(phone=188).first()
print(authordetail_obj.author.name)
基于双下滑线的查询
正向
查询书籍为三国演义的出版社地址
res = models.Book.objects.filter(title='三国演义').values('publish__addr','title')
print(res)
查询书籍为西游记的作者的姓名
res = models.Book.objects.filter(title='西游记').values("authors__name",'title')
print(res)
查询作者为linwow的家乡
res = models.Author.objects.filter(name='linwow').values('authordetail__addr')
print(res)
反向
查询南方出版社出版的书名
res = models.Publish.objects.filter(name='南方出版社').values('book__title')
print(res)
查询电话号码为188的作者姓名
res = models.AuthorDetail.objects.filter(phone=188).values('author__name')
print(res)
查询作者为linwow的写的书的名字
res = models.Author.objects.filter(name='linwow').values('book__title')
print(res)
查询书籍为三国演义的作者的电话号码
res = models.Book.objects.filter(title='三国演义').values('authors__authordetail__phone')
print(res)
查询linwow作者的手机号
正向
res = models.Author.objects.filter(name='linwow').values('authordetail__phone')
print(res)
反向
res = models.AuthorDetail.objects.filter(author__name='linwow').values('phone')
print(res)
查询出版社为东方出版社的所有图书的名字和价格
正向
res = models.Publish.objects.filter(name='东方出版社').values('book__title','book__price')
print(res)
反向
res = models.Book.objects.filter(publish__name='东方出版社').values('title','price')
print(res)
查询东方出版社出版的价格大于400的书
正向
res = models.Publish.objects.filter(name="东方出版社",book__price__gt=400).values('book__title','book__price')
print(res)
反向
res = models.Book.objects.filter(price__gt=400,publish__name='东方出版社').values('title','price')
print(res)
聚合查询:aggregate
from django.db.models import Max,Min,Count,Sum,Avg
查询所有书籍的作者个数
res = models.Book.objects.filter(pk=3).aggregate(count_num=Count('authors'))
print(res)
查询所有出版社出版的书的平均价格
res = models.Publish.objects.aggregate(avg_price=Avg('book__price'))
print(res)
统计东方出版社出版的书籍的个数
res = models.Publish.objects.filter(name='东方出版社').aggregate(count_num=Count('book__id'))
print(res)
分组查询(group_by):annotate
统计每个出版社出版的书的平均价格
res = models.Publish.objects.annotate(avg_price=Avg('book__price')).values('name','avg_price')
print(res)
统计每一本书的作者个数
res = models.Book.objects.annotate(count_num=Count('authors')).values('title','count_num')
print(res)
统计出每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
print(res)
查询每个作者出的书的总价格
res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price')
print(res)
使用中的问题:
可以通过前面的values()指定分组字段,后面的values()指定要查询的字段信息
res = models.Publish.objects.values('name').annotate(min_price=Min('book__price')).values('name', 'min_price')
如果在models模型中指定 ordering 字段,在 annotate 后要加 order_by(),否则自定分组字段无效,默认以id分组
res = models.Publish.objects.values('name').annotate(min_price=Min('book__price')).values('name', 'min_price').order_by("name")
拓展:多对多表三种创建方式
1.第一种django中orm自动创建
class Book(models.Model):
name = models.CharField(max_length=32)
authors = models.ManyToManyField(to='Author')
class Author(models.Model):
name = models.CharField(max_length=32)
2.第二种纯手动创建第三张表
class Book(models.Model):
name = 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')
info = models.CharField(max_length=32)
3.第三种半自动创建第三张表(可扩展性高,并且能够符合orm查询)
class Book(models.Model):
name = 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)
# book = models.ManyToManyField(to='Book',through='Book2Author',through_fields=('author','book'))
class Book2Author(models.Model):
book = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')
info = models.CharField(max_length=32)
F查询和Q查询
先创建表模型
class Product(models.Model):
name = models.CharField(max_length=32) # 都是类实例化出来的对象
price = models.DecimalField(max_digits=8,decimal_places=2)
sold = models.IntegerField()
stock = models.IntegerField()
F查询
Django 提供 F() 进行字段值与某个我们自己设定的常量做比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。F可以帮我们取到表中某个字段对应的值来当作我的筛选条件,而不是我认为自定义常量的条件了,实现了动态比较的效果。
查询卖出数大于库存数的商品
from django.db.models import F
res = models.Product.objects.filter(sold__gt=F('stock'))
Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作
将所有的商品的价格提高100块
from django.db.models import F
models.Product.objects.update(price=F('price')+100)
对字符串进行拼接Concat操作,并且要加上拼接值Value)
将所有商品的名字后面都加一个爆款
from django.db.models.functions import Concat
from django.db.models import Value,F
models.Product.objects.update(name=Concat(F('name'),Value('爆款')))
Q查询
filter() 等方法中逗号隔开的条件是与的关系。 如果需要执行更复杂的查询(例如OR语句),可以使用 Q对象。可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。
from django.db.models import F, Q
res = models.Product.objects.filter(Q(price=188.88),Q(name='连衣裙爆款')) # and
res = models.Product.objects.filter(Q(price=188.88)|Q(name='连衣裙爆款')) # or
res = models.Product.objects.filter(Q(price=188.88)|~Q(name='连衣裙爆款')) # not
查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。
混合使用:需要注意的是Q对象必须放在普通的过滤条件前面
res = models.Product.objects.filter(~Q(name='连衣裙爆款'),price=188.88) # not
Q对类的实例化操作
from django.db.models import F, Q
q = Q()
q.connector = 'or' # 通过这个参数可以将Q对象默认的and关系变成or
q.children.append(('price',188.88))
res = models.Product.objects.filter(q) # Q对象查询默认也是and
事务
事务的定义:将多个 sql 语句操作变成原子性操作,要么同时成功,有一个失败则里面回滚到原来的状态,保证数据的完整性和一致性(NoSQL数据库对于事务则是部分支持)。
from django.db import transaction
from django.db.models import F
with transaction.atomic():
# 在with代码块儿写你的事务操作 models.Product.objects.filter(id=1).update(kucun=F('stock')-1)
models.Product.objects.filter(id=1).update(maichu=F('sold')+1)
# 写其他代码逻辑
拓展:配置文件配置参数查看所有orm操作内部的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',
},
}
}