zoukankan      html  css  js  c++  java
  • 第七章续集笔记

    接着第七章的数据查询

    这里写数据‘排除’查询
    在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应用开发实战

  • 相关阅读:
    JZOJ 3034. 【NOIP2012模拟10.17】独立集
    JZOJ 3035. 【NOIP2012模拟10.17】铁轨
    JZOJ 1259. 牛棚安排
    数位DP JZOJ 3316. 非回文数字
    JZOJ 3046. 游戏
    JZOJ 3013. 填充棋盘
    debian 安装oracle提供的java8
    java 汉字转拼音 PinYin4j
    debian ssh设置root权限登陆 Permission denied, please try again
    java并发下订单生成策略
  • 原文地址:https://www.cnblogs.com/wkhzwmr/p/15592602.html
Copyright © 2011-2022 走看看