标签:级联 子查询 choices mode efi ted pre max sel
分别从一张表中取出一条记录与另外一张表比对
每次删除或改动表与字段 都要做数据库迁移,数据库迁移的时候会通过改动同步到数据库
删除表:
改动字段:
实例:我们来假定下面这些概念,字段和关系
作者详细模型:
出版商模型:
书籍模型:
many-to-many: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many),django自动创建第三张表,存放外键书籍id,作者id
one-to-many: 一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。在多的一方创建关联字段
注意事项:
myapp_modelName
,是根据 模型中的元数据自动生成的,也可以覆写为别的名称 id
字段是自动添加的
"_id"
来创建数据库中的列名CREATE TABLE
SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。models.py
所在应用的名称。models.py
from django.db import models
# Create your models here.
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name=models.CharField( max_length=32)
age=models.IntegerField()
# 与AuthorDetail建立一对一的关系
# 在app01_author表中添加 authordetail_id字段, 小写关联表名+_id
# AuthorDetail 记录删除,Author记录也删除, 级联删除
authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE)
class AuthorDetail(models.Model):
nid = models.AutoField(primary_key=True)
birthday=models.DateField()
telephone=models.BigIntegerField()
addr=models.CharField( max_length=64)
class Publish(models.Model):
nid = models.AutoField(primary_key=True)
name=models.CharField( max_length=32)
city=models.CharField( max_length=32)
email=models.EmailField()
class Book(models.Model):
nid = models.AutoField(primary_key=True)
title = models.CharField( max_length=32)
publishDate=models.DateField()
price=models.DecimalField(max_digits=5,decimal_places=2)
# 与Publish建立一对多的关系,外键字段建立在多的一方
# app01_book表中添加publish_id
publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
# 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
# app01_book_authors 表结构: id book_id author_id
# 表名拼接方法: app应用名称 + 小写表名 + 所在表属性(authors) : app01_book_authors
authors=models.ManyToManyField(to=‘Author‘,)
models.ForginKey:
sql语句:
create table book(title varchar(32),......, publish_id INT, foreignkey references Publish(id))
参数:django源码
def __init__(self, to, on_delete=None, related_name=None, related_query_name=None,
limit_choices_to=None, parent_link=False, to_field=None,
db_constraint=True, **kwargs):
添加记录
create方法 与 save方法 区别:
用于创建新的对象,两者都可以。如果用于更新现有对象,只能用save
sunny = Author(name=‘sunny‘, age=‘20‘)
sunny.save()
没有关联的表的记录的添加与单表操作一样:
sunny_author_detail = Book.object.create(name=‘sunny‘, age=‘10‘)
一对多,多的表中添加关联字段:
一对一,与一对多添加一致
# 书籍:多 出版社:一
# 第一种方法,字段+_id直接赋值,推荐:直观
python_book = Book.object.create(title=‘python‘, price=‘100‘, publish_id=1)
# 第二种方法,先在publish表中查询出model对象,再赋值
# 语法:
# publish = pub_obj
pub_obj = Publish.object.filter(title=‘python‘).first() # 取得model对象
python_book = Book.object.create(title=‘python‘, price=‘100‘, publish=pub_obj)
# django会多出翻译的过程
# 赋值关联字段记录,关联属性publish会被翻译成publish_id, pub_obj会取出主键id值 赋值给publish_id
多对多:
多对多,orm自动创建的第三张表,如 书籍 与 作者 book <---> author, 第三张表:app01_book_authors
# 先查询需要添加记录的表记录
book_python = Book.objects.filter(title=‘python‘).first()
author_sunny = Author.objects.filter(name=‘sunny‘).first()
author_vickey = Author.objects.filter(name=‘ethan‘).first()
# 第三张表添加关联字段
# 第一种方式:
book_python.authors.add(author_sunny, author_vickey)
# 第二种方式:
book_python.authors.add(1, 2)
# 第三种方式: 使用最多,因为绑定键值一般是用户指定
book_python.authors.add(*[1, 2])
‘‘‘
book_python.authors 会找到Book与Author的关系表
app01_book_authors:
orm 会自动将 book_python的id 添加到 app01_book_authors 中的book_id
自动将 author_sunny author_vickey的id 添加到 app01_book_authors 中的 author_id
‘‘‘
解除单个键:表的关联属性.remove(关联表的记录对象或id)
# 解除作者:sunny 与 book:python 的绑定关系, 会在第三张表中删除此记录
author_sunny = Author.objects.filter(name=‘sunny‘).first()
book_python = Book.objects.filter(title=‘python‘).first()
# book_python = Book.objects.filter(nid=1).first()
book_python.authors.remove(author_sunny)
# book_python.authors.remove(1)
解除该记录所有绑定: 表的关联属性.clear()
book_python = Book.objects.filter(nid=1).first()
# 删除书籍python在第三张表中所有绑定的记录
book_python.authors.clear()
解除再绑定: 表的关联属性.set(id)
book_python = Book.objects.filter(nid=1).first()
book_python.authors.set(2)
"""
等价于:
book_python.authors.clear()
book_python.authors.add(2)
"""
查询记录:
关联属性所在的表 发起的查询为 正向查询
先进行父查询,再对父查询的结果进行子查询
在SQL语言中,一个SELECT-FROM-WHERE语句称为一个查询块。当获得一个查询的答案需要多个步骤的操作,首先必须创建一个查询来确定用户不知道但包含在数据库中的值,将一个查询块嵌套在另一个查询块的WHERE字句或HAVING短语的条件中查询块称为子查询或内层查询
一对多:
正向查询: 通过关联属性所在的表("多") 查 关联表("一")记录 (book_obj.publish)
? 按属性(字段)查:Book对象.关联属性(字段)
? Book -----------------------------------------------> Publish
? <-----------------------------------------------
反向查询: 按关联属性(字段)所在的表名小写+_set.all(): Publish对象.book_set.all()
多对多:
? ? 正向查询: 按属性(字段) book_obj.authors.all() (多对多可能包含多个记录)
? ? Book ---------------------------------------------------------------------> Author
? <---------------------------------------------------------------------
? ? 反向查询: 关联属性所在的表名小写+_set.all() author_obj.book_set.all() queryset 对象
一对一:
? ? 正向查询:按属性(字段) author_obj.author_detail (因为是唯一的记录 不加.all())
? ? Author --------------------------------------------------------------------> AuthorDetails
? <--------------------------------------------------------------------
? ? 反向查询: 关联属性所在的表名小写 authordetails_obj.author.age (唯一记录,不加_set)
def query(request):
# # ############### 基于对象的跨表查询 ####################
# 先进行父查询,在对父查询的结果进行子查询
# 一对多:
# 基于对象的查询, 多的表中的关联属性 即对应的表的对象
# # 1. 查询python这本书的出版社和邮箱 ------> 正向查询
python_book = Book.objects.filter(title=‘python‘).first()
# python_book.publish 与这本书关联的出版社对象
# 即 publish_obj = Publish.objects.filter(nid=python_book_id).first() 对象
print(python_book.publish)
print(python_book.publish.name)
print(python_book.publish.email)
# # 2. 查询苹果出版社所有书籍的名称 -------> 反向查询
pub_obj = Publish.objects.get(name=‘苹果出版社‘)
print(pub_obj.book_set.all()) # QuerySet对象
book_obj_lst = pub_obj.book_set.all().values(‘title‘, ‘price‘, ‘pub_date‘)
for book in book_obj_lst:
for k, v in book.items():
print(f‘{k}:{v}‘)
# 多对多:
# # 1. 查询python这本书的作者的年龄 -------> 正向查询
python_book = Book.objects.filter(title=‘python‘).first()
ret = python_book.authors.all() # 与这本书关联的所有作者的QuerySet对象
ret = python_book.authors.all().values(‘age‘)
print(ret)
# # 2. 查询sunny出版过的所有书籍名称 --------> 反向查询
sunny_author = Author.objects.filter(name=‘sunny‘).first()
ret = sunny_author.book_set.all()
print(ret)
# 一对一:
# # 1. 查询作者名字为sunny的手机号 ----------> 正向查询
sunny = Author.objects.get(name=‘sunny‘)
sunny_tel = sunny.author_detail.tel
print(sunny_tel)
# # 2. 查询手机号为10086的作者名字 ---------> 反向查询
tel_obj = AuthorDetails.objects.get(tel=‘10086‘)
author_obj = tel_obj.author.name
print(author_obj)
# return HttpResponse(publish_python.name + publish_python.email, charset=‘gbk‘)
return HttpResponse(‘ok‘)
将多张表拼接成单张表,再进行相关的查询
从基表出发,按关联关系进行拼表, 先拼接有关系的表
拼表后会按"多"的表记录进行显示,如Book表的一本书有2个作者,拼表后会有2条同样书籍对象但作者不一样的记录
正向查询:按子段
反向查询: 按表名小写
# ############### 关联表查询 ##################
# filter: sql语句中where过滤的条件, values: sql语句中select要显示的字段
# 一对多:
# 1. 查询python这本书的出版社和邮箱
# 以Book为基表 拼接 Publish表
ret = Book.objects.filter(title=‘python‘).values(‘publish__name‘, ‘publish__email‘)
# 以Publish为基表 拼接 Book表
ret = Publish.objects.filter(book__title="python").values(‘name‘, ‘email‘)
# 2. 查询苹果出版社所有书籍的名称
ret = Publish.objects.filter(name=‘苹果出版社‘).values(‘book__title‘)
ret = Book.objects.filter(publish__name=‘苹果出版社‘).values(‘title‘)
# 多对多: 连接3张表, Book Author, orms自动拼接第三张表 book_authors
# 1. 查询python这本书的作者的年龄
ret = Book.objects.filter(title=‘python‘).values(‘authors__age‘) # 正跨: 通过book属性authors连接第三张表
ret = Author.objects.filter(book__title=‘python‘).values(‘age‘) # 反跨: 通过表名小写自动连接第三张表
# 2. 查询少商出版过的所有书籍名称
ret = Author.objects.filter(name=‘少商‘).values(‘book__title‘)
ret = Book.objects.filter(authors__name=‘少商‘).values(‘title‘)
# 一对一:
# # 1. 查询作者名字为少商的手机号
ret = Author.objects.filter(name=‘少商‘).values(‘author_detail__tel‘)
ret = AuthorDetails.objects.filter(author__name=‘少商‘).values(‘tel‘)
# 2. 查询手机号为10086的作者名字
ret = Author.objects.filter(author_detail__tel=‘10086‘).values(‘name‘)
ret = AuthorDetails.objects.filter(tel=‘10086‘).values(‘author__name‘)
# ############# 连续跨表查询 ####################
# 查询苹果出版社出版过的所有书籍的名字以及作者的姓名
ret = Book.objects.filter(publish__name=‘橘子出版社‘).values(‘title‘, ‘authors__name‘)
ret = Publish.objects.filter(name=‘橘子出版社‘).values(‘book__title‘, ‘book__authors__name‘)
ret = Author.objects.filter(book__publish__name=‘橘子出版社‘).values(‘name‘, ‘book__title‘)
ret = AuthorDetails.objects.filter(author__book__publish__name=‘橘子出版社‘).values(‘author__book__title‘, ‘author__name‘)
# 手机号以100开头的作者出版过的所有书籍名称以及出版社名称
ret = Book.objects.filter(authors__author_detail__tel__startswith=100).values(‘title‘, ‘publish__name‘)
ret = Author.objects.filter(author_detail__tel__startswith=200).values(‘book__title‘, ‘book__publish__name‘)
ret = Publish.objects.filter(book__authors__author_detail__tel__startswith=200).values(‘book__title‘, ‘name‘)
# author__book__title 按关联关系进行拼接
ret = AuthorDetails.objects.filter(tel__startswith=100).values(‘author__book__title‘,
‘author__book__publish__name‘)
聚合函数: Count, Sum, Avg, Max, Minfrom django.db.models import Count, Sum, Avg, Max, Min**
支持聚合函数的方法:
from django.db.models import Count, Sum, Avg, Max, Min # 导入聚合函数
# ############ 聚合查询 ############
# 1. Avg: 查询所有书籍的平均价格
# sql: select avg(price) from book
ret = Book.objects.all().aggregate(Avg(‘price‘)) # 返回字典, {‘price__avg‘: Decimal(‘88.000000‘)}
# 起别名
ret = Book.objects.all().aggregate(priceAvg=Avg(‘price‘)) # {‘priceAvg‘: Decimal(‘88.000000‘)}
# 2. Count: 查询所有书籍的个数
ret = Book.objects.all().aggregate(c=Count(‘nid‘)) # Count(1) 也可以
print(ret)
分组查询:
支持分组函数的方法: annotate() 返回QuerySet即 对象的列表,如果是values返回包装成字典的对象列表
语法: Book.objects.values(‘title‘).annotate(bookCount=Count(1))
sql: select title, count(1) from book group by title
ps: 单表按主键分组没有意义
语法解析: values(): select与group by的字段, annotate():group by统计(分组统计)
# ############ 分组查询 annotate############
# ######### 单表分组查询
# 1. 查询书籍表每一个出版社id以及对应的书籍的个数
# values(): select & group by. annotate():统计
# 返回QuerySet
# sql: select publish_id, count(1) from book GROUP BY publish_id
ret = Book.objects.values(‘publish_id‘).annotate(c=Count(1))
# <QuerySet [{‘publish_id‘: 1, ‘c‘: 2}, {‘publish_id‘: 2, ‘c‘: 2}, {‘publish_id‘: 3, ‘c‘: 1}]>
# emp员工表查询
# 1. 查询每一个部门的名称以及对应员工的平均薪水
ret = Emp.objects.values(‘dep‘).annotate(depAvg=Avg(‘salary‘)) # group by dep
# 2. 查询每一个省份的名称以及对应的员工人数
ret = Emp.objects.values(‘pro‘).annotate(empCount=Count(1))
# 3. 查询每一个省份的名称以及对应的员工最大年龄
ret = Emp.objects.values(‘pro‘).annotate(maxAge=Max(‘age‘))
# ############# 跨表分组查询 ##################
# 相关表join成一张表,再group by
# 1. 查询每个出版社的名称以及对应的书籍的个数
ret = Book.objects.values(‘publish__name‘).annotate(bookCount=Count(1))
ret = Publish.objects.values(‘name‘).annotate(bookCount=Count(‘book__title‘))
# 2. 查询每个作者的名字以及出版的书籍的最高价格
# 作者可能有重复的名称且不是一个人,所以要按主键和name进行分组
# 如果按多个字段分组, 如果多个字段的值完全相同则放入同一个组,有一个字段不相等则放入一个新组
ret = Author.objects.values(‘pk‘, ‘name‘).annotate(maxPrice=Max(‘book__price‘))
# 3. 查询每个作者出版的书籍的名称和个数
ret = Book.objects.values(‘title‘).annotate(Count(‘authors‘))
常用方法及其注意事项:
# #### 重要 ###
# 如果基表字段有重复值, 分组会将2个相同字段分为一个组 即 values按字段分组会可能会有不符合预期的逻辑错误
# all() 即按主键分组,即每行表记录都是一个分组的依据
# 如:Book表的title有一个重复的书籍名称‘python学习手册‘,group by title 会把重复的书籍汇总到一个组
ret = Book.objects.values(‘title‘).annotate(Count(‘authors‘))
# 4条记录: <QuerySet [{‘title‘: ‘python学习手册‘, ‘authors__count‘: 4}, {‘title‘: ‘python标准库‘, ‘authors__count‘: 3},
# {‘title‘: ‘JAVA编程思想‘, ‘authors__count‘: 1}, {‘title‘: ‘linux私房菜‘, ‘authors__count‘: 2}]>
print(ret)
ret = Book.objects.all().annotate(Count(‘authors‘))
# 5条记录: <QuerySet [<Book: python学习手册>, <Book: python标准库>, <Book: JAVA编程思想>, <Book: linux私房菜>, <Book: python学习手册>]>
print(ret)
# ################ 分组查询常用方法 ###################
# 用all()方法返回的model对象的列表(QuerySet),在model对象自动加入了统计字段authors_count,可用对象.统计字段显示
# 并且可在此QuerySet对象基础上用其他QuerySet可调用的方法进行级联操作
ret = Book.objects.all().annotate(Count(‘authors‘))
# 简写:
ret = Book.objects.annotate(Count(‘authors‘))
# 显示书籍作者大于2个的书籍名称
ret = Book.objects.annotate(Count(‘authors‘)).filter(authors__count__gt=2).values(‘title‘)
# ######## F 与 Q 查询
from django.db.models import F, Q
# ------------ F 查询:(Field) -----------
# 运算符右边可以是字段: 可以两个字段比较 comment=F("thumbs_up")
# 1. 书籍评论数等于点赞数
ret = Book.objects.filter(comment=F("thumbs_up")).values(‘title‘, ‘comment‘, ‘thumbs_up‘)
# 2. 书籍评论数大于点赞数
ret = Book.objects.filter(comment__gt=F("thumbs_up")).values(‘title‘, ‘comment‘, ‘thumbs_up‘)
# 3. 修改名称 加上版号: 如 python(第二版)
from django.db.models.functions import Concat # Concat 连接
from django.db.models import Value # Value 常量
Book.objects.filter(title__startswith="py").update(title=Concat(F("title"), Value("第二版"), Value("中文版")))
# 4. price提高2倍
# Book.objects.filter(nid=2).update(price=F("price") * 2)
Book.objects.filter(nid=2).update(price=F("price") * Value(2))
# ------------ Q 查询:(Field) -----------
# filter 参数之间都是与操作.
# Q 查询可以进行其他逻辑查询, 并且可以嵌套使用
# & : 与
# 书籍评论数与点赞数都大于100的书籍
ret = Book.objects.filter(Q(comment__gt=100) & Q(thumbs_up__gt=100)).values(‘title‘)
# |: 或
# 书籍评论数小于500或点赞数大于800的书籍
ret = Book.objects.filter(Q(comment__lt=500) | Q(thumbs_up__lt=800)).values(‘title‘)
# ~: 非
# 书籍评论数不等于1000的书籍
ret = Book.objects.filter(~Q(comment=1000)).values(‘title‘)
# 查询函数可以混合使用Q 对象和关键字参数。
# 所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。
# 但是,如果出现Q 对象,它必须位于所有关键字参数的前面。
ret = Book.objects.filter(~Q(comment=1000) | Q(comment__lt=100), title__contains=‘py‘).values(‘title‘)
标签:级联 子查询 choices mode efi ted pre max sel
原文地址:https://www.cnblogs.com/relaxlee/p/12843100.html