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. autobase之配置文件

    配置文件内容: 1.db_info{},数据库链接属性,包括Oracle,dmdb,快立方 2.credit etc目录的路径 3.批量基础数据导入sql路径 4.用例执行日志存储目录路径 功能: 1 ...

  2. heartbeat单独提供高可用服务

    本文目录:1.简介2.安装heartbeat 2.1 编译安装Heartbeat3.heartbeat相关配置文件 3.1 配置文件ha.cf 3.2 配置文件authkeys 3.3 配置文件har ...

  3. jquery pjax 用法总结

    以前我们点击a链接的时候总是会刷新整个页面并跳转到新页面,中间可以很明显的看到短暂的白屏.pjax就很好的解决了这问题. pjax的原理很简单,就是发送一个ajax请求,获取html代码,再把相关代码 ...

  4. 利用XAMPP本地搭建WordPress博客

    现在越来越多的人利用WordPress搭建了自己的博客网站,我也是一样,但是还有一些人不知道怎么搭建WordPress网站的方法,因为怕弄 不好,所以也就没有花钱去做,所以这里我就讲讲怎么样利用XAM ...

  5. centos6.8安装cdh5.10.0(离线版)

    Centos6.8安装CDH5 相关包的下载地址: Cloudera Manager地址:http://archive.cloudera.com/cm5/cm/5/ CDH安装包地址:http://a ...

  6. Python基本知识

    python是一门编程语言,这是我们学习python首先要了解的内容,那么编程语言又是什么,我们为什么需要编程语言 我们讲的话是汉语,英语或者其他语言,计算机也可以讲话,但是他只会说0,1,也只能看懂 ...

  7. 在做APP前端开发时应注意的一些问题

    在做APP前端开发时应注意的一些问题 在整个app开发流程中,app前端开发是一个必不可少的环节,也是一个在app开发过程中重量级的角色.说到这,那么在app应用的前端开发中,又要注意什么问题呢?一. ...

  8. Node.JS开发环境准备

    1.安装Nodejs的Windows包. 官网:http://nodejs.org/ 2.可以使用cmd运行nodejs项目,命令格式: node  文件名.js node  文件名 3.对于不熟悉的 ...

  9. 用JNDI连接数据库

    之前说到了利用Java中的Properties类读取properties配置文件,连接数据库,现在说另一种方法,他们的目的和作用都是一样的,都是为了提高代码的复用性,解决了更改数据库 时还要更改代码的 ...

  10. Java 泛型进阶

    擦除 在泛型代码内部,无法获得任何有关泛型参数类型的信息. 例子1: //这个例子表明编译过程中并没有根据参数生成新的类型 public class Main2 { public static voi ...