19. Django 2.1.7 模型的关联

2022-01-17 09:55:36 浏览数 (1)

上一篇Django 2.1.7 模型 - 条件查询 F对象 Q对象 聚合查询讲述了关于Django模型的F对象、Q对象、聚合查询等功能。

但是没有讲到两张表的关联查询的实现,这个在模型里面该怎么处理呢?

参考文献

https://docs.djangoproject.com/zh-hans/2.1/ref/models/relations/

https://docs.djangoproject.com/zh-hans/2.1/topics/db/managers/

模型类关系

在进行关联查询之前,首先要了解一下模型之间的关联关系,以及相应的操作。

关系字段类型

关系型数据库的关系包括三种类型:

  • ForeignKey:一对多,将字段定义在多的一端中。
  • ManyToManyField:多对多,将字段定义在任意一端中。
  • OneToOneField:一对一,将字段定义在任意一端中。
  • 可以维护递归的关联关系,使用'self'指定。
一对多关系

想在前面篇章中,写到的服务器信息表以及中间件表,就是一对多的关系。 一台服务器对应多个中间件,相应的数据模型如下:

代码语言:javascript复制
class ServerInfo(models.Model):
    server_hostname = models.CharField(max_length=20, default=None)
    server_intranet_ip = models.CharField(max_length=20, default=None)
    server_internet_ip = models.CharField(max_length=20, default=None)
    server_shelves_date = models.DateField(auto_now_add=True) # 数据加入时间
    update_time = models.DateTimeField(auto_now=True) # 数据更新时间
    is_delete = models.BooleanField(default=False) # 逻辑删除

class MiddlewareInfo(models.Model):
    name = models.CharField(max_length=20)
    port = models.IntegerField()
    server = models.ForeignKey('ServerInfo',on_delete=models.CASCADE, default=None)
    shelves_date = models.DateTimeField(auto_now_add=True) # 数据加入时间
    update_time = models.DateTimeField(auto_now=True)  # 数据更新时间
    is_delete = models.BooleanField(default=False) # 逻辑删除

可以从上面的模型看出,一对多的关系构建的关键就是MiddlewareInfo(中间件-多类)设置外键连接ServerInfo(服务器信息 - 一类)。 server = models.ForeignKey('ServerInfo',on_delete=models.CASCADE, default=None)。 但是在实际使用的过程中,使用外键的话,在做一些数据处理的时候很不方便。也可以不设置一个外键,直接就单纯一个int字段来记录ServerInfo类的id也是可以的。

多对多关系

在前面篇章中,并没有设计关于多对多的关联模型,那么现在可以设计一个。

在前面已有服务器类的前提下,可以设计一个服务器用途类。 定义一个服务器用途类的话,那么一台服务器可能有多种用途,同时一种用途类型下,可能有多台服务器对应。 这种就是多对多的关系。

代码语言:javascript复制
class ServerUsedType(models.Model):
    used_type = models.CharField(max_length=20,default=None)

class ServerInfo(models.Model):
    server_hostname = models.CharField(max_length=20, default=None)
    server_intranet_ip = models.CharField(max_length=20, default=None)
    server_internet_ip = models.CharField(max_length=20, default=None)
    server_shelves_date = models.DateField(auto_now_add=True) # 数据加入时间
    update_time = models.DateTimeField(auto_now=True) # 数据更新时间
    is_delete = models.BooleanField(default=False) # 逻辑删除
    server_used_type_id = models.ManyToManyField(ServerUsedType) # 通过ManyToManyField建立多对多的关系

那么这种模型多对多关系的字段通过数据迁移,会生成什么样的字段呢? 执行数据迁移,如下:

代码语言:javascript复制
python3 manage.py makemigrations
python3 manage.py migrate

从mysql日志查看关键执行日志如下:

代码语言:javascript复制
2019-06-14T16:54:06.348996Z	   21 Query	CREATE TABLE `assetinfo_serverinfo_server_used_type_id` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `serverinfo_id` integer NOT NULL, `serverusedtype_id` integer NOT NULL)
2019-06-14T16:54:06.394877Z	   21 Query	ALTER TABLE `assetinfo_serverinfo_server_used_type_id` ADD CONSTRAINT `assetinfo_serverinfo_serverinfo_id_b297c62e_fk_assetinfo` FOREIGN KEY (`serverinfo_id`) REFERENCES `assetinfo_serverinfo` (`id`)
2019-06-14T16:54:06.441100Z	   21 Query	ALTER TABLE `assetinfo_serverinfo_server_used_type_id` ADD CONSTRAINT `assetinfo_serverinfo_serverusedtype_id_5551cbd5_fk_assetinfo` FOREIGN KEY (`serverusedtype_id`) REFERENCES `assetinfo_serverusedtype` (`id`)
2019-06-14T16:54:06.489104Z	   21 Query	ALTER TABLE `assetinfo_serverinfo_server_used_type_id` ADD CONSTRAINT `assetinfo_serverinfo_ser_serverinfo_id_serverused_c12509b8_uniq` UNIQUE (`serverinfo_id`, `serverusedtype_id`)

查看mysql迁移数据之后,生成了两个表,如下:

代码语言:javascript复制
mysql> show tables;
 ------------------------------------------ 
| Tables_in_assetinfo                      |
 ------------------------------------------ 
| assetinfo_middlewareinfo                 |
| assetinfo_scriptinfo                     |
| assetinfo_serverinfo                     |
| assetinfo_serverinfo_server_used_type_id |
| assetinfo_serverusedtype                 |

可以看到为了实现多对多的关系,django自动创建了一张中间表assetinfo_serverinfo_server_used_type_id,通过中间表绑定assetinfo_serverinfoassetinfo_serverusedtype表的关系。 查看这三张表的表结构如下:

代码语言:javascript复制
mysql> desc assetinfo_serverinfo;
 --------------------- ------------- ------ ----- --------- ---------------- 
| Field               | Type        | Null | Key | Default | Extra          |
 --------------------- ------------- ------ ----- --------- ---------------- 
| id                  | int(11)     | NO   | PRI | NULL    | auto_increment |
| server_hostname     | varchar(20) | NO   |     | NULL    |                |
| server_intranet_ip  | varchar(20) | NO   |     | NULL    |                |
| server_internet_ip  | varchar(20) | NO   |     | NULL    |                |
| server_shelves_date | date        | NO   |     | NULL    |                |
| is_delete           | tinyint(1)  | NO   |     | NULL    |                |
| update_time         | datetime(6) | NO   |     | NULL    |                |
 --------------------- ------------- ------ ----- --------- ---------------- 
7 rows in set (0.00 sec)

mysql> desc assetinfo_serverinfo_server_used_type_id;
 ------------------- --------- ------ ----- --------- ---------------- 
| Field             | Type    | Null | Key | Default | Extra          |
 ------------------- --------- ------ ----- --------- ---------------- 
| id                | int(11) | NO   | PRI | NULL    | auto_increment |
| serverinfo_id     | int(11) | NO   | MUL | NULL    |                |
| serverusedtype_id | int(11) | NO   | MUL | NULL    |                |
 ------------------- --------- ------ ----- --------- ---------------- 
3 rows in set (0.00 sec)

mysql> desc assetinfo_serverusedtype;
 ----------- ------------- ------ ----- --------- ---------------- 
| Field     | Type        | Null | Key | Default | Extra          |
 ----------- ------------- ------ ----- --------- ---------------- 
| id        | int(11)     | NO   | PRI | NULL    | auto_increment |
| used_type | varchar(20) | NO   |     | NULL    |                |
 ----------- ------------- ------ ----- --------- ---------------- 
2 rows in set (0.00 sec)

mysql> 

在知道Django模型如何实现多对多的关联之后,下面来看看怎么关联查询。

关联查询

Django中也能实现类似于join查询。

通过对象执行关联查询

首先写一个一对多的关联查询SQL,如下:select s.server_hostname,m.name,s.id,m.server_id from assetinfo_serverinfo as s left join assetinfo_middlewareinfo as m on s.id = m.server_id where s.id = 1;

代码语言:javascript复制
mysql> select s.server_hostname,m.name,s.id,m.server_id from assetinfo_serverinfo as s left join assetinfo_middlewareinfo as m on s.id = m.server_id where s.id = 1;
 ----------------- ----------- ---- ----------- 
| server_hostname | name      | id | server_id |
 ----------------- ----------- ---- ----------- 
| 测试服务器      | memcached |  1 |         1 |
| 测试服务器      | redis     |  1 |         1 |
| 测试服务器      | test      |  1 |         1 |
| 测试服务器      | test      |  1 |         1 |
| 测试服务器      | test      |  1 |         1 |
| 测试服务器      | test      |  1 |         1 |
| 测试服务器      | test      |  1 |         1 |
| 测试服务器      | test      |  1 |         1 |
| 测试服务器      | test      |  1 |         1 |
| 测试服务器      | mongodb   |  1 |         1 |
| 测试服务器      | test      |  1 |         1 |
| 测试服务器      | test      |  1 |         1 |
 ----------------- ----------- ---- ----------- 
12 rows in set (0.00 sec)

mysql> 

那么模型类该怎么实现这个查询呢?

由一到多的访问语法:一对应的模型类对象.多对应的模型类名小写_set

代码语言:javascript复制
In [1]: from assetinfo.models import ServerInfo,MiddlewareInfo

# 设置查询 id = 1 的 服务器信息
In [2]: s = ServerInfo.objects.get(id=1)

# 关联查询相关的中间件信息
In [3]: s.middlewareinfo_set.all()
Out[3]: <QuerySet [<MiddlewareInfo: MiddlewareInfo object (1)>, <MiddlewareInfo: MiddlewareInfo object (2)>, <MiddlewareInfo: MiddlewareInfo object (5)>, <MiddlewareInfo: Middlewar
eInfo object (6)>, <MiddlewareInfo: MiddlewareInfo object (7)>, <MiddlewareInfo: MiddlewareInfo object (8)>, <MiddlewareInfo: MiddlewareInfo object (9)>, <MiddlewareInfo: Middlewar
eInfo object (10)>, <MiddlewareInfo: MiddlewareInfo object (11)>, <MiddlewareInfo: MiddlewareInfo object (14)>, <MiddlewareInfo: MiddlewareInfo object (15)>, <MiddlewareInfo: Middl
ewareInfo object (16)>]>

In [4]: 

对应的SQL如下:SELECT assetinfo_middlewareinfo.id, assetinfo_middlewareinfo.name, assetinfo_middlewareinfo.port, assetinfo_middlewareinfo.server_id, assetinfo_middlewareinfo.shelves_date, assetinfo_middlewareinfo.update_time, assetinfo_middlewareinfo.is_deleteFROMassetinfo_middlewareinfoWHEREassetinfo_middlewareinfo.server_id = 1 LIMIT 21;

代码语言:javascript复制
mysql> SELECT `assetinfo_middlewareinfo`.`id`, `assetinfo_middlewareinfo`.`name`, `assetinfo_middlewareinfo`.`port`, `assetinfo_middlewareinfo`.`server_id`, `assetinfo_middlewareinfo`.`shelves_date`, `assetinfo_middlewareinfo`.`update_time`, `assetinfo_middlewareinfo`.`is_delete` FROM `assetinfo_middlewareinfo` WHERE `assetinfo_middlewareinfo`.`server_id` = 1  LIMIT 21;
 ---- ----------- ------- ----------- ---------------------------- ---------------------------- ----------- 
| id | name      | port  | server_id | shelves_date               | update_time                | is_delete |
 ---- ----------- ------- ----------- ---------------------------- ---------------------------- ----------- 
|  1 | memcached | 11211 |         1 | 2019-06-10 14:56:46.150556 | 2019-06-10 17:37:51.365155 |         1 |
|  2 | redis     |  6379 |         1 | 2019-06-10 14:56:46.150556 | 2019-06-10 17:38:20.712862 |         1 |
|  5 | test      |   123 |         1 | 2019-06-10 17:05:16.632773 | 2019-06-10 17:05:16.632773 |         1 |
|  6 | test      |   123 |         1 | 2019-06-10 17:06:20.120658 | 2019-06-10 17:06:20.121656 |         1 |
|  7 | test      |   123 |         1 | 2019-06-10 17:06:43.193412 | 2019-06-10 17:06:43.193412 |         1 |
|  8 | test      |   123 |         1 | 2019-06-10 17:07:03.747395 | 2019-06-10 17:07:03.747395 |         1 |
|  9 | test      |   123 |         1 | 2019-06-10 17:08:43.372097 | 2019-06-10 17:08:43.372097 |         1 |
| 10 | test      |   123 |         1 | 2019-06-10 17:09:37.877019 | 2019-06-10 17:09:37.877019 |         1 |
| 11 | test      |   123 |         1 | 2019-06-10 17:11:45.403627 | 2019-06-10 17:11:45.403627 |         1 |
| 14 | mongodb   |  3306 |         1 | 2019-06-11 14:01:24.003175 | 2019-06-11 14:06:14.525648 |         1 |
| 15 | test      |   123 |         1 | 2019-06-11 14:04:10.576241 | 2019-06-11 14:04:10.576241 |         0 |
| 16 | test      |  3306 |         1 | 2019-06-11 14:06:05.608006 | 2019-06-11 14:06:05.608006 |         0 |
 ---- ----------- ------- ----------- ---------------------------- ---------------------------- ----------- 
12 rows in set (0.00 sec)

mysql> 

可以结果看出,其实Django模型的关联查询,也只是查询多类一方的单独数据而已。

上面是一到多的查询方式,下面再来一个多到一的查询方式,如下: 查看中间件信息id = 1 对应的 服务器信息

代码语言:javascript复制
# 首先查询中间件的数据
In [4]: m = MiddlewareInfo.objects.get(id=1)

# 根据中间件的查询结果,再进行服务器信息查询
In [13]: s = ServerInfo.objects.filter(id = m.server_id )

# 打印查询出来的服务器名称
In [14]: for item in s:
    ...:     print(item.server_hostname)
    ...: 
测试服务器

上面查询主键编号的时候都是使用id,还可以使用pk来进行查询,如下:

代码语言:javascript复制
In [15]: ServerInfo.objects.get(pk=1)
Out[15]: <ServerInfo: ServerInfo object (1)>

In [16]: ServerInfo.objects.get(id=1)
Out[16]: <ServerInfo: ServerInfo object (1)>

In [17]: 

这两个查询的结果是一样的。

上面就是使用对象来实现的关联查询。那么有没有更加一句话能搞定的关联查询呢?

通过模型类执行关联查询

由多模型类条件查询一模型类数据:

语法如下:

代码语言:javascript复制
关联模型类名小写__属性名__条件运算符=值

 如果没有"__运算符"部分,表示等于,结果和sql中的inner join相同。 ”

例:查询服务器信息,要求服务器中中间件的name包含'redis'。

代码语言:javascript复制
In [17]: result = ServerInfo.objects.filter(middlewareinfo__name__contains='redis')

In [18]: print(result)
<QuerySet [<ServerInfo: ServerInfo object (1)>]>

对应的SQL如下:

代码语言:javascript复制
mysql> SELECT `assetinfo_serverinfo`.`id`, `assetinfo_serverinfo`.`server_hostname`, `assetinfo_serverinfo`.`server_intranet_ip`, `assetinfo_serverinfo`.`server_internet_ip`, `assetinfo_serverinfo`.`server_shelves_date`, `assetinfo_serverinfo`.`update_time`, `assetinfo_serverinfo`.`is_delete` FROM `assetinfo_serverinfo` INNER JOIN `assetinfo_middlewareinfo` ON (`assetinfo_serverinfo`.`id` = `assetinfo_middlewareinfo`.`server_id`) WHERE `assetinfo_middlewareinfo`.`name` LIKE BINARY '%redis%'  LIMIT 21
    -> ;
 ---- ----------------- -------------------- -------------------- --------------------- ---------------------------- ----------- 
| id | server_hostname | server_intranet_ip | server_internet_ip | server_shelves_date | update_time                | is_delete |
 ---- ----------------- -------------------- -------------------- --------------------- ---------------------------- ----------- 
|  1 | 测试服务器      | 172.16.5.1         | 223.5.5.5          | 2019-06-10          | 2019-06-10 14:56:46.425830 |         0 |
 ---- ----------------- -------------------- -------------------- --------------------- ---------------------------- ----------- 
1 row in set (0.00 sec)

mysql> 

由一模型类条件查询多模型类数据: 语法如下:

代码语言:javascript复制
一模型类关联属性名__一模型类属性名__条件运算符=值

例:查询服务器为“测试服务器”的所有中间件信息。

代码语言:javascript复制
mysql> select * from assetinfo_middlewareinfo as m join assetinfo_serverinfo as s on m.server_id = s.id where s.server_hostname = '测试服务器';
 ---- ----------- ------- ----------- ----------- ---------------------------- ---------------------------- ---- ----------------- -------------------- -------------------- --------------------- ----------- ---------------------------- 
| id | name      | port  | server_id | is_delete | shelves_date               | update_time                | id | server_hostname | server_intranet_ip | server_internet_ip | server_shelves_date | is_delete | update_time                |
 ---- ----------- ------- ----------- ----------- ---------------------------- ---------------------------- ---- ----------------- -------------------- -------------------- --------------------- ----------- ---------------------------- 
|  1 | memcached | 11211 |         1 |         1 | 2019-06-10 14:56:46.150556 | 2019-06-10 17:37:51.365155 |  1 | 测试服务器      | 172.16.5.1         | 223.5.5.5          | 2019-06-10          |         0 | 2019-06-10 14:56:46.425830 |
|  2 | redis     |  6379 |         1 |         1 | 2019-06-10 14:56:46.150556 | 2019-06-10 17:38:20.712862 |  1 | 测试服务器      | 172.16.5.1         | 223.5.5.5          | 2019-06-10          |         0 | 2019-06-10 14:56:46.425830 |
|  5 | test      |   123 |         1 |         1 | 2019-06-10 17:05:16.632773 | 2019-06-10 17:05:16.632773 |  1 | 测试服务器      | 172.16.5.1         | 223.5.5.5          | 2019-06-10          |         0 | 2019-06-10 14:56:46.425830 |
|  6 | test      |   123 |         1 |         1 | 2019-06-10 17:06:20.120658 | 2019-06-10 17:06:20.121656 |  1 | 测试服务器      | 172.16.5.1         | 223.5.5.5          | 2019-06-10          |         0 | 2019-06-10 14:56:46.425830 |
|  7 | test      |   123 |         1 |         1 | 2019-06-10 17:06:43.193412 | 2019-06-10 17:06:43.193412 |  1 | 测试服务器      | 172.16.5.1         | 223.5.5.5          | 2019-06-10          |         0 | 2019-06-10 14:56:46.425830 |
|  8 | test      |   123 |         1 |         1 | 2019-06-10 17:07:03.747395 | 2019-06-10 17:07:03.747395 |  1 | 测试服务器      | 172.16.5.1         | 223.5.5.5          | 2019-06-10          |         0 | 2019-06-10 14:56:46.425830 |
|  9 | test      |   123 |         1 |         1 | 2019-06-10 17:08:43.372097 | 2019-06-10 17:08:43.372097 |  1 | 测试服务器      | 172.16.5.1         | 223.5.5.5          | 2019-06-10          |         0 | 2019-06-10 14:56:46.425830 |
| 10 | test      |   123 |         1 |         1 | 2019-06-10 17:09:37.877019 | 2019-06-10 17:09:37.877019 |  1 | 测试服务器      | 172.16.5.1         | 223.5.5.5          | 2019-06-10          |         0 | 2019-06-10 14:56:46.425830 |
| 11 | test      |   123 |         1 |         1 | 2019-06-10 17:11:45.403627 | 2019-06-10 17:11:45.403627 |  1 | 测试服务器      | 172.16.5.1         | 223.5.5.5          | 2019-06-10          |         0 | 2019-06-10 14:56:46.425830 |
| 14 | mongodb   |  3306 |         1 |         1 | 2019-06-11 14:01:24.003175 | 2019-06-11 14:06:14.525648 |  1 | 测试服务器      | 172.16.5.1         | 223.5.5.5          | 2019-06-10          |         0 | 2019-06-10 14:56:46.425830 |
| 15 | test      |   123 |         1 |         0 | 2019-06-11 14:04:10.576241 | 2019-06-11 14:04:10.576241 |  1 | 测试服务器      | 172.16.5.1         | 223.5.5.5          | 2019-06-10          |         0 | 2019-06-10 14:56:46.425830 |
| 16 | test      |  3306 |         1 |         0 | 2019-06-11 14:06:05.608006 | 2019-06-11 14:06:05.608006 |  1 | 测试服务器      | 172.16.5.1         | 223.5.5.5          | 2019-06-10          |         0 | 2019-06-10 14:56:46.425830 |
 ---- ----------- ------- ----------- ----------- ---------------------------- ---------------------------- ---- ----------------- -------------------- -------------------- --------------------- ----------- ---------------------------- 
12 rows in set (0.00 sec)

mysql> 

那么模型该怎么写呢?

代码语言:javascript复制
In [1]: from assetinfo.models import ServerInfo,MiddlewareInfo

In [2]: MiddlewareInfo.objects.filter(server_id__server_hostname='测试服务器')
Out[2]: <QuerySet [<MiddlewareInfo: MiddlewareInfo object (1)>, <MiddlewareInfo: MiddlewareInfo object (2)>, <MiddlewareInfo: MiddlewareInfo object (5)>
, <MiddlewareInfo: MiddlewareInfo object (6)>, <MiddlewareInfo: MiddlewareInfo object (7)>, <MiddlewareInfo: MiddlewareInfo object (8)>, <MiddlewareInfo
: MiddlewareInfo object (9)>, <MiddlewareInfo: MiddlewareInfo object (10)>, <MiddlewareInfo: MiddlewareInfo object (11)>, <MiddlewareInfo: MiddlewareInf
o object (14)>, <MiddlewareInfo: MiddlewareInfo object (15)>, <MiddlewareInfo: MiddlewareInfo object (16)>]>

In [3]:

对应执行的SQL如下:

代码语言:javascript复制
mysql> SELECT `assetinfo_middlewareinfo`.`id`, `assetinfo_middlewareinfo`.`name`, `assetinfo_middlewareinfo`.`port`, `assetinfo_middlewareinfo`.`server_id`, `assetinfo_middlewareinfo`.`shelves_date`, `assetinfo_middlewareinfo`.`update_time`, `assetinfo_middlewareinfo`.`is_delete` FROM `assetinfo_middlewareinfo` INNER JOIN `assetinfo_serverinfo` ON (`assetinfo_middlewareinfo`.`server_id` = `assetinfo_serverinfo`.`id`) WHERE `assetinfo_serverinfo`.`server_hostname` = '测试服务器'  LIMIT 21
    -> ;
 ---- ----------- ------- ----------- ---------------------------- ---------------------------- ----------- 
| id | name      | port  | server_id | shelves_date               | update_time                | is_delete |
 ---- ----------- ------- ----------- ---------------------------- ---------------------------- ----------- 
|  1 | memcached | 11211 |         1 | 2019-06-10 14:56:46.150556 | 2019-06-10 17:37:51.365155 |         1 |
|  2 | redis     |  6379 |         1 | 2019-06-10 14:56:46.150556 | 2019-06-10 17:38:20.712862 |         1 |
|  5 | test      |   123 |         1 | 2019-06-10 17:05:16.632773 | 2019-06-10 17:05:16.632773 |         1 |
|  6 | test      |   123 |         1 | 2019-06-10 17:06:20.120658 | 2019-06-10 17:06:20.121656 |         1 |
|  7 | test      |   123 |         1 | 2019-06-10 17:06:43.193412 | 2019-06-10 17:06:43.193412 |         1 |
|  8 | test      |   123 |         1 | 2019-06-10 17:07:03.747395 | 2019-06-10 17:07:03.747395 |         1 |
|  9 | test      |   123 |         1 | 2019-06-10 17:08:43.372097 | 2019-06-10 17:08:43.372097 |         1 |
| 10 | test      |   123 |         1 | 2019-06-10 17:09:37.877019 | 2019-06-10 17:09:37.877019 |         1 |
| 11 | test      |   123 |         1 | 2019-06-10 17:11:45.403627 | 2019-06-10 17:11:45.403627 |         1 |
| 14 | mongodb   |  3306 |         1 | 2019-06-11 14:01:24.003175 | 2019-06-11 14:06:14.525648 |         1 |
| 15 | test      |   123 |         1 | 2019-06-11 14:04:10.576241 | 2019-06-11 14:04:10.576241 |         0 |
| 16 | test      |  3306 |         1 | 2019-06-11 14:06:05.608006 | 2019-06-11 14:06:05.608006 |         0 |
 ---- ----------- ------- ----------- ---------------------------- ---------------------------- ----------- 
12 rows in set (0.00 sec)

mysql> 

自关联

对于地区信息、分类信息等数据,表结构非常类似,每个表的数据量十分有限,为了充分利用数据表的大量数据存储功能,可以设计成一张表,内部的关系字段指向本表的主键,这就是自关联的表结构。

示例数据如下:

创建表的SQL:

代码语言:javascript复制
CREATE TABLE `AREA` (  
  `ID` int(11) NOT NULL,  
  `PARENT_ID` int(11) NOT NULL DEFAULT '0' COMMENT '父级ID',  
  `NAME` varchar(50) NOT NULL COMMENT '名称',  
  `SHORT_NAME` varchar(50) NOT NULL COMMENT '简称',  
  `LONGITUDE` float NOT NULL DEFAULT '0' COMMENT '经度',  
  `LATITUDE` float NOT NULL DEFAULT '0' COMMENT '纬度',  
  `LEVEL` int(1) NOT NULL COMMENT '等级(1省/直辖市,2地级市,3区县,4镇/街道)',  
  `SORT` int(3) NOT NULL DEFAULT '1' COMMENT '排序',  
  `STATUS` int(1) NOT NULL DEFAULT '0' COMMENT '状态(0禁用/1启用)',  
  PRIMARY KEY (`ID`)  
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

那么为了更好实验,先来写上这个表结构的模型类,如下:

代码语言:javascript复制
# 全国区域信息
class AREA(models.Model):
    ID = models.AutoField(primary_key=True,db_column='ID',auto_created=True, serialize=False, verbose_name='ID')
    PARENT_ID = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True,db_column='PARENT_ID')  # 父级id
    NAME = models.CharField(max_length=50,default=None,db_column='NAME')  # 名称
    SHORT_NAME = models.CharField(max_length=50,default=None)  # 简称
    LONGITUDE = models.FloatField(default=0)  # 经度
    LATITUDE = models.FloatField(default=0) # 纬度
    LEVEL = models.IntegerField(default=1)# 等级
    SORT = models.IntegerField(default=1) # 排序
    STATUS = models.IntegerField(default=1) # 状态

    class Meta:
        db_table = 'AREA' # 设置表名为 AREA

执行数据迁移:

代码语言:javascript复制
python3 manage.py makemigrations
python3 manage.py migrate

到mysql中查看创建的表结构,如下:

代码语言:javascript复制
mysql> desc AREA;
 ------------ ------------- ------ ----- --------- ---------------- 
| Field      | Type        | Null | Key | Default | Extra          |
 ------------ ------------- ------ ----- --------- ---------------- 
| ID         | int(11)     | NO   | PRI | NULL    | auto_increment |
| Name       | varchar(50) | NO   |     | NULL    |                |
| SHORT_NAME | varchar(50) | NO   |     | NULL    |                |
| LONGITUDE  | double      | NO   |     | NULL    |                |
| LATITUDE   | double      | NO   |     | NULL    |                |
| LEVEL      | int(11)     | NO   |     | NULL    |                |
| SORT       | int(11)     | NO   |     | NULL    |                |
| STATUS     | int(11)     | NO   |     | NULL    |                |
| PARENT_ID  | int(11)     | YES  | MUL | NULL    |                |
 ------------ ------------- ------ ----- --------- ---------------- 
9 rows in set (0.00 sec)

mysql> 

导入几条数据,如下:

代码语言:javascript复制
In [1]: from assetinfo.models import AREA

In [2]: area1 = AREA()

In [3]: area2 = AREA()

In [4]: area1.NAME = '广东省'

In [6]: area1.SHORT_NAME = '广东'

In [7]: area1.save()

In [8]: area2.NAME = '深圳市'

In [9]: area2.SHORT_NAME = '深圳'

In [10]: area2.PARENT_ID = area1

In [11]: area2.save()

In [13]: area3.NAME = '广州市'

In [14]: area3.SHORT_NAME = '广州'

In [15]: area3.PARENT_ID = area1

In [16]: area3.save()

到mysql中查询数据如下:

代码语言:javascript复制
mysql> select * from AREA;
 ---- ------------ ----------- ---------- ------- ------ -------- ----------- ----------- 
| ID | SHORT_NAME | LONGITUDE | LATITUDE | LEVEL | SORT | STATUS | PARENT_ID | NAME      |
 ---- ------------ ----------- ---------- ------- ------ -------- ----------- ----------- 
|  1 | 广东       |         0 |        0 |     1 |    1 |      1 |      NULL | 广东省    |
|  2 | 深圳       |         0 |        0 |     1 |    1 |      1 |         1 | 深圳市    |
|  3 | 广州       |         0 |        0 |     1 |    1 |      1 |         1 | 广州市    |
 ---- ------------ ----------- ---------- ------- ------ -------- ----------- ----------- 
3 rows in set (0.00 sec)

mysql> 

0 人点赞