第七章续集笔记

接着第七章的数据查询

这里写数据‘排除’查询
在SQL语句中是 NOT
这里是

在Q查询前使用~即可

# sql语句:SELECT * FROM index_vocation where not(job='网站设计')
In [4]: from django.db.models import Q

In [5]: v = Vocation.objects.filter(~Q(job='网站设计'))

In [6]: v
Out[6]: <QuerySet [<Vocation: 2>, <Vocation: 5>, <Vocation: 7>, <Vocation: 8>, <Vocation: 11>, <Vocation: 12>]>

In [7]: v[3].job
Out[7]: 'zz'

还可以使用exclude实现不等于查询

In [8]: v = Vocation.objects.exclude(job='网站设计')

In [9]: v
Out[9]: <QuerySet [<Vocation: 2>, <Vocation: 5>, <Vocation: 7>, <Vocation: 8>, <Vocation: 11>, <Vocation: 12>]>

In [10]: v[3].job
Out[10]: 'zz'

使用count方法统计查询数据的数据量

In [12]: v = Vocation.objects.filter(job='网站设计').count()

In [13]: v
Out[13]: 1

去重查询

distinct方法无需设置参数,去重方法根据values设置的字段执行
sql语句:select distinct job from index_vocation where job=''网站设计

In [16]: v = Vocation.objects.values('job').filter(job='网站设计').distinct()

In [17]: v
Out[17]: <QuerySet [{'job': '网站设计'}]>

In [18]: v = Vocation.objects.values('job').filter(job='网站设计')

In [19]: v
Out[19]: <QuerySet [{'job': '网站设计'}]>

更具字段id降序排列,降序只要在order_by里面的字段前面加'-'即可

order_by还可设置多字段排列,在它的参数中传入多个字段即可

In [21]: v = Vocation.objects.order_by('-id')

In [22]: v
Out[22]: <QuerySet [<Vocation: 12>, <Vocation: 11>, <Vocation: 8>, <Vocation: 7>, <Vocation: 5>, <Vocation: 3>, <Vocation: 2>]>

聚合查询(不理解实现)

主要是下方的sum何用;求和,对job进行group by 分组(sql语句,django的实现更简洁)
可参考该资料https://www.cnblogs.com/jingfengling/p/5962182.html
实现对数据值求和,求平均值等。由annotate和aggregate方法实现

annotate类似于SQL里面的GROUP BY方法

如果不设置values,默认对主键进行GROUP BY分组

# sql:select job,sum(id) as 'id_sum' from index_vocation group by job
In [23]: from django.db.models import Sum,Count

In [24]: v = Vocation.objects.values('job').annotate(Sum('id'))

In [25]: print(v.query)
SELECT "index_vocation"."job", SUM("index_vocation"."id") AS "id__sum" FROM "index_vocation" GROUP BY "index_vocation"."job"

aggregate是计算某个字段的值并只返回计算结果

# sql: select count(id) as 'id_count' from index_vocation
In [35]: v = Vocation.objects.aggregate(id_count=Count('id'))

In [36]: v
Out[36]: {'id_count': 7}

In [37]: v['id_count']
Out[37]: 7

union、intersection和difference语法

每次查询结果的字段必须相同,第一次查询为v1,第二次为v2

In [48]: v1 = Vocation.objects.filter(payment=6666)

In [49]: v1
Out[49]: <QuerySet [<Vocation: 2>, <Vocation: 5>]>

In [50]: v2 = Vocation.objects.filter(payment=6667)

In [51]: v2
Out[51]: <QuerySet [<Vocation: 3>, <Vocation: 7>, <Vocation: 8>, <Vocation: 11>, <Vocation: 12>]>

使用sql的union来组合两个或多个查询结果的并集
获取两次查询结果的并集

In [52]: v1.union(v2)
Out[52]: <QuerySet [<Vocation: 2>, <Vocation: 3>, <Vocation: 5>, <Vocation: 7>, <Vocation: 8>, <Vocation: 11>, <Vocation: 12>]>

使用sql的intersect来获取两个或多个查询结果的交集
获取两次查询结果的交集

In [53]: v1.intersection(v2)
Out[53]: <QuerySet []>

使用sql语句的except来获取两个或多个查询结果的差
以v2为共同数据,去除v1和v2的共同数据

In [54]: v2.difference(v1)
Out[54]: <QuerySet [<Vocation: 3>, <Vocation: 7>, <Vocation: 8>, <Vocation: 11>, <Vocation: 12>]>

若想使用不等于,大于等模糊的查询,可以使用如下的匹配符
只要在查询字段的末端使用相应的匹配符,就能实现不同的数据查询方式

匹配符

可作用在filter和get上

__exact 精确等于,如sql的like'xxx'
__iexact 精确等于并忽略大小写
__contains 模糊匹配,如sql的like'%荣耀%'
__icontains 模糊匹配,忽略大小写
__gt 大于
__gte 大于等于
__lt 小于
__lte小于等于
__in 判断是否在列表内
startswith 以...开头
istartwith 以...开头并忽略大小写
__endswith 以...结尾
__iendswith 以...结尾并忽略大小写
__range 在...范围内
__year 日期字段的年份
__month 日期字段的月份
__day 日期字段的天数
__isnull 判断是否为空

查询小于等于payment为6667的数据


In [56]: v2 = Vocation.objects.filter(payment__lte=6667)

In [57]: v2
Out[57]: <QuerySet [<Vocation: 2>, <Vocation: 3>, <Vocation: 5>, <Vocation: 7>, <Vocation: 8>, <Vocation: 11>, <Vocation: 12>]>

多表查询

正向查询

一对多里面的多的外键查询


In [65]: v = Vocation.objects.filter(id=2).first()

In [66]: v
Out[66]: <Vocation: 2>
## 通过外键name去查询模型PersonInfo所对应的数据
In [67]: v.name.hireDate
Out[67]: datetime.date(2018, 9, 18)

反向查询

查询主体是一对多模型中的一,通过一查询到与他关联到的模型多的数据
查询模型PersonInfo某行数据对象p
这里一对多数据库一为 PersonInfo,多为Vocation

In [68]: p = PersonInfo.objects.filter(id=2).first()

In [69]: p
Out[69]: <PersonInfo: Tim>
# 方法一
#vocation_set的返回值为queryset对象,即查询结果
# vocation_set为模型Vocation的名称小写
# 模型Vocation的外键字段name不能设置参数related_name
# 若设置参数related_name,则无法使用vocation_set
In [70]: v= p.vocation_set.first()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-70-ff74ffcfbda9> in <module>
----> 1 v= p.vocation_set.first()

AttributeError: 'PersonInfo' object has no attribute 'vocation_set'
## 方法二
# 由模型Vocation的外键字段name的参数related_name实现
# 外键字段name必须设置参数related_name才有效,否则无法查询。
# 将外键字段name的参数related_name设为personinfo
In [76]: p = PersonInfo.objects.filter(id=4).first()

In [77]: v= p.personinfo.first()

In [78]: v
Out[78]: <Vocation: 3>

In [79]: v.job
Out[79]: '网站设计'

正向查询和反向查询还可在查询条件(filter,get)里使用,无论是正向查询还是反向查询,他们在数据库里需要执行两次SQL查询,第一次是查询某张数据表的数据,再通过外键关联获取另外一张数据表的数据信息。
为了减少查询次数,提高查询效率,
可以使用select_related或prefetch_related方法实现,该方法只需执行依次SQL查询就能实现多表查询。

select_related方法

执行依次SQL查询就能实现多表查询。

#select_related,针对一对一或一对多,使用sql的join语句进行优化的
通过减少sql查询的次数来进行优化和提高性能
# 以PersonInfo为查询对象
# 使用left outer join方式查询两个数据表
# 查询模型PersonInfo的字段name和模型Vocation的字段payment
# select_related参数为personinfo,代表外键字段name的参数related_name
# 若要得到其他数据表的关联数据,则可用双下划线'__'连接字段名
# 双下划线连接字段名必须是外键字段名或外键字段参数related_name
In [92]: p = PersonInfo.objects.select_related('personinfo').values('name','personinfo__payment')

In [93]: print(p.query)
SELECT "index_personinfo"."name", "index_vocation"."payment" FROM "index_personinfo" LEFT OUTER JOIN "index_vocation" ON ("index_personinfo"."id" = "index_vocation"."name_id")

# 可以看下p的类型
In [94]: p = PersonInfo.objects.select_related('personinfo')

In [95]: print(type(p))
<class 'django.db.models.query.QuerySet'>
In [96]: p.values('name','personinfo__payment') # 参数一为一中的此段,参数二为多中的字段,要加双下划线(一对多数据库)
Out[96]: <QuerySet [{'name': 'Lucy', 'personinfo__payment': 6666}, {'name': 'Tim', 'personinfo__payment': None}, {'name': 'Mary', 'personinfo__payment': 6667}, {'name': 'Mary', 'personinfo__payment': 6667}, {'name': 'Mary', 'personinfo__payment': 6667}, {'name': 'Mary', 'personinfo__payment': 6667}, {'name': 'Mary', 'personinfo__payment': 6667}, {'name': 'Tony', 'personinfo__payment': 6666}]>
以模型Vocation为查询对象

select_related使用INNER JOIN 方式查询两个数据表
select_related的参数为name,代表外键字段name

In [98]: v = Vocation.objects.select_related('name').values('name','name__age')

In [99]: v
Out[99]: <QuerySet [{'name': 1, 'name__age': 20}, {'name': 4, 'name__age': 24}, {'name': 4, 'name__age': 24}, {'name': 4, 'name__age': 24}, {'name': 4, 'name__age': 24}, {'name': 4, 'name__age': 24}, {'name': 5, 'name__age': 25}]>
In [101]: print(v.query)
SELECT "index_vocation"."name_id", "index_personinfo"."age" FROM "index_vocation" INNER JOIN "index_personinfo" ON ("index_vocation"."name_id" = "index_personinfo"."id")

select_related还支持3个或3个以上的数据表同时查询

就是通关外键两两关联
这样的查询都是通过一张表的数据,查询另外一张表的数据
本例中的模型关系,模型Person通过外键living关联模型City,模型City通过外键province关联模型Province,从而使3个模型形成一种递进关系。
下方的参数说明:
···
living是模型Person的外键字段,该字段指向模型City
province是模型City的外键字段,该字段指向模型Province
···

In [107]: P = Person.objects.select_related('living__province').get(name='Lucy')

In [108]: P.living.province
Out[108]: <Province: 浙江省>

是分别查询每张数据表,然后由python语法来处理他们之间的关系,对于多对多查询,prefetch_related更有优势
而select_related是由SQL语句的join实现的,使用会增加数据查询时间和内存占用。
本例是创建两个模型分别为performer和program,这里面分别添加人员信息和节目信息,然后设置两表为多对多就会生成一张多对多字段的表,该表名字是设置多对多的那张表名_多对多的外键字段名(_是连接两个字段名),该表的字段如何设置不清楚;是用他们的主键做关联,数据好像也要自己插入
代码如下,不知为何打印出所有的数据
这应该想要的是查询喜洋洋扮演演员有多少个把,这里查询出来了,performer所对应的是演员人名,再多对多表中program为节目名称,program_id与performer_id一一对应,这里只取了喜洋洋的id1对应了以他performer表的所有id

# 查询模型Program的某行数据
In [109]: p = Program.objects.prefetch_related('performer').filter(name='喜洋洋').first()

# 根据外键字段performer获取当前数据的多对多多线
In [110]: p.performer.all()
Out[110]: <QuerySet [<Performer: Lily>, <Performer: Lilei>, <Performer: Tom>, <Performer: Hanmei>]>

In [111]: p
Out[111]: <Program: 喜洋洋>

In [112]: p.performer
Out[112]: <django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager at 0x1d2ad52ca20>

In [113]: p.performer.first()
Out[113]: <Performer: Lily>

多对多的表在数据库中查找不出来

NameError: name 'people_name_many_to_many' is not defined

第七章剩余部分

extraSQL语句的执行方法

源码位置:django/db/models/query.py
适用于ORM难以实现的查询条件,将查询条件使用原生SQL语法实现,此方法需要模型对象,在某种程度上可防止SQL注入。
该字段共有6个参数

order_by:设置数据的排序方式
 where设置查询条件
params为where提供数值(where设置了字符串格式化%s)
select 新增查询字段
select_params为select的%s提供数值
table 连接其他数据表,可实现多表查询

where和params参数的使用

# where设置查询条件,params为where提供数值(where设置了字符串格式化%s)
In [3]: Vocation.objects.extra(where=['job=%s'],params=['网站设计'])
Out[3]: <QuerySet [<Vocation: 3>]>

select,select_params

# 新增查询字段select,select_params为select的%s提供数值
In [5]: v = Vocation.objects.extra(select={'seat':'%s'},select_params=['seatInfo']) 

In [6]: print(v.query)
SELECT (seatInfo) AS "seat", "index_vocation"."id", "index_vocation"."job", "index_vocation"."title", "index_vocation"."payment", "index_vocation"."name_id" FROM "index_vocation"

tables连接其他数据表,可实现多表查询

# 连接其他数据表
In [7]: v = Vocation.objects.extra(tables=['index_personinfo'])

In [8]: print(v.query)
SELECT "index_vocation"."id", "index_vocation"."job", "index_vocation"."title", "index_vocation"."payment", "index_vocation"."name_id" FROM "index_vocation" , "index_personinfo"

raw的功能和extra相同,实现数据库查询

raw源码位置在:django/db/models/query.py
字段参数

raw_quert是必选参数,sql语句
parames:如果raw_quert设置字符串格式化%s,那么该参数为raw_quert提供值
translations:为查询的字段设置别名
using:数据库对象,即Django所连接的数据库

raw_query

raw_quert是必选参数,sql语句

In [9]: v = Vocation.objects.raw('select * from index_vocation')

In [10]: v[0]
Out[10]: <Vocation: 2>

execute语法

它执行sql语句无需经过Django的ORM框架,他连接数据库类似于使用第三方模块mysqlclient,可通过游标的方式来执行sql语句

# 导入连接的模块
In [25]: from django.db import connection
# 产生游标
In [26]: cursor = connection.cursor()
# 执行sql语句
In [27]: cursor.execute('select * from index_vocation')
Out[27]: <django.db.backends.sqlite3.base.SQLiteCursorWrapper at 0x13607385dc8>
# 读取第一行数据
In [28]: cursor.fetchone()
Out[28]: (2, '文员', '前台文员', 6666, 1)
# 读取全部数据
In [29]: cursor.fetchall()
Out[29]: 
[(3, '网站设计', '前端开发', 6667, 4),
 (5, '项目经理', '项目负责人', 6666, 5),
 (7, 'jj', 'hh', 6667, 4),
 (8, 'zz', 'java', 6667, 4),
 (11, 'aa1', 'ss1', 6667, 4),
 (12, 'aa2', 'ss2', 6667, 4)]

execute能够执行所有的sql语句,但很容易受到sql注入攻击,一般不建议使用该方法实现数据操作。

数据库事务

django的事务定义在transaction.py文件中。
该文件定义了两个类和16个函数方法
开发中常用的函数方法如下:
atomic() :在视图函数视图类中使用事务
savepoint():开启事务
savepoint_rollback():回滚事务
savepoint_commit():提交事务

实例views.py

from django.shortcuts import render
from .models import *
from django.db import transaction
from django.db.models import F

@transaction.atomic # 使函数支持事务操作
def index(request):
    # 开启事务保护
    sid = transaction.savepoint()
    try:
        id = request.GET.get('id', '') # ?/id=xxx,这样就可以在url中传入参数了
        if id:
            v = Vocation.objects.filter(id=id) # 提取Url中输入的id
            v.update(payment=F('payment') + 1) # 更新payment加1
            print('Done') # 执行语句在控制台会输出啊
            # 提交事务
            # 如不设置,当程序执行完成后,会自动提交事务
            # transaction.savepoint_commit(sid)
        else:
            # 全表的payment字段自减1
            Vocation.objects.update(payment=F('payment') - 1)
            # 事务回滚,将全表payment字段自减1的操作撤回
            transaction.savepoint_rollback(sid)
    except Exception as e:
        # 事务回滚
        transaction.savepoint_rollback(sid)
    return render(request, 'index.html', locals())

使用with模块实现事务操作

除了使用装饰器@transaction.atomic外
还可以使用with模块实现事务操作

def index(request):
    # 开启事务保护
    # sid = transaction.savepoint()
    with transaction.atomic():
        sid = transaction.savepoint()
                ....

当网站的数据量越来越庞大时,使用单个数据库处理很容易使数据库系统瘫痪,从而导致整个网站瘫痪。
为了减轻数据库系统的压力,django可以同时连接和使用多个数据库。

项目同时配置两个mysql数据库

两个mysql数据库名分别为db1,db2

# 配置多数据库
# 新增dbRouter.py文件编写类DbAppsRouter
DATABASE_ROUTERS = ['MyDjango.dbRouter.DbAppsRouter']
DATABASE_APPS_MAPPING = { # 设置数据库与项目应用的映射关系
    # 设置每个App的模型使用的数据库
    # {'app_name':'database_name',}
    'admin': 'defualt',
    'index': 'db1', # 代表index的models.py所定义的模型都在数据库db1里创建数据表
    'user': 'db2',
}

多个数据库实现需要编写的配置文件,貌似什么都不用更改,只需要在settings.py中配置正确即可

from django.conf import settings

DATABASE_MAPPING = settings.DATABASE_APPS_MAPPING # 从配置文件获取DATABASE_APPS_MAPPING的值


class DbAppsRouter(object):
    """
    A router to control all database operations on models for different
    databases.

    In case an app is not set in settings.DATABASE_APPS_MAPPING, the router
    will fallback to the `default` database.

    Settings example:

    DATABASE_APPS_MAPPING = {'app1': 'db1', 'app2': 'db2'}
    类DbAppsRouter根据变量DATABASE_MAPPING(数据库与项目的映射关系)来设置数据库的
    读取(类方法 db_for_read)、写入(类方法 db_for_write)、数据表关系(类方法 allow_relation)和数据迁移(类方法 allow_migrate)

    """

    def db_for_read(self, model, **hints): # 读取
        """"Point all read operations to the specific database."""
        if model._meta.app_label in DATABASE_MAPPING:
            return DATABASE_MAPPING[model._meta.app_label]
        return None

    def db_for_write(self, model, **hints): # 写入
        """Point all write operations to the specific database."""
        if model._meta.app_label in DATABASE_MAPPING:
            return DATABASE_MAPPING[model._meta.app_label]
        return None

    def allow_relation(self, obj1, obj2, **hints): #数据表关系
        """Allow any relation between apps that use the same database."""
        db_obj1 = DATABASE_MAPPING.get(obj1._meta.app_label)
        db_obj2 = DATABASE_MAPPING.get(obj2._meta.app_label)
        if db_obj1 and db_obj2:
            if db_obj1 == db_obj2:
                return True
            else:
                return False
        return None

    # 用于创建数据表
    def allow_migrate(self, db, app_label, model_name=None, **hints): # 数据迁移
        if db in DATABASE_MAPPING.values():
            return DATABASE_MAPPING.get(app_label) == db
        elif app_label in DATABASE_MAPPING:
            return False
        return None

两个不同的数据库中无法建立数据表关系(一对多等关系),
这样并不能创建mysql数据库,这样python manage.py migrate语句只在Sqlite3数据库里床火箭DJango内置功能的数据表。若要为多个mysql数据库设定指定数据表,需要在migrate指令中设置参数
--database==数据库名

python manage.py migrate s --database==数据库名

但我执行这个报错,我去

django.db.utils.ConnectionDoesNotExist: The connection =db2 doesn't exist

笔记来源:Django Web应用开发实战

posted @ 2021-11-23 18:37  索匣  阅读(38)  评论(0编辑  收藏  举报