不忘初心,
牢记使命。

Django模型层之多表操作---Django–admin、多表增删改、多表查询(跨表查询、双下划线

2021-04-02 大聪明 0评论 238 0喜欢

Django模型层---多表操作

参考:https://www.cnblogs.com/clschao/articles/10439958.html

三种表关系:一对一、一对多、多对多。

创建模型

# models.py
# 快速运行:
# Tools-->run manage.py Task

from django.db import models

# Create your models here.

from django.db import models

# Create your models here.


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

1. 创建一个超级用户,密码不能低于8位
    manage.py@manytable > createsuperuser
    "C:\Program Files\JetBrains\PyCharm20190301\bin\runnerw64.exe" C:\Users\libo\AppData\Local\Programs\Python\Python36\python.exe "C:\Program Files\JetBrains\PyCharm20190301\plugins\python\helpers\pycharm\django_manage.py" createsuperuser D:/django_projects/manytable
    Tracking file by folder pattern:  migrations
    Username (leave blank to use 'libo'):  laowang
    Email address:  
    Warning: Password input may be echoed.
    Password:  laowang123456
    Warning: Password input may be echoed.
    Password (again):  laowang123456
    Superuser created successfully.
2. admin.py文件中注册你的表到admin后台中
    from django.contrib import admin
    from app01 import models
    # Register your models here.
    admin.site.register(models.Book)
    admin.site.register(models.Publish)
    admin.site.register(models.Author)
    admin.site.register(models.AuthorDetail)
3. 浏览器访问http://127.0.0.1:8000/admin
    登录你的超级用户后就有图形界面,点击相应按钮来操作你的表

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中级联更新,自查

查询

基于对象的跨表查询 -- 类似于子查询

正向查询和反向查询

通过设立约束关系的表想被约束关系的表查询是正向查询--从大腿想孩子查

反过来查就是反向查询

正向查询:关系属性(字段)卸载哪个类(表)里边,从当前类(表)的数据去查询它的关联类(表)的数据。

反向查询:反之。

# 一对一
# 正向查询
# 查询盖伦的住址
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也有

聚合查询

from django.db.models import Avg, Aggregate, Max, Min, Sum, Count

# 计算所有图书的平均价格
obj = models.Book.objects.all().aggregate(a=Avg('price'), m=Max('price'))
print(obj)  # {'a': 55.95, 'm': Decimal('200.00')}  所有书的价格平均值 返回的是键值对 后边不能在跟任何表操作语句

分组查询

# 分组查询
# 统计每个出版社出版的书籍的平均价格
ret = models.Book.objects.values('publishs_id').annotate(a=Avg('price'))
print(ret)  # <QuerySet [{'publishs_id': 1, 'a': 10.9}, {'publishs_id': 2, 'a': 200.0}, {'publishs_id': 3, 'a': 2.0}]>
# select avg(price) from app01_book group by publishs_id;
ret = models.Publish.objects.annotate(a=Avg('book__price')).values('name', 'a')
print(ret)  # <QuerySet [{'name': '老男人出版社', 'a': 10.9}, {'name': '妹妹出版社', 'a': 200.0}, {'name': '各个出版社', 'a': 2.0}]>

F查询

在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?我们在book表里面加上两个字段:评论数:commentNum,收藏数:KeepNum

  Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。

# F查询
from django.db.models import F, Q
# 点赞数大于评论数的数据
ret = models.Book.objects.filter(good__gt=F('comment'))
# 查询评论数大于收藏数2倍的书籍
Book.objects.filter(commentNum__lt=F('keepNum')*2)
#  修改操作也可以使用F函数,比如将每一本书的价格提高30元:
Book.objects.all().update(price=F("price")+30)

Q查询

# ` filter()` 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如`OR` 语句),你可以使用`Q 对象`。
from django.db.models import Q
Q(title__startswith='Py')
# Q 对象可以使用&(与) 、|(或)、~(非) 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。
bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))
# WHERE name ="yuan" OR name ="egon"

发表评论 取消回复

电子邮件地址不会被公开。

请输入正确格式的qq邮箱
请输入以http或https开头的URL,格式如:https://libo_sober.top