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应用开发实战

  • 相关阅读:
    Android · SQLite
    Android · 获取网络图片
    Android · 广告走灯
    Android · 动画
    Android常用资源
    Android · Fragment
    JSP 自动刷新
    JSP 点击量统计
    JSP 页面重定向
    JSP 日期处理
  • 原文地址:https://www.cnblogs.com/wkhzwmr/p/15592602.html
Copyright © 2011-2022 走看看