【Python】Django查询数据


本文章顺序参照Django官方文档的Making queries章节[https://docs.djangoproject.com/en/2.1/topics/db/queries/],举例model使用如下model:

class Book(models.Model):
    name = models.CharField(max_length=256)
    author = models.ManyToManyField("Author")
    publisher = models.ForeignKey("Publisher",on_delete=models.CASCADE,related_name="pub")
    publishDate = models.DateField()
    description = models.TextField(blank=True,null=True)

    def __str__(self):
        return "《%s》" % self.name

    class Meta():
        verbose_name = "书籍"
        verbose_name_plural = "书籍"


class Author(models.Model):
    name = models.CharField(max_length=64)
    age = models.IntegerField(null=True,blank=True)
    nationality = models.CharField(max_length=128,verbose_name="国籍")

    def __str__(self):
        return self.name

    class Meta():
        verbose_name = "作者"
        verbose_name_plural = "作者"

class Publisher(models.Model):
    name = models.CharField(max_length=128)
    address = models.CharField(max_length=256,null=True,blank=True)

    def __str__(self):
        return self.name

    class Meta():
        verbose_name = "出版社"
        verbose_name_plural = "出版社"

1. 一般查询

1)查询所有对象

objs = Book.objects.all()

返回的objs为QuerySet;

2) 使用筛选器实现检索特定对象

筛选nameBook1的所有Book的对象:

objs = Book.objects.filter(name = "Book1")

筛选name不为Book1的所有Book的对象;

objs = Book.objects.exclude(name = "Book1")

链接过滤器:

objs = Book.objects.exclude(name = "Book1").filter(description = "")

过滤后的查询集是惟一的;每次优化QuerySet时,都将得到一个全新的QuerySet,而这个QuerySet绝不绑定到前面的QuerySet。每个细化都创建一个单独的、不同的QuerySet,该QuerySet可以存储、使用和重用。

QuerySet是惰性的,只有在被使用时,才会去数据库查询:

obj = Book.objects.exclude(name = "Book1")
obj = obj.objects.filter(description ="")
obj = obj.objects.filter(name = "Book2")
print(obj)

看似三次查询,但实际上print(obj)之前,查询的结果都不会从数据库中获取,只有执行print(obj)时,才会去数据库查询,实际就进行了一次数据库取值。

##3)使用get()实施检索单个对象

obj = Book.objects.get(name = "Book1")

不返回QuerySet,而是返回单个对象;
没有符合条件的对象时,会触发Book.DoesNotExist异常

4) 其他QuerySet方法

参见另一个篇博客 [baidu.com]

5) 限制QuerySet

使用Python数组切片语法的一个子集来将查询集限制为一定数量的结果。这相当于SQL的LIMITOFFSET语句:

返回前5个对象(LIMIT 5):

obj = Book.objects.all()[:5]

返回第6到第10个对象 (OFFSET 5 LIMIT 5):

obj = Book.objects.all()[5:10]

返回前10个对象的每个第二个对象的列表:

obj = Book.objects.all()[:10:2]

注:不支持负数查询,如:

obj = Book.objects.all()[-1] # 错误的

6)字段查询

字段查找是指定SQL中 WHERE子句的主体的方式,字段可以被指定为QuerySet方法filter()、exclude()和get()的关键字参数。

基本查找关键字参数采用表单**filed__lookuptype = value(双下划线) **

obj = Book.objects.filter(publishDate__lte = "2017-01-01")

转换为SQL语言大致如下:

SELECT * FROM app_book WHERE publishDate <= '2006-01-01';
# app_book中的app指的是Book所在的django的app名字

查找中指定的字段必须是模型字段的名称,但是有一个例外,对于外键,可以指定_id为后缀的字段名,这种情况下,value参数应该包含外部模型的主键的原始值

obj = Book.objects.filter(publishDate__lte = "2017-01-01")

如果传递无效的关键字参数,则查找函数将引发TypeError错误

详细的字段查找,可以参见另一个篇博客 [baidu.com]

2. 跨模型查询

Django提供了一种强大而直观的方法来“跟踪”查询中的关系,在幕后自动地为您处理SQL连接。要跨越关系,只需跨模型使用相关字段的字段名,用双下划线分隔,直到到达你想要的字段。

#外键查询
obj = Book.objects.filter(publisher__name__contanins="publish")
#ManyToMany字段查询
obj = Book.objects.filter(author__name = "Jim")
#返回Book所有对象中author的name为Jim的对象组成的 QuerySet

也可以进行反向查询

obj = Author.objects.filter(book__name__contanins="Book1")
# name中包含Book1的Book对象中的author对应的对象
obj = Publisher.objects.filter(pub__name = "Jim")
#用pub是应为文章开头的model中,Book中的publisher字段中设置了related_name="pub"

3. F()查询

将一个模型字段的值与同一模型上的另一个字段的值进行比较时,就会用到F()表达式

# 查询Book对象中name字段与description字段的值相等的对象
obj = Book.objects.filter(name = F('description'))

F()也可以跨模型

# 查询Book对象中name字段与外键publisher的name字段的值相等的对象
obj = Book.objects.filter(name = F('publisher__name'))

4. 主键pk快捷查询

Book.objects.get(id__exact=14) # Explicit form
Book.objects.get(id=14) # __exact is implied
Book.objects.get(pk=14) # pk implies id__exact

以上三句是相同的,id是默认主键,也可以在model中自定义主键

5.Caching 和 QuerySets

每个QuerySet都包含一个缓存,以最小化数据库访问。

 print([e.name for e in Book.objects.all()])
 print([e.publishDate for e in Book.objects.all()])

上述代码,相同的数据库查询将执行两次,使数据库负载加倍;
此外,这两个列表可能不包含相同的数据库记录,因为在两个请求之间可能在瞬间添加或删除了一个条目。

为了避免这个问题,只需保存QuerySet并重用它:

queryset = Book.objects.all()
print([p.name for p in queryset]) 
print([p.publishDate for p in queryset]) # 重用QuerySet

何时QuerySet不进行缓存:
具体来说,这意味着使用数组切片或索引限制queryset不会填充缓存。

  • 在QuerySet对象中反复获取某个索引将每次都查询数据库
queryset = Book.objects.all()
print(queryset[0]) # 查询数据库
print(queryset[0]) # 再次查询数据库
  • 如果已经计算了整个QuerySet,那么将检查缓存
queryset = Book.objects.all()
[book for book in queryset] # Queries the database
print(queryset[5]) # 使用缓存
print(queryset[5]) # 使用缓存

6.使用Q对象进行复杂的查找

如果需要执行更复杂的查询(例如,带有OR语句的查询),可以使用Q对象。

Book.objects.get(
    Q(name__startswith='Who'),
    Q(publishData=date(2005, 5, 2)) | Q(publishData=date(2005, 5, 6))
)

对应SQL语句为:

SELECT * from books WHERE name LIKE 'Who%'
    AND (publishData= '2005-05-02' OR publishData= '2005-05-06')

7. 关联查询

注:对应于原生SQL语句的join查询

1) 通过对象执行关联查询

三种关联关系:
  • 一对多关系
  • 多对多关系
  • 一对一关系
由一到多的访问语法:

**以 出版社- 图书 即为一对多的关系 **

  • 由一到多的访问语法
# 一对应的模型类对象.多对应的模型类名小写_set
# 例:
pub = Publisher.objects.get(id=1)
b.book_set.all()
  • 由多到一的访问语法:
# 多对应的模型类对象.多对应的模型类中的关系类属性名
# 例:
book1 = Book.objects.get(id=1)
book1.publisher

通过模型类执行关联查询

由多模型类条件查询一模型类数据
# 关联模型类名小写__属性名__条件运算符=值
# 如果没有 __条件运算符  部分,表示等于
# 例:
list = Publish.objects.filter(book__title__contains="八"
有一模型类条件查询多模型类数据
# 一模型类关联属性名__一模型类属性名__条件运算符 = 值
# 例如,查询出版社名为“清华大学出版社”的所有书
list = Book.objects.filter(publisher__name="清华大学出版社")

8. 自关联

自关联模型,就是表中的某一列,关联了这个表中的另外一列。最典型的自关联模型就是地区表。省、市、县都在一张表里面。省的pid为null,市的pid为省的id,县的pid为市的id。
例如:

class Area(models.Model):    
        name = models.CharField(max_length=20, verbose_name='名称')
        # 自关联(特殊的一对多): 生成的字段名 parent_id
        parent = models.ForeignKey('self', verbose_name='上级行政区划')
        class Meta:
            db_table = 'tb_areas'
            verbose_name = '行政区划'

如果知道一个市,叫a市,想查他属于什么省:

a = Area.objects.get(id=1)
# b就是a市的省份的对象
b = a.parent

如果知道一个省,叫a省,想查他有什么市

c = Area.objects.get(id=1)
# d就是c省的所有市的查询集合
d = c.area__set.all()
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值