美文网首页
Django系列 3:ORM 常用技巧详解

Django系列 3:ORM 常用技巧详解

作者: dingxutao | 来源:发表于2020-11-26 09:54 被阅读0次

    1:查询集的原生SQL

    注意:像values, values_list的查询集没有query属性

    queryset=Event.objects.all()

    print(queryset.query)

    2:OR查询

    from django.db.modelsimport Q

    第一种:queryset_1 | queryset_2

    第二种:filter(Q(<condition_1>)|Q(<condition_2>)  

    3:AND查询

    from django.db.modelsimport Q

    第一种:filter(<condition_1>, <condition_2>)

    第二种:queryset_1 & queryset_2

    第三种:filter(Q(<condition_1>) & Q(<condition_2>))  

    4:NOT 查询

    from django.db.modelsimport Q

    第一种: exclude(<condition>)

    第二种: filter(~Q(<condition>))

    5:UNION查询 (并集)

    关于MySQL union 操作可以参考:MySQL 内连接、左连接、右连接

    注意:union操作只能在查询集拥有相同的字段和数据类型前提下执行,如果两个查询集的字段和数据类型不一样,union操作出现错误。你可以两个models上执行union,只要他们有相同字段或相同字段子集。

    Hero.objects.all().values_list("name","gender").union(Villain.objects.all().values_list("name","gender"))

    6:查询集中的部分字段

    第一种: 查询集的 values 和 values_list 方法

                   values :查询集queryset是一个django.db.models.query.QuerySet对象, 迭代的每个元素是一个字典

                   values_list :查询集queryset是一个django.db.models.query.QuerySet对象, 迭代的每个元素是一个元组

    第二种: only 方法,

                   查询集queryset是一个django.db.models.query.QuerySet对象, 迭代的每个元素是一个模型类的实例

                 (1):你可以访问每个实例的only方法指定的其他字段

                 (2):每个实例的 __dict__ 只含有 _state、id、指定的字段

                 (3):可以对迭代的实例进行 obj.aaa = 1000, obj.save() 操作

    7:子查询

    各家公司对子查询的标准不太一样,对于数据量比较大的公司一般可能不适用子查询,因为在生产中会产生查询延迟拖慢系统的或网站的整体访问效率

    8:基于字段值比较标准来筛选查询集

    注意:比较的两个字段的类型要求一致

              from django.db.modelsimport F

              from django.db.models.functionsimport Substr

            (1):全字段相等  User.objects.filter(last_name=F("first_name"))

            (2):首字母相同 UsersModel.objects.annotate(name=Substr("email", 1, 1), mail=Substr("username",

                         1, 1)).filter(name=F("mail")).values("id", "username", "email")

             SQL:   SELECT "circle_users"."id", "circle_users"."username", "circle_users"."email" FROM                                          "circle_users"   WHERE SUBSTRING("circle_users"."email", 1, 1) =                                                                       SUBSTRING("circle_users"."username", 1, 1)

    9:annotate 高级查询方法

    用于分组与统计,通常与聚合函数一起使用

    from django.db.modelsimport Count

    (1):CircleUsersModel.objects.annotate(cnt=Count("department_chz")).values("department_chz", "cnt")

      SQL:SELECT "circle_users"."department_chz", COUNT("circle_users"."department_chz") AS "cnt" FROM                 "circle_users" GROUP BY "circle_users"."id"  (其中AS 的 `cnt` 是自定义的名字)

    (2):CircleUsersModel.objects\

                 .values("username", "first_name")\

                 .annotate(name_count=Count("username"))\

                 .filter(name_count__gte=2)

    SQL: SELECT "circle_users"."username", "circle_users"."first_name", COUNT("circle_users"."username") AS            "name_count" FROM "circle_users" GROUP BY "circle_users"."username", "circle_users"."first_name"               HAVING COUNT("circle_users"."username") >= 2

    (3):CircleUsersModel.objects\

                 .annotate(name_count=Count("username")).filter(name_count__gte=2)\

                .values("username", "first_name")

    SQL: SELECT "circle_users"."username", "circle_users"."first_name" FROM "circle_users" GROUP BY                        "circle_users"."id" HAVING COUNT("circle_users"."username") >= 2

    10:aggregate 聚合函数

    注意: 改方法返回的不再是 django.db.models.query.QuerySet 对象, 而是一个字典;如果我要对 QerySet 中每个元素都进行聚合计算、并且返回的仍然是 QuerySet ,那就要用到 annotate() 方法了。

    from django.db.modelsimport F, Count, Q, Avg, Max, Min, Sum

    result = CircleUsersModel.objects.aggregate(

                     mix_id=Min("id"),

                     max_id=Max("id"),

                    count=Count("id"),

                    avg=Avg("id")

                )

    result: {'mix_id': 15, 'max_id': 4175, 'count': 4159, 'avg': 2095.8946862}

    原生SQL: SELECT MIN("id"), MAX("id") ,COUNT("id"), SUM("id") FROM "circle_users"

    aggregate 的结果集是一个字典,所以你无法通过 result.query 来打印原生sql语句。

    11:extra  

    有些情况下,Django的查询语法难以简单的表达复杂的 WHERE 子句,对于这种情况, Django 提供了extra()QuerySet修改机制 — 它能在QuerySet生成的SQL从句中注入新子句。更多用户可以参考其他博客

    注意:为了防止SQL注入,select 和 where 中的参数在select_params 和 params 中填充

    queryset = CircleUsersModel.objects\

                      .extra(select={"bbs": "create_time > %s"}, select_params=["2020-11-24 12:00:00"],

                                where=['username like %s', "id> %s"], params=["丁%", "4000"])\

                      .values("id", "username", "bbs")

    SQL:  SELECT (create_time > 2020-11-24 12:00:00) AS "bbs", "circle_users"."id", "circle_users"."username" FROM "circle_users" WHERE (username like 丁%) AND (id> 4000)

    结果:<QuerySet [

                                    {'bbs': False, 'id': 4007, 'username': '丁星平'}, {'bbs': False, 'id': 4016, 'username': '丁备'},                                      {'bbs': False, 'id': 4035, 'username': '丁菊花'}, {'bbs': False, 'id': 4083, 'username': '丁楠'},                                      {'bbs': False, 'id': 4100, 'username': '丁君'}, {'bbs': False, 'id': 4101, 'username': '丁君'},                                        {'bbs': False, 'id': 4125, 'username': '丁锦如'}

                ]> 

    12:查找具有重复字段值的列 (其实就是分组与过滤)

    duplicates = User.objects.values( 'first_name' )\

                        .annotate(name_count=Count('first_name'))

                        .filter(name_count__gt=1) 

    13:从查询集中找到独一无二的字段值 (同上)

    distinct = User.objects.values('first_name').annotate(name_count=Count('first_name')).filter(name_count=1)

    records = User.objects.filter(first_name__in=[item['first_name'] for item in distinct]) 

    这个和 User.objects.distinct("first_name").all() 不同, User.objects.distinct("first_name").all() 会获取遇到的不同的first_name时的第一条记录。(注意部分数据库如MySQL,distinct用法是 User.objects.value("first_name").distinct())

    14:Q的复杂查询

    前面我们使用Q 对象来做 OR 、 AND 和 NOT 运算。Q 对象给你在where查询上绝对的控制权。

    queryset = User.objects.filter( Q(first_name__startswith='R') & ~Q(last_name__startswith='Z'))

    queryset = User.objects.filter( Q(first_name__startswith='R') & Q(last_name__startswith='D'))

    15:一次创建多个对象

    Category.objects.bulk_create([

              Category(name="God"),

             Category(name="Demi God"),

             Category(name="Mortal")

    ])

    16:复制一个现有的模型对象

     Hero.objects.all().count()         =>  4

    hero = Hero.objects.first()

    hero.pk = None

    hero.save()

    Hero.objects.all().count()          =>  5

    17:model signals

    Django 提供了用于模型对象创建和删除周期的信号钩子。这些Django提供的信号是:

    pre_init、post_init、pre_save、post_save、pre_delete、post_delete

    在这其中最常用的信号是 pre_save 和 post_save 。我们来仔细看看他们

    信号 vs 重写.save()

    既然信号和重写 .save 有类似的作用,用哪一个是一个时常困惑的问题。这里给出了何时需要用哪一个。

    如果你想其他人,如第三方应用,重写或定制对象的 save 行为,你应该抛出你自己singnals。

    如果你想要介入一个你无权控制更改的应用中的 save 行为,你应该使用 post_save 或 pre_save 钩子信号。

    如果你想定制化你自己的应用的 save 行为,你可以重写 save 方法。

    18:不区分大小写的方式排序查询集

    第一种:

                 from django.db.models.functions import Lower

                 User.objects.all().order_by(Lower('username')).values_list('username',flat=True)

    第二种:

                User.objects.annotate(uname=Lower('username')).order_by('uname').values_list('username',flat=True)

    19:转化已有的数据表到Django模型

    python manage.py inspectdb > models.py

    20:使用Django的slug字段提高代码可读性

    slug 是url的一部分,它以用户可读的形式标识一个网页中的特殊页面。为了能让它工作,Django 为我们提供了slugfield字段。可以和下面一样使用它。我们已经有一个模型 Article ,我们要添加一个slugfield使其具有有用户可读性。

    from django.utils.text import slugify 

    class Article(models.Model):

             headline = models.CharField(max_length=100) 

             ......

              slug = models.SlugField(unique=True) 

    def save(self, *args, **kwargs): 

             self.slug = slugify(self.headline) 

             super(Article, self).save(*args, **kwargs)  

     >>> u1 = User.objects.get(id=1)

     >>> from datetime import date

     >>> a1 = Article.objects.create(headline="todays market report", pub_date=date(2018, 3, 6), reporter=u1) 

    >>> a1.save() // slug here is auto-generated, we haven't created it in the above create method. 

    >>> a1.slug 'todays-market-report'     

    Slug字段如此有用是因为:

    它是人性化的(如/blog/1 而不是/1/)。

    在标题,头部和url中创建一致性是一种不错的SEO。

    21:ORM 各种条件查询关键字

    注意:必须是model字段名后面紧跟着二个下划线

    __exact        精确等于 like ‘aaa’ 

    __iexact        精确等于 忽略大小写 ilike ‘aaa’ 

    __contains    包含 like ‘%aaa%’ 

    __icontains    包含 忽略大小写 ilike ‘%aaa%’,但是对于sqlite来说,contains的作用效果等同于icontains

    __gt                 大于 

    __gte               大于等于 

    __lt                 小于 

    __lte              小于等于 

    __in              存在于一个list范围内 

    __startswith         以…开头 

    __istartswith         以…开头 忽略大小写 

    __endswith         以…结尾 

    __iendswith         以…结尾,忽略大小写 

    __range              在…范围内 

    __year                 日期字段的年份 

    __month             日期字段的月份 

    __day                 日期字段的日 

    __isnull=True/False

    相关文章

      网友评论

          本文标题:Django系列 3:ORM 常用技巧详解

          本文链接:https://www.haomeiwen.com/subject/eqnmiktx.html