PyMySQL高级多表关联查询

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/geek64581/article/details/102716644

PyMySQL高级多表关联查询

第一章 模型类添加关联表

1.1 模型类

添加HeroInfo模型类,外键关联BookInfo

class HeroInfo(models.Model):
    GENDER_CHOICES = (
        (0, 'female'),
        (1, 'male')
    )
    hname = models.CharField(max_length=20, verbose_name='名称')
    hgender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别')

    hbook = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name='图书')  # 外键

    class Meta:
        db_table = 'tb_heros'


    def __str__(self):
        return self.hname

1.2 外键

在设置外键时,需要通过on_delete选项指明主表删除数据时,对于外键引用表数据如何处理,在django.db.models中包含了可选常量:

  • CASCADE 级联,删除主表数据时连通一起删除外键表中数据
  • PROTECT 保护,通过抛出ProtectedError异常,来阻止删除主表中被外键应用的数据
  • SET_NULL 设置为NULL,仅在该字段null=True允许为null时可用
  • SET_DEFAULT 设置为默认值,仅在该字段设置了默认值时可用
  • SET() 设置为特定值或者调用特定方法,如
    from django.conf import settings
    from django.contrib.auth import get_user_model
    from django.db import models
    
    def get_sentinel_user():
        return get_user_model().objects.get_or_create(username='deleted')[0]
    
    class MyModel(models.Model):
        user = models.ForeignKey(
            BookInfo,
            on_delete=models.SET(get_sentinel_user),
        )
    
  • DO_NOTHING 不做任何操作,如果数据库前置指明级联性,此选项会抛出IntegrityError异常

1.3 添加测试数据

insert into tb_heros(hname,hgender, hbook_id) values
('曹操',1, 1),
('刘备',1, 1),
('孙权',1, 1),
('诸葛亮',1, 1),
('孙悟空',1, 2),
('猪八戒',1, 2),
('唐僧',1, 2),
('沙僧',1, 2),
('白龙马',1, 2),
('宋江',1, 3),
('卢俊义',1, 3),
('李逵',1, 3),
('高俅',1, 3),
('高俅',1, 3),
('郭靖',1,4),
('黄蓉',0,3),
('黄药师',1,4),
('欧阳锋',1,4),
('乔峰',1,5),
('段誉',1,5),
('虚竹',1,5),
('王语嫣',0,5),
('王语嫣',0,6),
('令狐冲',1,6),
('任盈盈',0,6),
('岳不群',1,6),
('东方不败',0,6),
('胡斐',1,7),
('苗若兰',0,7),
('程灵素',0,7),
('袁紫衣',0,7);

第二章 多表关联查询

2.1 关联查询

由多到一的(有外键):

多对应的模型类对象.多对应的模型类中的关系类属性名

例:

h = HeroInfo.objects.get(id=1)
h.hbook

访问一对应的模型类关联对象的id语法:

多对应的模型类对象.关联类属性_id

例:

h = HeroInfo.objects.get(id=1)
h.hbook_id

由一到多的(无外键关联):

一对应的模型类对象.多对应的模型类名小写_set

例:

b = BookInfo.objects.get(id=1)
b.heroinfo_set.all()

2.2 related_name 反向关联操作

如果定义外键的时候指定related_name 比如

hbook = models.ForeignKey(BookInfo, related_name="heros", on_delete=models.CASCADE, )

那么反向关联查询的时候可以直接使用related_name 例如 b.heroinfo_set.all() 可以替换成 b.heros.all()

2.3 关联过滤查询

由一模型类条件查询多模型类数据:

语法如下:

一模型类关联属性名__一模型类属性名__条件运算符=值

注意:如果没有"__运算符"部分,表示等于。

例:

查询书名为“天龙八部”的所有英雄。

HeroInfo.objects.filter(hbook__btitle=‘天龙八部’)

由多模型类条件查询一模型类数据:

语法如下:

关联模型类名小写__属性名__条件运算符=值

注意:如果没有"__运算符"部分,表示等于。

例:

查询图书,要求图书英雄为"孙悟空"

BookInfo.objects.filter(heroinfo__hname=‘孙悟空’)

第三章 查询集QuerySet

3.1 概念

Django的ORM中存在查询集的概念。

查询集,也称查询结果集、QuerySet,表示从数据库中获取的对象集合。

当调用如下过滤器方法时,Django会返回查询集(而不是简单的列表):

  • all():返回所有数据。
  • filter():返回满足条件的数据。
  • exclude():返回满足条件之外的数据。
  • order_by():对结果进行排序。

对查询集可以再次调用过滤器进行过滤,如

BookInfo.objects.filter(bread__gt=30).order_by(‘bread’)

也就意味着查询集可以含有零个、一个或多个过滤器。过滤器基于所给的参数限制查询的结果。

从SQL的角度讲,查询集与select语句等价,过滤器像where、limit、order by子句。

判断某一个查询集中数量和是否有数据:

  • count(): 判断查询集里数量
  • exists():判断查询集中是否有数据,如果有则返回True,没有则返回False。

3.2 两大特性

1)惰性执行

创建查询集不会访问数据库,直到调用数据时,才会访问数据库,调用数据的情况包括迭代、序列化、与if合用

例如,当执行如下语句时,并未进行数据库查询,只是创建了一个查询集qs

qs = BookInfo.objects.all()

继续执行遍历迭代操作后,才真正的进行了数据库的查询

for book in qs:
    print(book.btitle)

2)缓存

使用同一个查询集,第一次使用时会发生数据库的查询,然后Django会把结果缓存下来,再次使用这个查询集时会使用缓存的数据,减少了数据库的查询次数。缓存的数据保存在querset._result_cache里
在这里插入图片描述
情况一:如下是两个查询集,无法重用缓存,每次查询都会与数据库进行一次交互,增加了数据库的负载。

from booktest.models import BookInfo
[book.id for book in BookInfo.objects.all()]
[book.id for book in BookInfo.objects.all()]

情况二:经过存储后,可以重用查询集,第二次使用缓存中的数据。

qs=BookInfo.objects.all()
[book.id for book in qs]
[book.id for book in qs]

3.3 限制查询集

可以对查询集进行取下标或切片操作,等同于sql中的limit和offset子句。

注意:不支持负数索引。

对查询集进行切片后返回一个新的查询集,不会立即执行查询。

如果获取一个对象,直接使用[0],等同于[0:1].get(),但是如果没有数据,[0]引发IndexError异常,[0:1].get()如果没有数据引发DoesNotExist异常。

-- 分页
	-- limit start, count
	-- limit 放在最后面(注意)

	起始位置 : (页数-1)*每一页个数
	
	-- 限制查询出来的数据个数
	-- 查询前5个数据
	select * from students limit 0,5; 

示例:获取第1、2项,运行查看。

qs = BookInfo.objects.all()[0:2]

第四章 综合案例

功能:展示某一本书籍里的所有英雄人物信息

技术点:关联查询 + 路径传参(或者查询字符串传参)
在这里插入图片描述
步骤:

  • 图书名称显示的时候更换成a标签,链接指向/books/heros?id=
{% extends 'base.html' %}


{% block content %}
    <div>
        <table id="customers">
            <tr>
                <th>序号</th>
                <th>名称</th>
                <th>作者</th>
            </tr>

{#            开始展示图书#}
            {% for book in books %}
                <tr>
                    <td>{{ loop.index }}</td>
                    # 图书名称显示的时候更换成a标签,链接指向/books/heros?id=
                    <td><a href="/books/heros?id={{ book.id }}">{{ book.btitle }}</a></td>
                    <td>{{ book.author }}</td>
                </tr>
            {% endfor %}
        </table>
    </div>
{% endblock %}
  • 后端收到这个请求,获取参数比如id=1, 找到对应的图书,找到关联的英雄对象
  • 在模板里遍历展示英雄信息(继承模板)

代码: 视图

# views.py
def heros(request):
    # 得到QueryDict
    qd = request.GET

    book_id = qd.get('id')

    # 获取图书
    book = BookInfo.objects.get(id=book_id)

    # 获取这本书了的英雄
    hero_list = book.heros.all()

    # 渲染一个html, 传递参数
    context = {
        "heros": hero_list,
    }
    return render(request, 'heros.html', context)

路由

# urls.py
url(r'^heros', views.heros),

模板heros.html

{% extends 'base.html' %}

{% block content %}
    <div>
        <table id="customers">
            <tr>
                <th>序号</th>
                <th>英雄名称</th>
                <th>性别</th>
            </tr>


            {% for hero in heros %}
                <tr>
                    <td>{{ loop.index }}</td>
                    <td>{{ hero.hname }}</td>
                    <td>{{ hero.hgender }}</td>
                </tr>
            {% endfor %}
        </table>
    </div>
{% endblock %}

猜你喜欢

转载自blog.csdn.net/geek64581/article/details/102716644
今日推荐