Django中的数据库查询

web应用需要经常向数据库查询相关的数据,Django语言也不例外。了解Django中的数据查询操作,对于优化应用的性能,有着重要的意义。

基础信息

Django中,model通过Manager获取QuerySet,每个model至少有objects这个 Manager 。

QuerySet 可以有一个或多个 filter , filter 根据传入的参数返回 QuerySet 结果。

用SQL来对比, QuerySet 相当于 SELECT , filter 相当于 WHERE 或者是 LIMIT

每次添加一条filter,会获得一个新的 QuerySet 。

思路

利用Django的测试框架,构造测试数据,查看Django构建的SQL,以及查询到的内容。

设计表结构

schema

创建model

创建测试app,并加入settings.py APPS中

python manage.py startapp learning

INSTALLED_APPS = [ 
     ... 
    ’learning’, 
    ... 

创建对应的model

class Student(models.Model): 
    name = models.CharField(max_length=255, help_text="学生姓名") 
    create_time = models.DateTimeField(auto_now_add=True, help_text="创建时间") 
    def __str__(self): 
        return self.name

class Teacher(models.Model): 
    name = models.CharField(max_length=255, help_text="老师姓名") 
    create_time = models.DateTimeField(auto_now_add=True, help_text="创建时间") 
    def __str__(self): 
        return self.name

class Classe(models.Model): 
    name = models.CharField(max_length=255, help_text="班级名称") 
    teacher = models.ForeignKey(Teacher, help_text="老师") 
    create_time = models.DateTimeField(auto_now_add=True, help_text="创建时间") 
    def __str__(self): 
        return self.name

class ClasseStudent(models.Model): 
    student = models.ForeignKey(Student) 
    classe = models.ForeignKey(Classe) 
    create_time = models.DateTimeField(auto_now_add=True, help_text="创建时间") 
测试数据

使用factory-boy来辅助构造测试数据,代码如下

class TeacherFactory(factory.DjangoModelFactory): 
    class Meta: 
        model = Teacher

name = factory.Sequence(lambda n: "Teacher #%s" % n)

class StudentFactory(factory.DjangoModelFactory): 
    class Meta: 
        model = Student

name = factory.Sequence(lambda n: "Student #%s" % n)

class ClasseFactory(factory.DjangoModelFactory): 
    class Meta: 
        model = Classe

name = factory.Sequence(lambda n: "Class #%s" % n)

class ClasseStudentFactory(factory.DjangoModelFactory): 
    class Meta: 
        model = ClasseStudent

student = factory.SubFactory(StudentFactory) 
    classe = factory.SubFactory(ClasseFactory) 
现在来伪造测试数据

在tests.py添加测试用例

class ModelTestCase(TestCase): 
    def setUp(self): 
        self.teacher_one = TeacherFactory() 
        self.class_one = ClasseFactory(teacher=self.teacher_one) 
        self.class_two = ClasseFactory(teacher=self.teacher_one) 
        for i in range(40): 
            ClasseStudentFactory(student=StudentFactory(), classe=self.class_one) 
        for i in range(40): 
            ClasseStudentFactory(student=StudentFactory(), classe=self.class_two) 
可以看到,添加了一个老师,两个班,每个班加入了40个学生

filter和exclude

添加testcase

def test_filter_chain(self): 
    query_set = Student.objects.filter(name__startswith=’Student’).exclude(pk=1).filter(create_time__year__gte=2015)[3:10] 
    print query_set.query 
    print query_set 
执行

python manage.py test python manage.py test learning.test.test.ModelTestCase.test_filter_chain 
可以看到结果

SELECT `learning_student`.`id`, `learning_student`.`name`, `learning_student`.`create_time` FROM `learning_student` WHERE (`learning_student`.`name` LIKE BINARY Student% AND NOT (`learning_student`.`id` = 1) AND `learning_student`.`create_time` >= 2014-12-31 16:00:00) LIMIT 7 OFFSET 3
<QuerySet [<Student: Student #4>, <Student: Student #5>, <Student: Student #6>, <Student: Student #7>, <Student: Student #8>, <Student: Student #9>, <Student: Student #10>]> 
和我们预期的一致,值得注意的是,Django执行的是lazy query,也就是说前面的filter和exclude并没有去查询数据库,后面的分片才导致了那次的查询

order_by

def test_order_by(self): 
        query_set = Classe.objects.order_by("-create_time") 
        print query_set.query 
        print query_set 
SELECT `learning_classe`.`id`, `learning_classe`.`name`, `learning_classe`.`teacher_id`, `learning_classe`.`create_time` FROM `learning_classe` ORDER BY `learning_classe`.`create_time` DESC 
<QuerySet [<Classe: Class #3>, <Classe: Class #2>]> 
如果有多个column参与排序,可以使用 Coalesce

select_related

可以用select_related查询外键的信息,并将结果保存,这样查找外键信息时,将不会向数据库发送请求,如下所示

def test_select_related(self): 
        query_set = Classe.objects.select_related("teacher") 
        print query_set.query 
        print query_set[0].teacher 
SELECT `learning_classe`.`id`, `learning_classe`.`name`, `learning_classe`.`teacher_id`, `learning_classe`.`create_time`, `learning_teacher`.`id`, `learning_teacher`.`name`, `learning_teacher`.`create_time` FROM `learning_classe` INNER JOIN `learning_teacher` ON (`learning_classe`.`teacher_id` = `learning_teacher`.`id`) 
<QuerySet [<Classe: Class #0>, <Classe: Class #1>]> 
defer和only

有时候查询只需要部分字段的结果,可以用defer和only来限制查询的结果

def test_defer(self): 
        print Classe.objects.defer("create_time").query 
        print Classe.objects.only("create_time").query 
SELECT `learning_classe`.`id`, `learning_classe`.`name`, `learning_classe`.`teacher_id` FROM `learning_classe` 
SELECT `learning_classe`.`id`, `learning_classe`.`create_time` FROM `learning_classe` 
如果访问到没有获取的字段时,会再从数据库中读一次

文章来源:简书

时间: 2024-10-31 13:09:44

Django中的数据库查询的相关文章

[Django]中建立数据库视图

Django中建立数据库视图 Django中没有建立视图的接口,如果要建立一个视图需要一些手动的改变. 这里使用的Django 版本>1.5, 使用的数据库为mysql 第一步 建立视图,例如视图的名称叫做 user_info 第二步 model中这么写: class MyModel(models.Model): ... class Meta: managed = False db_table = "user_info" 这样就可以把视图经过orm变成对象了. REF: crea

关于Django中的数据库操作API之distinct去重的一个误传

关于Django中的数据库操作API之distinct去重的一个误传 最近在做一个Server的项目,后台框架是Apache mod_wsgi + django.django是一个基于Python的Web开发框架,功能十分强大,至于有多强大,还是读者们自己去体验吧.我在这里要说的一个问题是关于Python的ORM功能的.问题就在django提供的数据库操作API中的distinct()函数,了解SQL语句的读者都应该知道,DISTINCT关键字可以在select操作时去重.django里的这个d

【Django】Django中的模糊查询以及Q对象的简单使用

Django中的模糊查询: 需要做一个查找的功能,所以需要使用到模糊查询. 使用方法是:字段名加上双下划线跟上contains或者icontains,icontains和contains表示是否区分大小写. 实测icontains为不区分大小写,contains为区分大小写. from djangp.db.models import Q def select_seller(request,keyword): seller_info= Seller.objects.filter(Q(usernam

关于Django中ORM数据库迁移的配置

Django中ORM数据库迁移配置 1,若想将模型转为mysql数据库中的表,需要在settings中配置: DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME':'bms', # 要连接的数据库,连接前需要创建好 'USER':'root', # 连接数据库的用户名 'PASSWORD':'', # 连接数据库的密码 'HOST':'127.0.0.1', # 连接主机,默认本级 'PORT':3306

Django中多表查询思路

需求: 1.有一张文章表和一张评论表 2.两张表的关系是一对多 3.规则:若是有新评论,则将对应的文章置顶,若是有新文章则将新文章置顶. 思路: 在文章表中增加一个最后评论时间的字段.然后采用分组排序,即可解决 预留: Django中除了有外键对应关系表外,如何将多张表联合查询?

Django中多数据库的使用

django中,你可以为每一个app都单独设置一个数据库. 一.在项目project下新建文件database_router.py文件: DATABASE_MAPPING = {'app1': 'db1','app2':'db2'}class DatabaseAppsRouter(object): def db_for_read(self, model, **hints): """" Point all read operations to the specific

django中对数据库的增删改查

Django的配置文件时settings.py中的 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], 只修改这一个 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.d

在Django中使用数据库遇到的问题

很多人读了Django老版本的书籍,却安装了比较新的Django,以至于在使用数据库时出了很多头疼的问题,我也不例外,不想再让别人继续被折磨了.现将本人遇到的一些问题以及解决的办法整理如下:(我的环境是Linux终端) 一. 数据库的配置: 1.首先你要保证在终端上安装了数据库(MySQL).接下来在在里面创建你自己的数据库,比如create database djangodb. 2.cd到你创建工程的目录,我的是username/djcode/mysite,然后cd 到mysite里,然后vi

django中对数据库生成记录操作失败

在终端执行以下语句时,会发现一点效果也没有,但是在manage.py中会成功: python3 manage.py makemigrations # 仅仅是在小本本上(migrations文件夹)记录数据库的修改 并不会直接操作数据 python3 manage.py migrate # 将数据库修改记录 真正同步到数据库 最后发现开始的python3是为了避免python版本的重叠问题,在环境变量配置的时候,区分加上去的,而我的配置不是python3,所以不生效. 我的解释器: 所以正确的语句