多表操作查询

1. 表关系models

from django.db import models

class Author(models.Model):
    """
    作者表
    """
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    sex = models.CharField(max_length=20, default='male')
    au = models.OneToOneField("AuthorDetail",on_delete=models.CASCADE)

    def __str__(self):
        return self.name

class AuthorDetail(models.Model):
    """
    作者详细信息表
    """
    birthday = models.DateField()
    telephone = models.CharField(max_length=11)
    addr = models.CharField(max_length=64)
    # class Meta:
        # db_table='authordetail' #指定表名
        # ordering = ['-id',]
    def __str__(self):
        return self.telephone + self.addr

class Publish(models.Model):
    """
    出版社表
    """
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    def __str__(self):
        return self.name

class Book(models.Model):
    """
    书籍表
    """
    title = models.CharField(max_length=32)
    publishDate = models.DateField()
    price = models.DecimalField(max_digits=5,decimal_places=2)
    comment = models.FloatField(default=100)  # 评论数
    good = models.FloatField(default=100)  # 点赞数
    # publishs=models.ForeignKey(to="Publish",on_delete=models.CASCADE 级联,related_name='xxx',related_query_name='xx' 设置别名,两者都存在,按后者)
    publishes = models.ForeignKey(to="Publish",on_delete=models.CASCADE,)
    authors = models.ManyToManyField(to='Author',)

    def __str__(self):
        return self.title

2. 增加

1. 一对一

# au_obj = models.AuthorDetail.objects.get(id=4)

models.Author.objects.create(
    name='海贼王',
    age=59,
    # 两种方式
    au_id=4   		# 以id形式添加
    # au=au_obj     # 以对象形式添加
)

2. 一对多

# pub_obj = models.Publish.objects.get(id=3)

models.Book.objects.create(
    title='xx2',
    price=13,

    publishDate='2011-11-12',
    # publishs=pub_obj , #类属性作为关键字时,值为model对象
    publishs_id=3  # 如果关键字为数据库字段名称,那么值为关联数据的值
)

3. 多对多

# ziwen = models.Author.objects.get(id=3)
# haigou = models.Author.objects.get(id=5)

new_obj = models.Book.objects.create(
    title='护理第二部',
    price=0.5,
    publishDate='2019-09-29',
    publishs_id=2,
)

new_obj.authors.add(3,5)  #  #*args  **kwargs
new_obj.authors.add(*[3,5])  # 用的最多,
new_obj.authors.add(ziwen, haigou)
new_obj.authors.set([1,3])
正向:对象.关联属性.set(列表)
反向:对象.表名小写_set.set(列表)

3. 删除

# 一对一
# models.AuthorDetail.objects.filter(id=3).delete()
# models.Author.objects.filter(id=3).delete()

# 一对多
# models.Publish.objects.filter(id=3).delete()
# models.Book.objects.filter(id=4).delete()

# 多对多
    book_obj = models.Book.objects.get(id=2)
    # book_obj.authors.add()  # 添加
    # book_obj.authors.remove(1)  #删除  2  1
    
    # book_obj.authors.clear()  # 清除 
    # book_obj.authors.set(['1','5'])  # 先清除再添加,相当于修改

注意:

对于ForeignKey对象,clear()和remove()方法仅在null=True时存在(一对多,多对一)

ForeignKey字段没设置null=True时,

class Book(models.Model):
    title = models.CharField(max_length=32)
    publisher = models.ForeignKey(to=Publisher)

没有clear()和remove()方法:

>>> models.Publisher.objects.first().book_set.clear()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: 'RelatedManager' object has no attribute 'clear'

当ForeignKey字段设置null=True时,

class Book(models.Model):
    name = models.CharField(max_length=32)
    publisher = models.ForeignKey(to=Class, null=True)

此时就有clear()和remove()方法:

>>> models.Publisher.objects.first().book_set.clear()

4. 改

ret = models.Publish.objects.get(id=2)
models.Book.objects.filter(id=5).update(
     # title='华丽丽',
     publishs=ret,
     # publishs_id=2,
)

5. 外键

    # 查询
    # 一对一
    # 关系属性写在表1,关联到表2,那么通过表1的数据去找表2的数据,叫做正向查询,返过来就是反向查询
    # 查询一下王洋的电话号码

    # 正向查询  对象.属性
    # obj = models.Author.objects.filter(name='王洋').first()
    # ph = obj.au.telephone
    # print(ph)

    # 查一下电话号码为120的作者姓名
    # 反向查询  对象.小写的表名
    # obj = models.AuthorDetail.objects.filter(telephone=120).first()
    # ret = obj.author.name  #陈硕
    # print(ret)

    # 一对多
    # 查询一下 海狗的怂逼人生这本书是哪个出版社出版的  正向查询
    # obj = models.Book.objects.filter(title='海狗的怂逼人生').first()
    # ret = obj.publishs.name
    # print(ret)  #24期出版社
    #  查询一下 24期出版社出版过哪些书
    # obj = models.Publish.objects.filter(name='24期出版社').first()
    #
    # ret = obj.book_set.all() #<QuerySet [<Book: 母猪的产后护理>, <Book: 海狗的怂逼人生>]>
    # for i in ret:
    #     print(i.title)

    # 多对多
    # 海狗的怂逼人生 是哪些作者写的 -- 正向查询
    # obj = models.Book.objects.filter(title='海狗的怂逼人生').first()
    # ret = obj.authors.all()
    #
    # print(ret)  #<QuerySet [<Author: 王洋>, <Author: 海狗>]>
    # for i in ret:
    #     print(i.name)

    # 查询一下海狗写了哪些书 -- 反向查询
    # obj = models.Author.objects.filter(name='海狗').first()
    # ret = obj.book_set.all()
    # print(ret)
    # for i in ret:
    #     print(i.publishs.name)
    #     print(i.title)
    # return HttpResponse('ok')

示例:

# 一对一
# 查询一下王洋的电话号码

# ret = models.Author.objects.filter(name='王洋').values('au__telephone')

# ret = models.AuthorDetail.objects.filter(author__name='王洋').values('telephone')
# print(ret) #<QuerySet [{'au__telephone': '110'}]> #<QuerySet [{'telephone': '110'}]>
#

# 一对多
# 海狗的怂逼人生这本书是哪个出版社出版的
# ret = models.Book.objects.filter(title='海狗的怂逼人生').values('publishs__name')
# print(ret) #<QuerySet [{'publishs__name': '24期出版社'}]>
# ret = models.Publish.objects.filter(book__title='海狗的怂逼人生').values('name')
# print(ret) #<QuerySet [{'name': '24期出版社'}]>


#查询一下24期出版社出版了哪些书
# ret = models.Publish.objects.filter(name='24期出版社').values('book__title')
# print(ret) #<QuerySet [{'book__title': '华丽的产后护理'}, {'book__title': '海狗的怂逼人生'}]>

# ret = models.Book.objects.filter(publishs__name='24期出版社').values('title')
# print(ret) #<QuerySet [{'title': '华丽的产后护理'}, {'title': '海狗的怂逼人生'}]>

# 多对多
#海狗的怂逼人生 是哪些作者写的
# ret = models.Book.objects.filter(title='海狗的怂逼人生').values('authors__name')
# print(ret)

# ret = models.Author.objects.filter(book__title='海狗的怂逼人生').values('name')
# print(ret) #<QuerySet [{'name': '王洋'}, {'name': '海狗'}]>
# return render(request,'index.txt',{'x':123,'y':456})


# related_name
# 查询一下24期出版社出版了哪些书
ret = models.Publish.objects.filter(name='24期出版社').values('xxx__title') #xxx代替反向查询的小写表名
print(ret)

聚合查询 [`æɡrɪɡeɪt]

ret = models.Book.objects.all().aggregate(a=Avg('price'),m=Max('price'))
print(ret) 
有别名
#{'a': 45.1, 'm': Decimal('200.00')} 
无别名
#{'price__avg': 45.1, 'price__max': Decimal('200.00')} 
# python字典格式,也就是说,聚合查询是orm语句的结束

分组查询

    # 每个出版社出版的书的平均价格
    # 用的是publish表的id字段进行分组
    # ret = models.Book.objects.values('publishs__id').annotate(a=Avg('price'))
    # 用的book表的publishs_id字段进行分组
    # ret = models.Book.objects.values('publishs_id').annotate(a=Avg('price'))
    # print(ret)
    # ret = models.Publish.objects.annotate(a=Avg('book__price')).values('a')
    # print(ret) #<QuerySet [{'a': None}, {'a': 71.166667}, {'a': 6.0}]>

F查询

from django.db.models import Avg, Sum, Max, Min, Count,F
# 查询一下评论数大于点赞数的书
ret = models.Book.objects.filter(comment__gt=F('good'))
print(ret)

# 将所有书的价格上调100块
models.Book.objects.all().update(
     price=F('price')+100
)
 
# 引申 同时在char字段后边添加文字
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.all().update(title=Concat(F("title"), Value("("), Value("第一版"), Value(")")))

Q查询

与 & 或 | 非 ~

from django.db.models import Avg, Sum, Max, Min, Count, F,Q
ret = models.Book.objects.filter(Q(id=2)&Q(Q(price__gt=112)|~Q(comment__lte=200)))
print(ret)

views

from django.shortcuts import render,HttpResponse,redirect
from app01 import models
from django.db.models import Avg,Sum,Max,Min,Count,F,Q
# Create your views here.

def index(request):

    #1.增加 一对一
    # models.AuthorDetail.objects.create(
    #     birthday="2000-11-11",
    #     telephone="555",
    #     addr="美国",
    # )

    # au_obj = models.AuthorDetail.objects.get(id=5)

    # models.Author.objects.create(
    #     name="海王",
    #     age=66,
    #     au_id=5,
    #     #或者 au="au_obj"
    # )

    # 2. 一对多 书籍和出版社
    # pub_obj = models.Publish.objects.get(id=3)
    #
    # models.Book.objects.create(
    #     title="xx2",
    #     price=33,
    #     publishDate="2011-11-12",
    #     comment=324,
    #     good=444,
    #     publishes=pub_obj, #类属性作为关键字时,值为model对象
    #     # publishes_id=3, # 如果关键字为数据库字段名称,那么值为关联数据的值
    # )

    # 3. 多对多 书籍和作者
    # sese = models.Author.objects.get(id=1)
    # xingxing = models.Author.objects.get(id=2)
    #
    # new_obj = models.Book.objects.create(
    #     title="海贼王",
    #     price="45",
    #     publishDate="2009-11-14",
    #     comment=399,
    #     good=666,
    #     publishes_id=2,
    # )

    # 三种方法
    # new_obj.authors.add(*[1,2]) #用的最多
    # new_obj.authors.add(1,2)
    # new_obj.authors.add(sese,xingxing)

    # 4. 删除 一对一
    # models.AuthorDetail.objects.filter(id=5).delete()
    # models.Author.objects.filter(id=4).delete()

    # 5.  删除 一对多
    # models.Publish.objects.filter(id=4).delete()
    # models.Book.objects.filter(id=4).delete()

    # 6.删除 多对多
    # book_obj = models.Book.objects.get(id=8)
    # # book_obj.authors.add() #添加
    # book_obj.authors.remove(1)  # 删除作者1,书籍8

    # book_obj.authors.clear()  #清除关系
    # book_obj.authors.set(["3","4"])  #先清除再添加

    # 7. 改 修改书籍名和出版社
    # ret = models.Publish.objects.get(id=2)
    # models.Book.objects.filter(id=7).update(
    #     title="ooo",
    #     publishes=ret,
    #     # publishes_id=2
    # )

    # 8. 基于对象的跨表查询 一对一
    # 关系属性写在表1,关联到表2,那么通过表1的数据去找表2的数据,叫做正向查询,返过来就是反向查询
    # 正向查询用对象.属性,反向查询用对象.小写的表名

    # 1.查询韩星的电话号 str原因
    # obj = models.Author.objects.filter(name="韩星").first()
    # print(type(obj))
    # ph = obj.au.telephone
    # print(ph)

    # 反向查询已知电话444,查作者
    # obj = models.AuthorDetail.objects.filter(telephone=444).first()
    # pa = obj.author.name
    # print(pa)

    # 9. 基于对象的跨表查询 一对多
    # 已知书籍名,查询出版社,正向
    # obj = models.Book.objects.filter(title="大主宰").first()
    # ph = obj.publishes.name
    # print(ph)

    # 已知出版社,查询所有书籍,反向,对象.表名_set.all()
    # obj = models.Publish.objects.filter(name="24期出版社").first()
    # ret = obj.book_set.all() # <QuerySet [<Book: 风云>, <Book: 莽荒纪>, <Book: ooo>, <Book: 海贼王>]>
    # print(ret)
    # for i in ret:
    #     print(i.title)

    # 10.多对多
    # 已知书籍名,查询作者,正向查询
    # obj = models.Book.objects.filter(title="万剑道尊").first()
    # ret = obj.authors.all() #<QuerySet [<Author: 邸宗超>, <Author: 韩星>, <Author: 李文宝>]>
    # for i in ret:
    #     print(i.name)
    #     print(i.au.telephone)  #连环查询,有对象,正向查询

    # 已知作者名,查看书籍
    # obj = models.Author.objects.filter(name="韩星").first()
    # ret = obj.book_set.all()
    # print(ret)  #<QuerySet [<Book: 朝花夕拾>, <Book: 浮云>, <Book: 大主宰>, <Book: 万剑道尊>]>
    # for i in ret:
    #     print(i.title)

    # 11. 基于双下划线的跨表查询
    # 一对一 正向
    # 已知作者名,查询电话号
    # ret1 = models.Author.objects.filter(name="韩星").values("au__telephone")
    # print(ret1)  #<QuerySet [{'au__telephone': '222'}]>
    # ret2 = models.AuthorDetail.objects.filter(author__name="韩星").values("telephone")
    # print(ret2)  #<QuerySet [{'telephone': '222'}]>
    # for i in ret1:
    #     print(i["au__telephone"])
    # for j in ret2:
    #     print(j["telephone"])

    # 一对多
    # 已知书名查询出版社
    # ret1 = models.Book.objects.filter(title="大主宰").values("publishes__name")
    # print(ret1)  # <QuerySet [{'publishes__name': '老男人出版社'}]>
    # ret2 = models.Publish.objects.filter(book__title="大主宰").values("name")
    # print(ret2)  # <QuerySet [{'name': '老男人出版社'}]>
    # for i in ret1:
    #     print(i["publishes__name"])
    # for j in ret2:
    #     print(j["name"])

    # 已知出版社,查询书籍名
    # ret1 = models.Publish.objects.filter(name="24期出版社").values("book__title")
    # print(ret1)
    # ret2 = models.Book.objects.filter(publishes__name="24期出版社").values("title")
    # print(ret2)
    # for i in ret1:
    #     print(i["book__title"])
    # for j in ret2:
    #     print(j["title"])

    # 多对多
    # 已知书籍名,查询作者
    # ret1 = models.Book.objects.filter(title="万剑道尊").values("authors__name")
    # print(ret1)
    # ret2 = models.Author.objects.filter(book__title="万剑道尊").values("name")
    # print(ret2)
    # for i in ret1:
    #     print(i["authors__name"])

    # 12. 聚合查询
    # 统计所有书籍的平均价格,别名可以不写
    # ret1 = models.Book.objects.all().aggregate(a=Avg("price"),m=Max("price"))
    # print(ret1)

    # 每个出版社出版书的平均价格
    # 1.根据publish表的id字段进行分组,别名可以不写
    # ret1 = models.Book.objects.values("publishes__id").annotate(a=Avg("price"))
    # print(ret1)
    # 2. 根据book的publishs_id字段进行分组,等同于publishes
    # ret2 = models.Book.objects.values("publishes_id").annotate(a=Avg("price"))
    # print(ret2)
    # ret3 = models.Book.objects.values("publishes","id").annotate(a=Avg("price"))
    # print(ret3) #多条件分组

    # 此方法连表查询(book__price),查询的值是a,默认用publish的id分组
    # ret4 = models.Publish.objects.annotate(a=Avg("book__price")).values("a")
    # print(ret4)

    # F查询
    #查询一下评论数大于点赞数的书
    # ret = models.Book.objects.all()
    # l1 = []
    # for i in ret:
    #     if i.comment > i.good:
    #         l1.append(i)
    # for j in l1:
    #     print(j.title)

    # 双下方法
    # ret = models.Book.objects.filter(comment__gt=F("good"))
    # print(ret)
    # for i in ret:
    #     print(i.title)

    #将所有的书籍价格上调100块
    # ret = models.Book.objects.all()
    # for i in ret:
    #     i.price += 100
    #     i.save()

    # models.Book.objects.all().update(
    #     price=F("price") - 100
    # )

    # Q查询 或 |   与 &   非~
    # 查询书籍价格大于80并且评论数大于等于200
    # ret = models.Book.objects.filter(price__gt=80,comment__gte=200) #和,
    # print(ret)

    # 查询书籍价格大于80或者评论数大于等于600
    # ret = models.Book.objects.filter(Q(price__gt=80)|Q(comment__gte=600))
    # print(ret)

    #查询id=2,书籍价格大于80或者评论数大于等于600

    # ret = models.Book.objects.filter(Q(Q(price__gt=80)|Q(comment__gte=600))&Q(id=2))
    # print(ret)

    # 1 查询每个作者的姓名以及出版的书的最高价格
    # ret1 = models.Author.objects.values("name").annotate(m=Max("book__price"))
    # print(ret1)

    # ret2 = models.Book.objects.values("authors__name").annotate(Max("price"))
    # print(ret2) #有点小错误

    # 2 查询作者id大于2作者的姓名以及出版的书的最高价格

    # ret1 = models.Author.objects.filter(id__gt=2).annotate(m=Max("book__price")).values("name","m")
    # print(ret1)

    # 3 查询作者id大于2或者作者年龄大于等于20岁的女作者的姓名以及出版的书的最高价格
     # 不对,等于不用Q,直接逗号    # ret1 = models.Author.objects.filter(Q(id__gt=2)|Q(age__gte=20)&Q(sex="female")).annotate(m=Max("book__price")).values("sex","m")
    # ret1 = models.Author.objects.filter(Q(id__gt=2)|Q(age__gte=20),sex="female").annotate(m=Max("book__price")).values("sex","m")
    # print(ret1)

    # 4 查询每个作者出版的书的最高价格的平均值
    # ret1 = models.Author.objects.values("name").annotate(m=Max("book__price")).aggregate(a=Avg('m'))
    # # ret1 = models.Author.objects.annotate(m=Max("book__price")).aggregate(a=Avg('m')) # 默认id分组
    # print(ret1)

    # 5 每个作者出版的所有书的最高价格以及最高价格的那本书的名称
    ret1 = models.Author.objects.annotate(m=Max("book__price")).values("name","m")
    print(ret1)
    return HttpResponse("OK")

将orm转换为sql语句

# 将下述代码放入settings中
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

事务

from django.db import transaction

# 注意try的位置是在事物的外部,在内部时报错前会对数据库进行修改,不符合事物的特点
try:
    with transaction.atomic():
        # 一系列的操作
        models.Book.objects.update(publisher_id=4)
        models.Book.objects.update(publisher_id=3)
        int('ss')
        models.Book.objects.update(publisher_id=2)
        models.Book.objects.update(publisher_id=5)

except Exception as e:
    print(e)

在Python脚本中调用Django环境

1. 在项目下创建文件夹,并创建py文件
2.输入以下代码
import os   # 将数据库路径导入
# 外部文件执行数据库命令,注意修改路径
# os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "homework.settings")

import django  # 导入django

django.setup()   # 启动django运行程序

from app01 import models  # 导入models模块

# 写逻辑代码查询即可,已连接了Django环境

关于别名

publishers = models.ForeignKey(to="Publisher",related_name='books',related_query_name='xx')
关联字段中有此属性时,models.Publisher.objects.filter(xx__title="谁是谁")用到表名小写时替换,优先级
related_query_name 》 related_name 》 表名小写       author__title
posted @ 2019-11-04 21:34  lvweihe  阅读(116)  评论(0编辑  收藏  举报