zoukankan      html  css  js  c++  java
  • Django(17)orm查询操作

    前言

    查找是数据库操作中一个非常重要的技术。查询一般就是使用filterexclude以及get三个方法来实现。我们可以在调用这些方法的时候传递不同的参数来实现查询需求。在ORM层面,这些查询条件都是使用field+__+condition的方式来使用的。以下将那些常用的查询条件来一一解释。
     

    查询条件

    exact

    使用精确的=进行查找。如果提供的是一个None,那么在SQL层面就是被解释为NULL。示例代码如下:

    article = Article.objects.get(id__exact=14)
    article = Article.objects.get(id__exact=None)
    

    以上的两个查找在翻译为SQL语句为如下:

    select ... from article where id=14;
    select ... from article where id IS NULL;
    

    iexact

    在底层会被翻译成LIKE。示例代码如下:

    article = Article.objects.filter(title__iexact='hello world')
    

    那么以上的查询就等价于以下的SQL语句:

    select ... from article where title like 'hello world';
    
    • LIKE和=:大部分情况下都是等价的,只有少数情况下是不等价的。
    • exict和iexact:他们的区别其实就是LIKE和=的区别,因为exact会被翻译成=,而iexact会被翻译成LIKE。
    • 因为field__exact=xxx其实等价于filed=xxx,因此我们直接使用filed=xxx就可以了,并且因为大部分情况exactiexact又是等价的,因此我们以后直接使用field=xxx就可以了。
       

    QuerySet.query

    QuerySet.query:query可以用来查看这个ORM查询语句最终被翻译成的SQL语句。但是query只能被用在QuerySet对象上,不能用在普通的ORM模型上。因此如果你的查询语句是通过get来获取数据的,那么就不能使用query,因为get返回的是满足条件的ORM模型,而不是QuerySet。如果你是通过filter等其他返回QuerySet的方法查询的,那么就可以使用query

    article = Article.objects.filter(title__iexact='hello world')  # article是QuerySet对象,所以可以被翻译成sql语句
    print(article.query)  # 打印sql语句
    

    打印结果

    SELECT `article`.`id`, `article`.`title`, `article`.`content`, `article`.`category_id` FROM `article` WHERE `article`.`content` LIKE hello world
    

    contains

    大小写敏感,判断某个字段是否包含了某个数据。示例代码如下:

    articles = Article.objects.filter(title__contains='hello')
    

    在翻译成SQL语句为如下:

    select ... where title like binary '%hello%';
    

    要注意的是,在使用contains的时候,翻译成的sql语句左右两边是有百分号的,意味着使用的是模糊查询。而exact翻译成sql语句左右两边是没有百分号的,意味着使用的是精确的查询。
     

    icontains

    大小写不敏感的匹配查询。示例代码如下:

    articles = Article.objects.filter(title__icontains='hello')
    

    翻译成SQL语句为如下:

    select ... where title like '%hello%';
    

    in

    提取那些给定的field的值是否在给定的容器中。容器可以为list、tuple或者任何一个可以迭代的对象,包括QuerySet对象。示例代码如下:

    articles = Article.objects.filter(id__in=[1,2,3])
    

    也可以通过其他的表的字段来判断是否在某个集合中。示例代码如下:

    categories = Category.objects.filter(article__id__in=[1,2,3])
    

    如果要判断相关联的表的字段,那么也是通过__来连接。并且在做关联查询的时候,不需要写models_set,直接使用模型的名字的小写化就可以了。比如通过分类去查找相应的文章,那么通过article__id__in就可以了,而不是写成article_set__id__in的形式。当然如果你不想使用默认的形式,可以在外键定义的时候传递related_query_name来指定反向查询的名字。示例代码如下:

    class Category(models.Model):
            name = models.CharField(max_length=100)
    
            class Meta:
                db_table = 'category'
    
    
        class Article(models.Model):
            title = models.CharField(max_length=200)
            content = models.TextField()
            cateogry = models.ForeignKey("Category",on_delete=models.CASCADE,null=True,related_query_name='articles')
    
            class Meta:
                db_table = 'article'
    

    因为在cateogryForeignKey中指定了related_query_namearticles,因此你不能再使用article来进行反向查询了。这时候就需要通过articles__id__in来进行反向查询。

    反向查询和反向引用的区别

    • 反向查询:将模型名字小写化。比如article__in,就是将Article模型小写了。当然我们也可以通过related_query_name来指定自己的方式,而不使用默认的方式。
    • 反向引用:将模型名字小写化,然后再加上_set,比如article_set,可以通过related_name来指定自己的方式,而不是用默认的方式。

    并且,如果在做反向查询的时候,如果查询的字段就是模型的主键,那么可以省略掉这个字段,直接写成article__in就可以了,不需要这个id了。

    in不仅仅可以指定列表/元组,还可以为QuerySet。比如要查询“文章标题中包含有hello的所有分类”,那么可以通过以下代码来实现:

    articles = Article.objects.filter(title__icontains='hello')
        categories = Category.objects.filter(articles__in=articles)
        for cateogry in categories:
            print(cateogry)
    

    gt

    某个field的值要大于给定的值。示例代码如下:

    articles = Article.objects.filter(id__gt=3)
    

    以上代码的意思是将所有id大于3的文章全部都找出来。将翻译成以下SQL语句:

    select ... where id > 4;
    

    gte

    类似于gt,是大于等于。
     

    lt

    类似于gt是小于。
     

    lte

    类似于lt,是小于等于。
     

    startswith

    判断某个字段的值是否是以某个值开始的。大小写敏感。示例代码如下:

    articles = Article.objects.filter(title__startswith='hello')
    

    以上代码的意思是提取所有标题以hello字符串开头的文章。将翻译成以下SQL语句:

    select ... where title like 'hello%'
    

    istartswith

    类似于startswith,但是大小写是不敏感的。
     

    endswith

    判断某个字段的值是否以某个值结束。大小写敏感。示例代码如下:

    articles = Article.objects.filter(title__endswith='world')
    

    以上代码的意思是提取所有标题以world结尾的文章。将翻译成以下SQL语句:

    select ... where title like '%world';
    

    iendswith

    类似于endswith,只不过大小写不敏感。
     

    range

    判断某个field的值是否在给定的区间中。示例代码如下:

    from django.utils.timezone import make_aware
    from datetime import datetime
    start_time = make_aware(datetime(year=2018, month=1, day=1))
    end_time = make_aware(datetime(year=2018, month=12, day=12))
    article = Article.objects.filter(create_time__range=(start_time, end_time))
    

    以上代码的意思是提取所有发布时间在2018/1/1到2018/12/12之间的文章。将翻译成以下的SQL语句

    select ... from article where pub_time between '2018-01-01' and '2018-12-12'。
    

    注意点

    • 以上提取数据,不会包含最后一个值。也就是不会包含2018/12/12的文章。
    • 因为我们在settings.py中指定了USE_TZ=True,并且设置了TIME_ZONE='Asia/Shanghai',因此我们在提取数据的时候要使用django.utils.timezone.make_aware先将datetime.datetime从navie时间转换为aware时间。make_aware会将指定的时间转换为TIME_ZONE中指定的时区的时间。
       

    date

    针对某些date或者datetime类型的字段。可以指定date的范围。并且这个时间过滤,还可以使用链式调用。示例代码如下:

    articles = Article.objects.filter(create_time__date=date(2018,3,29))
    

    以上代码的意思是查找时间为2018/3/29这一天发表的所有文章。将翻译成以下的sql语句:

    select ... WHERE DATE(CONVERT_TZ(`front_article`.`pub_date`, 'UTC', 'Asia/Shanghai')) = 2018-03-29
    

    注意,因为默认情况下MySQL的表中是没有存储时区相关的信息的。因此我们需要下载一些时区表的文件,然后添加到Mysql的配置路径中。如果你用的是windows操作系统。那么在http://dev.mysql.com/downloads/timezones.html下载timezone_2018d_posix.zip - POSIX standard。然后将下载下来的所有文件拷贝到C:ProgramDataMySQLMySQL Server 5.7Datamysql中,如果提示文件名重复,那么选择覆盖即可。
    如果用的是linux或者mac系统,那么在命令行中执行以下命令:mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -D mysql -u root -p,然后输入密码,从系统中加载时区文件更新到mysql中。
     

    year

    根据年份进行查找。示例代码如下:

    articles = Article.objects.filter(create_time__year=2021)
    articles = Article.objects.filter(create_time__year__gte=2021)
    

    以上的代码在翻译成SQL语句为如下:

    select ... where pub_date between '2021-01-01' and '2021-12-31';
    select ... where pub_date >= '2021-01-01';
    

    month

    同year,根据月份进行查找。
     

    day

    同year,根据日期进行查找。
     

    week_day

    同year,根据星期几进行查找。1表示星期天,7表示星期六,2-6代表的是星期一到星期五。示例代码如下:

    article = Article.objects.filter(create_time__week_day=3)  # 寻找星期三创建的文章
    

    time

    根据时间进行查找。示例代码如下:

    articles = Article.objects.filter(create_time__time=datetime.time(12,12,12));
    

    以上的代码是获取每一天中12点12分12秒发表的所有文章。
     

    isnull

    根据值是否为空进行查找。示例代码如下:

    articles = Article.objects.filter(create_time__isnull=False)
    

    以上的代码的意思是获取所有发布日期不为空的文章。将来翻译成SQL语句如下:

    select ... where pub_date is not null;
    

    regex和iregex

    大小写敏感和大小写不敏感的正则表达式。示例代码如下:

    articles = Article.objects.filter(title__regex=r'^hello')
    

    以上代码的意思是提取所有标题以hello字符串开头的文章。将翻译成以下的SQL语句:

    select ... where title regexp binary '^hello';
    

    iregex是大小写不敏感的。
     

    根据关联的表进行查询

    假如现在有两个ORM模型,一个是Article,一个是Category。代码如下:

    class Category(models.Model):
        """文章分类表"""
        name = models.CharField(max_length=100)
    
    class Article(models.Model):
        """文章表"""
        title = models.CharField(max_length=100,null=True)
        category = models.ForeignKey("Category",on_delete=models.CASCADE)
    

    比如想要获取文章标题中包含"hello"的所有的分类。那么可以通过以下代码来实现:

    categories = Category.object.filter(article__title__contains("hello"))
    
  • 相关阅读:
    【BIEE】清除缓存
    【BIEE】安装好BIEE后,修改默认登录页面不为QuickStart页面
    【BIRT】修改BIRT的背景颜色
    【Oracle】查看死锁与解除死锁
    【Oracle】查看正在运行的存储过程
    【Oracle】表空间相关集合
    【Oracle】Oracle常用语句集合
    MapWindowPoints
    模拟鼠标操作
    SVN MERGE 方法(原创)
  • 原文地址:https://www.cnblogs.com/jiakecong/p/14780601.html
Copyright © 2011-2022 走看看