django models

在日常的编程中,我们需要建立数据库模型

而往往会用到表与表之间的关系,这就比单表取数据要复杂一些

在多表之间发生关系的情形下,我们如何利用models提供的API的特性获得需要的数据呢

我们先从对象和查询集说开去

查询结果有时是对象/有时是查询集

我们只需要知道 ,只有get方法或者对查询集合进行切片,比如objects.all()[0] 或者 objects.all().first()等得到的是对象,其他得到的都是queryset

我们举个例子看下

# -*- coding: utf-8 -*-
from __future__ import unicode_literals from django.db import models # Create your models here. class Province(models.Model):
name = models.CharField(max_length = 20) def __str__(self):
return self.name.decode("utf-8") def __unicode(self):
return self.name.decode("utf-8") class City(models.Model):
name = models.CharField(max_length = 20)
province = models.ForeignKey('Province', to_field = 'id') def __str__(self):
return self.name.decode("utf-8") def __unicode(self):
return self.name.decode("utf-8")

从上面的ORM类的建立可以看到,city通过外键和province表发生关系

BTW,我们通常(建议)都是把外键放在一对多的关系的多的那一方,因为这样是方便的。否则,放在少的那一方,因为少的一方是一对多的关系,数据库里要繁琐的建立这种关系:A省-B市,

A省-C市 A省-D市等等,这样会很麻烦

而放在多的一方,就只要建立一个 市-省的关系就行了

然后我们建立一些测试数据

我们可以通过django的shell,通过命令行的方式建立测试数据

python manage.py shell

这样就可以进入python 的django 的命令行用代码的方式建立测试数据

[root@khao test_models]# python manage.py  makemigrations
Migrations for 'app01':
app01/migrations/0001_initial.py
- Create model City
- Create model Province
- Add field province to city
[root@khao test_models]# python manage.py migrate
Operations to perform:
Apply all migrations: admin, app01, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying app01.0001_initial... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying sessions.0001_initial... OKre
[root@khao test_models]# python manage.py shell
Python 2.7.5 (default, Aug 4 2017, 00:39:18)
Type "copyright", "credits" or "license" for more information. IPython 5.5.0 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details. In [1]: from app01 import models In [2]: models.Province.objects.create(name = u"河北")
Out[2]: <Province: 河北> In [3]: models.Province.objects.create(name = u"山东")
Out[3]: <Province: 山东> In [4]: h = models.Province.objects.get(name = u"河北") In [5]: s = models.Province.objects.get(name = u"山东")
In [8]: models.City.objects.create(name = u"石家庄",province = h)
Out[8]: <City: 石家庄> In [9]: models.City.objects.create(name = u"张家口",province = h)
Out[9]: <City: 张家口> In [10]: models.City.objects.create(name = u"邢台",province = h)
Out[10]: <City: 邢台> In [11]: models.City.objects.create(name = u"济南",province = s)
Out[11]: <City: 济南> In [12]: models.City.objects.create(name = u"青岛",province = s)
Out[12]: <City: 青岛>

然后,我们进入数据库的命令行,看看数据和表结构:

[root@khao ~]# mysql
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 32
Server version: 5.6.39 MySQL Community Server (GPL) Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> set names utf8;
Query OK, 0 rows affected (0.00 sec) mysql> use test_models_db;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A Database changed
mysql> select * from app01_province;
+----+--------+
| id | name |
+----+--------+
| 1 | 河北 |
| 2 | 山东 |
+----+--------+
2 rows in set (0.00 sec) mysql> select * from app01_city;
+----+-----------+-------------+
| id | name | province_id |
+----+-----------+-------------+
| 1 | 石家庄 | 1 |
| 2 | 张家口 | 1 |
| 3 | 邢台 | 1 |
| 4 | 济南 | 2 |
| 5 | 青岛 | 2 |
+----+-----------+-------------+
5 rows in set (0.00 sec)

我们可以看到,在有外键的表里,也就是我们创建了

province = models.ForeignKey('Province', to_field = 'id')

这个字段的表里,django会自动给我们创建一个外键关联字段province_id字段

正是通过这个字段,city这张表找到了每行即每个城市对应的省的id,也即每个城市和省进行了关联

正向查找

我们在很多讲述django的文章/资料/教程里面,都会看到正向查找和反向查找的概念

那么什么是正向查找呢

正向查找就是通过数据库就有的字段去查找,比如

我们可以通过city这张表找到省(province),因为city表里有province这个外键

In [13]: models.City.objects.get(name = u"石家庄").province
Out[13]: <Province: 河北> In [14]: models.City.objects.get(name = u"石家庄").province_id
Out[14]: 1L In [17]: models.City.objects.get(name = u"石家庄").province.id
Out[17]: 1L
> 在上面的查找中,都是通过city表本身就有的字段进行出发查找的,这就叫做正向查找

跨表查询

其实在上面的查询过程,已经发生了跨表查询

什么是跨表查询

models.City.objects.get(name = u"石家庄").province_id 不是跨表查询,因为这个字段在city表结构里本来就有(上面已经演示了表结构)

models.City.objects.get(name = u"石家庄").province.id 是跨表查询,这个字段在city 表结构里本来没有,是通过什么方式查到的呢?

models.City.objects.get(name = u"石家庄") 拿到了 石家庄这个城市对象

models.City.objects.get(name = u"石家庄").province 拿到了石家庄这个城市对应的省,即河北这个对象

models.City.objects.get(name = u"石家庄").province.id 从河北这个对象里,查到了河北这个省对象自身的属性id

如何观察和验证上述结论(如何观察ORM对应的SQL语句)

这里有个小技巧

我们可以在django 命令行里面导入

from django.db import connection

然后编写ORM语句执行

再 敲入 connection.queries

就会看到之前所有的ORM对应的sql语句

我们看下 上面的查询过程

In [1]: from app01 import models

In [2]: models.City.objects.get(name = u"石家庄").province_id
Out[2]: 1L In [3]: from django.db import connection In [4]: connection.queries
Out[4]:
[{u'sql': u'SELECT @@SQL_AUTO_IS_NULL', u'time': u'0.000'},
{u'sql': u'SELECT VERSION()', u'time': u'0.000'},
{u'sql': u"SELECT `app01_city`.`id`, `app01_city`.`name`, `app01_city`.`province_id` FROM `app01_city` WHERE `app01_city`.`name` = '\u77f3\u5bb6\u5e84'",
u'time': u'0.000'}]

我们可以看到,models.City.objects.get(name = u"石家庄").province_id这个ORM操作,是从app01_city这张表查出来的,而且app01_city这张表有province_id这个字段,所以不是跨表查询

我们再继续看 models.City.objects.get(name = u"石家庄").province.id

{u'sql': u"SELECT `app01_city`.`id`, `app01_city`.`name`, `app01_city`.`province_id` FROM `app01_city` WHERE `app01_city`.`name` = '\u77f3\u5bb6\u5e84'",
u'time': u'0.002'},
{u'sql': u'SELECT `app01_province`.`id`, `app01_province`.`name` FROM `app01_province` WHERE `app01_province`.`id` = 1',
u'time': u'0.000'}]

可以清楚的看到,由于city这张表没有province.id这个字段,为此,先拿到models.City.objects.get(name = u"石家庄").province这个对象,也就是到province这张表去查找

在app01_province这张表查到了对应的id这个字段

什么时候用双下划线查找

凡是通过queryset对象去查找的时候,就用双下划线查找

前面已经讲过,在django的ORM里面,除了get拿到的是对象,其他查到到都是queryset,除非对queryset进行切片/索引取值,比如,objects.all()[0]这样拿到的才是对象,objects.all()拿到的是queryset

凡是get查到的对象,都可以通过点号 . 的方式进行逐级查找,比如上面的跨表查询 models.City.objects.get(name = u"石家庄").province.id

我们举几个双下划线查找的例子

In [10]: models.City.objects.all().filter(name = "石家庄")
Out[10]: <QuerySet [<City: 石家庄>]>

首先我们看到上面拿到的是QuerySet类型的

我们先看下上面的ORM语句对应的sql语句

In [4]: connection.queries
Out[4]:
[{u'sql': u'SELECT @@SQL_AUTO_IS_NULL', u'time': u'0.000'},
{u'sql': u'SELECT VERSION()', u'time': u'0.000'},
{u'sql': u"SELECT `app01_city`.`id`, `app01_city`.`name`, `app01_city`.`province_id` FROM `app01_city` WHERE `app01_city`.`name` = '\u77f3\u5bb6\u5e84' LIMIT 21",
u'time': u'0.000'}]

可以看到,这sql对应的显然是一个查询集合(QuerySet)。因为都用到了LIMIT 限定条件

我们再继续看

In [5]: models.City.objects.all().filter(name = "石家庄").values("province__name")
Out[5]: <QuerySet [{'province__name': u'\u6cb3\u5317'}]> In [6]: connection.queries
Out[6]:
[{u'sql': u'SELECT @@SQL_AUTO_IS_NULL', u'time': u'0.000'},
{u'sql': u'SELECT VERSION()', u'time': u'0.000'},
{u'sql': u"SELECT `app01_city`.`id`, `app01_city`.`name`, `app01_city`.`province_id` FROM `app01_city` WHERE `app01_city`.`name` = '\u77f3\u5bb6\u5e84' LIMIT 21",
u'time': u'0.000'},
{u'sql': u"SELECT `app01_province`.`name` FROM `app01_city` INNER JOIN `app01_province` ON (`app01_city`.`province_id` = `app01_province`.`id`) WHERE `app01_city`.`name` = '\u77f3\u5bb6\u5e84' LIMIT 21",
u'time': u'0.002'}]

看最后的SQL 可以看到,这种双下划线查询一般对应的都是各种类型的join联表查询,即联合两张表进行联合查询

XXX_set跨表查询

除了上面说到的通过对象在正向查找里面的点查询方式的跨表查询和通过QuerySet进行双下划线的方式查询

我们再简单总结下上面两种方式

  • 对象方式的点查询进行跨表查询

简单来说,凡是试图进行跨表查询,总得要两个表有某种关系吧,才能进行跨表查询,在有外键关系的两个表里,一般是在一对多的多的那一方里,通过某个对象,拿到与其有外键关系的别的表的对象,在通过这个对象进行查询(其实就是通过拿到的对象---别的表的,进行在别的表的查询,就是点方式的跨表查询)

  • 双下划线方式的跨表查询

这个一般都是通过从QuerySet类型的查询集出发,通过双下划线的方式查到别的表的属性,并且一般都是把双下划线作为某种属性的过滤条件或者是取出值的限定字段来使用的,比如可以使用在filter过滤中或者使用在values过滤中

比如 models.City.objects.filter(name = "石家庄").values("province__name")

或者也可以直接在filter的条件里面使用双下划线过滤

In [13]: models.City.objects.filter(province__name = u"河北")
Out[13]: <QuerySet [<City: 石家庄>, <City: 邢台>, <City: 张家口>]>
In [39]: models.City.objects.filter(province__name = "河北").values("province__name")
Out[39]: <QuerySet [{'province__name': u'\u6cb3\u5317'}, {'province__name': u'\u6cb3\u5317'}, {'province__name': u'\u6cb3\u5317'}]>
  • XXX_set方式跨表查询

这个一般都用在一对多的一的那一方

查询这个一的那一方对应的多的集合

而且一般只有对象才可以使用XXX_set属性查找,不能从QuerySet类型进行XXX_set查找

In [46]: models.Province.objects.first().city_set.all()
Out[46]: <QuerySet [<City: 石家庄>, <City: 邢台>, <City: 张家口>]>

django models的点查询/跨表查询/双下划线查询的更多相关文章

  1. Python--day69--单表查询之神奇的双下划线

    单表查询之神奇的双下划线: 单表查询之神奇的双下划线 models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 models. ...

  2. django基础之day04,必知必会13条,双下划线查询,字段增删改查,对象的跨表查询,双下划线的跨表查询

    from django.test import TestCase # Create your tests here. import os import sys if __name__ == " ...

  3. Django学习——Django测试环境搭建、单表查询关键字、神奇的双下划线查询(范围查询)、图书管理系统表设计、外键字段操作、跨表查询理论、基于对象的跨表查询、基于双下划线的跨表查询

    Django测试环境搭建 ps: 1.pycharm连接数据库都需要提前下载对应的驱动 2.自带的sqlite3对日期格式数据不敏感 如果后续业务需要使用日期辅助筛选数据那么不推荐使用sqlite3 ...

  4. django ORM模型表的一对多、多对多关系、万能双下划线查询

    一.外键使用 在 MySQL 中,如果使用InnoDB引擎,则支持外键约束.(另一种常用的MyIsam引擎不支持外键) 定义外键的语法为fieldname=models.ForeignKey(to_c ...

  5. python-day71--django多表双下划线查询及分组聚合及F/Q查询

    #====================================双下划线的跨表查询===============# 前提 此时 related_name=bookList 属性查询: # 查 ...

  6. ORM( ORM查询13种方法3. 单表的双下划线的使用 4. 外键的方法 5. 多对多的方法 ,聚合,分组,F查询,Q查询,事务 )

    必知必会13条 <1> all(): 查询所有结果 <2> get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或 ...

  7. 测试脚本配置、ORM必知必会13条、双下划线查询、一对多外键关系、多对多外键关系、多表查询

    测试脚本配置 ''' 当你只是想测试django中的某一个文件内容 那么你可以不用书写前后端交互的形式而是直接写一个测试脚本即可 脚本代码无论是写在应用下的test.py还是单独开设py文件都可以 ' ...

  8. 使用admin的步骤、必知必会13条、单表的双下划线、外键的操作、多对多的操作:

    MVC M: model 模型 与数据库交互 V: view 视图 HTML C:controller 控制器 流程 和 业务逻辑 MTV M:model ORM T:template 模板 HTML ...

  9. $Django 多对多-自定义第三张表 基于双下划线的跨表查询(补充)

    自定义第三张表的好处:可以定义多个字段, 缺点:查询不方便(有方法解决) 1.第三张表设置外键,联合唯一(查询不方便) class Books(models.Model): name=models.C ...

随机推荐

  1. Android OpenGL ES 开发(九): OpenGL ES 纹理贴图

    一.概念 一般说来,纹理是表示物体表面的一幅或几幅二维图形,也称纹理贴图(texture).当把纹理按照特定的方式映射到物体表面上的时候,能使物体看上去更加真实.当前流行的图形系统中,纹理绘制已经成为 ...

  2. MyBatis的三层级联和二层缓存

           我们这里说的三层级联和二级缓存其实也是MyBatis映射器的知识点,只是因为比较难理解,所以单独拿出来讲解,下面是三层级联的内容:        我们知道,在数据库中包含着一对一,一对多 ...

  3. CDN和CDN加速原理

    随着互联网的发展,用户在使用网络时对网站的浏览速度和效果愈加重视,但由于网民数量激增,网络访问路径过长,从 而使用户的访问质量受到严重影响.特别是当用户与网站之间的链路被突发的大流量数据拥塞时,对于异 ...

  4. Sql中根据旧表创建新表的SQL语句

    今天在网上查了下,根据旧表创建新表的SQL语句,网上给了两个答案 create table tab_new like tab_old (使用旧表创建新表) create table tab_new a ...

  5. linux(ubuntu)环境下安装IDEA

    想调试java虚拟机内存溢出的情况,在调试过程中总会出现一些不可预见的状况,正好在学linux,在windows上安装了虚拟机,安装的镜像是ubuntu(乌班图)装在了虚拟机中,装在虚拟机中好处是即使 ...

  6. shell基本命令学习

    Shell是一种脚步语言,那么,就必须有解释器来执行这些脚步. Unix/Linux上常见的shell脚步解释器有bash,sh,csh,ksh等,习惯把它们称为shell. 例如: #!/bin/b ...

  7. 避免uncaughtException错误引起node.js进程崩溃

    uncaughtException 未捕获的异常, 当node.js 遇到这个错误,整个进程直接崩溃. 或许这俩个人上辈子一定是一对冤家. 或许这俩个人经历了前世500次的回眸才换来了今生的相遇,只可 ...

  8. 开源一个定时任务调度器 webscheduler

    在企业应用中定时任务调度的需求是必不可少的,比如定时同步数据,定时结转数据,定时检测异常等等.公司之前是在使用一款采用.net 开发的windows服务形式的定时程序,基本能满足需求,在一段时间的时候 ...

  9. 下一个ajax异步请求被挂起问题

    异步请求按理来说应该是会不受其它ajax请求影响的,但如果是服务端访问了Session就不能这么说了. 了解了asp.net的会话管理,那我们来看看今天要谈到的主题: IReadOnlySession ...

  10. supervisor进程管理工具的使用

    supervisor是一款进程管理工具,当想让应用随着开机启动,或者在应用崩溃之后自启动的时候,supervisor就派上了用场. 广泛应用于服务器中,用于引导控制程序的启动 安装好superviso ...