Python 框架学习 Django篇 (六) 数据表关联、ORM关联

在后端服务器开发中,特别是前后端分离的架构中数据库是非常重要的,后端主要就是负责管理数据,而我们经常使用的mysql、oracle 都是关系型数据库,什么是关系型数据库?就是建立在关系模型基础上的数据库,而最难处理的就是各个表之间的关联关系,一般这种关系分为三种: 一对一 、一对多、多对多

 一、数据表关联

1、一对多

表之间以对多的关系就是数据库中的 "外键"  ,下面我们举个例子,比如一个医药系统中肯定会有客户的信息吧,我们先定义一个客户的基本信息(客户名称、联系电话、居住地址)

  vi Django_demo/paas/models.py

class Customer(models.Model):# 客户名称name = models.CharField(max_length=200)# 联系电话phonenumber = models.CharField(max_length=200)# 地址address = models.CharField(max_length=200)

我们是一个医药系统,肯定存在很多不同的药品类型,同样也需要定义一个药品类型的表

Medicine药品表 ,包含一些(药品名称、编号、描述信息)

  vi Django_demo/paas/models.py

class Medicine(models.Model):# 药品名name = models.CharField(max_length=200)# 药品编号sn = models.CharField(max_length=200)# 描述desc = models.CharField(max_length=200)

有了药品信息、客户信息,那么只要存在销售的话就一定会有订单信息

想一想,我们订单的信息是不是和上面的两张表多少有一些关联,比如订单中需要用到客户信息和药品信息

在实际观察中,我们发现订单表里面会同时需要拿到上面两张表中的数据,在下图中我们可以看到一个客户同时可能会有多个订单,这种情况就是一对多,或者说多对一

 

像是这种一对多的关系,在数据库中是以外键形式表示的,如果说一个表中的字段是外键,那么他的值一定来源与其他表的主键

另外,我们定义表的 Model类的时候,如果没有指定主键字段,migrate 的时候 Django 会为该Model对应的数据库表自动生成一个id字段,作为主键

#导入数据库
python manage.py makemigrations
python manage.py migrate #查看
desc paas_customer;

现在我们要生成订单表,按照实际情况我们订单表的字段里面也会有客户的信息表示谁下的订单,而用户的信息需要使用外键去关联客户的主键,而客户表也就是customer表的主键就是id字段,Django中定义外键 的方法就是 Model类的该属性字段 值为(ForeignKey)

 vi Django_demo/paas/models.py

import datetime
class Order(models.Model):# 订单名name = models.CharField(max_length=200,null=True,blank=True)# 创建日期create_date = models.DateTimeField(default=datetime.datetime.now)# 客户customer = models.ForeignKey(Customer,on_delete=models.PROTECT)

上面定义的customer是外键,让他去找Customer表的主键获取数据,而这里设置了一个on_delete的参数,这个意思是当主键被删除了那么外键这个数据还要不要了

1、CASCADE: 跟随主机一起把外键数据删除

2、PROTECT   禁止删除,如果非要删除就先清除外键数据后,才能删除对应主键

3、SET_NULL   删除后外键数据修改为null

注意

      外键字段,实际在数据库表中的字段名是DjangoForeignKey定义字段名加上后缀"_id"

比如上面,在执行了 migrate 命令更新数据库后,customer 这个外键字段实际上在 数据库表中的字段名 是 customer_id

python manage.py makemigrations
python manage.py migrate #查看
desc paas_info;

返回

mysql> desc paas_order;
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | bigint       | NO   | PRI | NULL    | auto_increment |
| name        | varchar(200) | YES  |     | NULL    |                |
| create_date | datetime(6)  | NO   |     | NULL    |                |
| customer_id | bigint       | NO   | MUL | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

2、一对一

上面的外键案例,可以说是一对多或者多对一,而有时候是一对一的情况

比如,某个学校的学生表 和学生的地址表,就形成一对一的关系,即 一条主键所在表的记录 只能对应一条 外键所在表的记录,而Django 中 用 OneToOneField 对象 实现 一对一 的关系

 vi Django_demo/paas/models.py

class Student(models.Model):# 姓名name = models.CharField(max_length=200)# 班级classname = models.CharField(max_length=200)# 描述desc = models.CharField(max_length=200)class ContactAddress(models.Model):# 一对一 对应学生 student = models.OneToOneField(Student, on_delete=models.PROTECT)# 家庭homeaddress = models.CharField(max_length=200)# 电话号码phone = models.CharField(max_length=200)

 Django发现这样一对一定定义,它会在migrate的时候,在数据库中定义该字段为外键的同时, 加上 unique=True 约束,表示在此表中,所有记录的该字段 取值必须唯一,不能重复

 3、多对多

数据库中还存在一种多对多的关系,在order订单表中

一个订单可以采购多种药品,就对应 Medicine表里面的多种药品;

而一种药品也可以被多个订单采购, 那么Order表 和 Medicine表 之间就形成了多对多的关系

 

 Django是通过 ManyToManyField 对象 表示 多对多的关系的

 vi Django_demo/paas/models.py

import datetime
class Order(models.Model):# 订单名name = models.CharField(max_length=200,null=True,blank=True)# 创建日期create_date = models.DateTimeField(default=datetime.datetime.now)# 客户customer = models.ForeignKey(Customer,on_delete=models.PROTECT)# 订单购买的药品,和Medicine表是多对多 的关系medicines = models.ManyToManyField(Medicine, through='OrderMedicine')class OrderMedicine(models.Model):#添加外键order = models.ForeignKey(Order, on_delete=models.PROTECT)medicine = models.ForeignKey(Medicine, on_delete=models.PROTECT)# 订单中药品的数量  一种特殊的类型,表示非负整数amount = models.PositiveIntegerField()  

我们上面通过medicines = models.ManyToManyField(Medicine, through='OrderMedicine')

去指定Order表和Medicine表的对应关系,其实不会在Order表上面创建medicines的字段

python manage.py makemigrations
python manage.py migrate #查看
desc paas_OrderMedicine;

 

4、管理药品实现

我们在 mgr 目录下面新建 medicine.py,处理 客户端发过来的 列出药品、添加药品、修改药品、删除药品 的请求,需要运用前面的数据库增删改查的方法

vi Django_demo/mgr/medicine.py

from django.http import JsonResponse# 导入 Medicine 对象定义(这块可能显示模块导入不正常,忽略)
from  paas.models import  Medicineimport jsondef Orderdispatcher(request):# 根据session判断用户是否是登录的管理员用户if 'usertype' not in request.session:return JsonResponse({'ret': 302,'msg': '未登录','redirect': '/mgr/sign.html'},status=302)if request.session['usertype'] != 'mgr':return JsonResponse({'ret': 302,'msg': '用户非mgr类型','redirect': '/mgr/sign.html'},status=302)# 将请求参数统一放入request 的 params 属性中,方便后续处理# GET请求 参数 在 request 对象的 GET属性中if request.method == 'GET':request.params = request.GET# POST/PUT/DELETE 请求 参数 从 request 对象的 body 属性中获取elif request.method in ['POST','PUT','DELETE']:# 根据接口,POST/PUT/DELETE 请求的消息体都是 json格式request.params = json.loads(request.body)# 根据不同的action分派给不同的函数进行处理action = request.params['action']if action == 'list_medicine':return listmedicine(request)elif action == 'add_medicine':return addmedicine(request)elif action == 'modify_medicine':return modifymedicine(request)elif action == 'del_medicine':return deletemedicine(request)else:return JsonResponse({'ret': 1, 'msg': '不支持该类型http请求'})def listmedicine(request):# 返回一个 QuerySet 对象 ,包含所有的表记录qs = Medicine.objects.values()# 将 QuerySet 对象 转化为 list 类型# 否则不能 被 转化为 JSON 字符串retlist = list(qs)return JsonResponse({'ret': 0, 'retlist': retlist})def addmedicine(request):info    = request.params['data']# 从请求消息中 获取要添加客户的信息# 并且插入到数据库中medicine = Medicine.objects.create(name=info['name'] ,sn=info['sn'] ,desc=info['desc'])return JsonResponse({'ret': 0, 'id':medicine.id})def modifymedicine(request):# 从请求消息中 获取修改客户的信息# 找到该客户,并且进行修改操作medicineid = request.params['id']newdata    = request.params['newdata']try:# 根据 id 从数据库中找到相应的客户记录medicine = Medicine.objects.get(id=medicineid)except Medicine.DoesNotExist:return  {'ret': 1,'msg': f'id 为`{medicineid}`的药品不存在'}if 'name' in  newdata:medicine.name = newdata['name']if 'sn' in  newdata:medicine.sn = newdata['sn']if 'desc' in  newdata:medicine.desc = newdata['desc']# 注意,一定要执行save才能将修改信息保存到数据库medicine.save()return JsonResponse({'ret': 0})def deletemedicine(request):medicineid = request.params['id']try:# 根据 id 从数据库中找到相应的药品记录medicine = Medicine.objects.get(id=medicineid)except Medicine.DoesNotExist:return  {'ret': 1,'msg': f'id 为`{medicineid}`的客户不存在'}# delete 方法就将该记录从数据库中删除了medicine.delete()return JsonResponse({'ret': 0})

添加路由

vi Django_demo/mgr/urls.py

from django.urls import pathfrom .k8s import dispatcher
from .sign_in_out import signin,signoutfrom .medicine import orderdispatcher  #添加
urlpatterns = [path('customers/', dispatcher),path('medicines/', orderdispatcher),  #添加 必须带斜杠path('signin', signin),path('signout', signout),]

5、添加药品

vi main.py

import  requests,pprint#添加认证
payload = {'username': 'root','password': '12345678'
}
#发送登录请求
response = requests.post('http://127.0.0.1:8000/api/mgr/signin',data=payload)
#拿到请求中的认证信息进行访问
set_cookie = response.headers.get('Set-Cookie')# 构建添加 客户信息的 消息体,是json格式
payload = {"action":"add_medicine","data":{"name":"板蓝根","sn":"133","desc":"感冒药"}
}
url='http://127.0.0.1:8000/api/mgr/medicines/'
if set_cookie:# 将Set-Cookie字段的值添加到请求头中headers = {'Cookie': set_cookie}# 发送请求给web服务response = requests.post(url,json=payload,headers=headers)pprint.pprint(response.json())

json类型说明

刚才上面我们使用了查询和添加数据,但是发现一个问题,两个请求传参的时候稍有不同

#data=payload  表示这个请求携带的参数是以表单的形式也就是字符串形式传输给后端的
requests.post(url,data=payload)#json=payload   表示参数是以json的形式传输给后端的
requests.post(url,json=payload)

在使用时要特别注意,我卡了半天才看到。。

6、查询药品

vi main.py

import  requests,pprintpayload = {'username': 'root','password': '12345678'
}
#发送登录请求
response = requests.post('http://127.0.0.1:8000/api/mgr/signin',data=payload)
#拿到请求中的认证信息进行访问
set_cookie = response.headers.get('Set-Cookie')
if set_cookie:# 将Set-Cookie字段的值添加到请求头中headers = {'Cookie': set_cookie}# 发送带有Cookie的新请求 修改url到新的路由response = requests.get('http://127.0.0.1:8000/api/mgr/medicines/?action=list_medicine',headers=headers)pprint.pprint(response.json())

返回

{'ret': 0,'retlist': [{'desc': '192.168.1.2', 'id': 1, 'name': 'abc', 'sn': '133'},{'desc': '感冒药', 'id': 2, 'name': '板蓝根', 'sn': '133'}]}

第一行是我写错了添加上的,一会当作删除的案例

遇到的问题

在访问url的时候,要确定url访问时是否需要带上/  如果定义的urls上有/,那边必须要带上斜杠不然会报错

7、修改药品

vi main.py

import  requests,pprint#添加认证
payload = {'username': 'root','password': '12345678'
}
#发送登录请求
response = requests.post('http://127.0.0.1:8000/api/mgr/signin',data=payload)
#拿到请求中的认证信息进行访问
set_cookie = response.headers.get('Set-Cookie')# 构建添加 客户信息的 消息体,是json格式
payload = {"action":"modify_medicine","id": "1","newdata":{"name":"诺氟沙星","sn":"141","desc":"无"}
}
url='http://127.0.0.1:8000/api/mgr/medicines/'if set_cookie:# 将Set-Cookie字段的值添加到请求头中headers = {'Cookie': set_cookie}# 发送请求给web服务response = requests.post(url,json=payload,headers=headers)pprint.pprint(response.json())

再次查询

{'ret': 0,'retlist': [{'desc': '无', 'id': 1, 'name': '诺氟沙星', 'sn': '141'},{'desc': '感冒药', 'id': 2, 'name': '板蓝根', 'sn': '133'}]}

8、删除药品

import  requests,pprint#添加认证
payload = {'username': 'root','password': '12345678'
}
#发送登录请求
response = requests.post('http://127.0.0.1:8000/api/mgr/signin',data=payload)
#拿到请求中的认证信息进行访问
set_cookie = response.headers.get('Set-Cookie')# 构建添加 客户信息的 消息体,是json格式
payload = {"action":"del_medicine","id":"1",
}
url='http://127.0.0.1:8000/api/mgr/medicines/'if set_cookie:# 将Set-Cookie字段的值添加到请求头中headers = {'Cookie': set_cookie}# 发送请求给web服务response = requests.post(url,json=payload,headers=headers)pprint.pprint(response.json())

 返回

{'ret': 0, 'retlist': [{'desc': '感冒药', 'id': 2, 'name': '板蓝根', 'sn': '133'}]}

可以看到除了查询以外,增加、修改、删除的操作基本是一致的,只需要修改携带参数中的动作以及传入的参数值即可

二、数据库关联操作(sql)

1、一对多

#先查询用户的id,然后基于id查询外键对应的订单
select * from paas_order where customer_id = (select id from paas_customer where name = "zhangsan");

一对多,我们查询指定的一个客户id,然后基于id去订单表中获取所以用户相关的订单

2、多对多

我们上面使用的时候添加过这么一个表  OrderMedicine 

class OrderMedicine(models.Model):#添加外键order = models.ForeignKey(Order, on_delete=models.PROTECT)medicine = models.ForeignKey(Medicine, on_delete=models.PROTECT)# 订单中药品的数量  一种特殊的类型,表示非负整数amount = models.PositiveIntegerField()  

多对多,表示什么意思呢,我们建立的上面的表是将order订单表 和Medicine药品表进行关联的一个中间表,我们在给前面两张表添加数据的时候,还需要单独去给中间表添加一次数据,用来声明订单和药品的关系,通过这张中间表,我们就能查询药品是那些客户进行购买,也可以查询那些客户购买了那些药品,我们做一个案例

 Django_demo/mgr/admin.py

from  paas.models import Customer,Medicine,Order,OrderMedicine
admin.site.register([Customer,Medicine,Order,OrderMedicine])

 访问admin页面,前面我们添加了很多的数据除了客户表信息以外都先删除一下

 

我们先给Medicines药品表  和orders 订单表 添加一条信息

 

上面给药品添加了感冒颗粒,订单表中给张三用户添加了一个test的订单 ,现在是没有任何关联机制的,这两张表也没有交互,我们在order medicines表中添加下两个订单的关联

 我们关联了第一条数据,将刚才的订单信息和药品信息以及数量一并放入到了中间表中

mysql> select * from paas_ordermedicine;
+----+--------+-------------+----------+
| id | amount | medicine_id | order_id |
+----+--------+-------------+----------+
|  1 |     10 |           6 |        5 |
+----+--------+-------------+----------+
1 row in set (0.00 sec)

 这里因为存放了药品的id和订单的id,我们就可以根据这个表来查询关联的数据信息,比如我想要看看订单表里面test订单对应下单了什么药品,买了多少

#找到订单中的id
select id from paas_order where name = "test";#基于id查询关系表中药品
select medicine_id,amount  from paas_ordermedicine where order_id = 5;#命令组合
mysql> select medicine_id,amount  from paas_ordermedicine where order_id = (select id from paas_order where name = "test");
+-------------+--------+
| medicine_id | amount |
+-------------+--------+
|           6 |     10 |
+-------------+--------+
1 row in set (0.00 sec)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/120886.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Sql Server中的表组织和索引组织(聚集索引结构,非聚集索引结构,堆结构)

正文 SqlServer用三种方法来组织其分区中的数据或索引页: 1、聚集索引结构 聚集索引是按B树结构进行组织的,B树中的每一页称为一个索引节点。每个索引行包含一个键值和一个指针。指针指向B树上的某一中间级页(比如根节点指向中间级节点中的…

超全面测评!2023年最常用的15款原型设计工具

在互联网和软件行业中,原型设计是产品经理、交互设计师以及运营人员等职位必不可少的一门技能。原型设计工具的重要性也是人尽皆知,这都是因为原型设计工具是帮助设计者表达产品想法、功能设定及流程逻辑的最佳方式。 今天就为大家带来原型设计工具的相…

「林曦的亲子美育」讲讲关于阅读的那些事儿

「林曦的亲子美育」是“林曦的小世界”2023年策划的一档新栏目。林曦老师作为一个“小男生的妈妈”,在这些年分享了许多关于亲子教育的心得:以“美”作为连接和最高标准,会护持着小朋友的选择和人生。教育是一个生活的过程。做一餐饭、读一本书、看一张画…

HCL模拟器选路实验案例

此选路题目选自职业院校技能竞赛中的一道题比较考验思路,适合于参加新华三杯大赛以及网络专业的同学,当做练习题目进行解题​​​​​​​ 题目 1.S1、S2、R1、R2运行ospf进程100,区域0,R1、R2、R3、R4、R5运行ospf进程200&#…

Ocelot简易教程目录

Ocelot简易教程目录 这里写目录标题 Ocelot简易教程目录 Ocelot简易教程(一)之Ocelot是什么Ocelot简易教程(二)之快速开始1Ocelot简易教程(二)之快速开始2Ocelot简易教程(三)之主要特…

安装虚拟机找不到虚拟网啦1(eth1)不出现

一、安装虚拟机找不到虚拟网啦1(eth1)不出现 1、先安装virtualbox 2、再安装vagrant 3、在windows使用ipconfig没有VirtualBoxHost-OnlyNetWork解决方法 1) 解决办法 在windows的设置中找到 网络和Internet 选项,选择右侧 更改适配器选项 …

微信小程序开发之投票管理及小程序UI的使用

目录 一、小程序UI 1.讲述 2. 介绍vantWeapp 3. 使用vantWeapp 安装 构建 依赖 引用 二、后端 1. 后端实体对象 2. 后端接口 3. 实现类 4. 请求处理类 三、前端 1. 定义路径 2. 页面引用 3. 页面 4. 页面美化 5. 数据 6. 效果展示 一、小程序UI 1.讲述 小…

信息学奥赛一本通2061:【例1.2】梯形面积

2061:【例1.2】梯形面积 时间限制: 1000 ms 内存限制: 65536 KB 提交数: 172550 通过数: 68183 【题目描述】 在梯形中阴影部分面积是150平方厘米,求梯形面积。 【输入】 (无) 【输出】 输出梯形面积(保留两位小数&a…

HarmonyOS开发:NodeJs脚本实现组件化动态切换

前言 上篇文章,我们使用NodeJs脚本完成了HarmonyOS项目的组件化运行,但是由于脚本是基于4.0.0.400版本的DevEco Studio开发的,可能在配置文件的修改上有些许差距,那么遇到这种情况怎么办,一种是再写一套针对性的脚本文…

【Oracle】[INS-30131]执行安装程序验证所需的初始设置失败。

这里写目录标题 一、问题描述1 报错内容1.1 无法从节点“kotin”检索 exectask 的版本1.2 工作目录"xxx"无法在节点"kotin"上使用 2 相关环境2.1 安装软件2.2 安装系统 3 解决思路分析 二、解决方案1 方案一、 满足验证条件 - 不换系统1.1 第一步、检查文件…

p5.js 到底怎么设置背景图?

本文简介 点赞 关注 收藏 学会了 在 《p5.js 光速入门》 里我们学过加载图片元素,学过过背景色的用法,但当时没提到背景图要怎么使用。 本文就把背景图这部分内容补充完整,并且会提到在 p5.js 里使用背景图的一些注意点。 背景图的用法…

在虚拟环境中,通过pip安装tensorflow

目录 激活python虚拟环境,更新pip 通过pip 安装tensorflow 确定python版本: ​编辑安装tensorflow: ​编辑 为什么使用pip安装tensorflow? 激活python虚拟环境,更新pip 命令为python -m pip install --upgrade pip 通过pip 安装tensorf…

C语言之结构体和共用体详解

目录 结构体 结构体的定义和使用 结构体数组的使用 结构体指针的使用 结构体大小的计算 共用体 共用体的定义和使用 typedef用法详解 enum枚举类型 结构体 结构体的定义和使用 C语言的结构体(Struct)是一种自定义的数据类型,它允许…

时间、空间复杂度的例题详解

文章前言 上篇文章带大家认识了数据结构和算法的含义&#xff0c;以及理解了时间、空间复杂度&#xff0c;那么接下来来深入理解一下时间、空间复杂度。 时间复杂度实例 实例1 // 计算Func2的时间复杂度&#xff1f; void Func2(int N) {int count 0;for (int k 0; k <…

图的应用4.0-----关键路径(AOE网)

目录 前言 AOE网 1.基本概念 2.应用 关键路径 1.算法理论 2.代码实现&#xff08;C/C&#xff09; 前言 前面学习了图AOV网的应用&#xff0c;即拓扑排序&#xff0c;那这一期我们学习AOE网的应用&#xff0c;这是一个图的一个很广泛问题&#xff0c;也就是关键路径。那…

k8s中kubectl陈述式资源管理

目录 1、 理论 1.1、 管理k8s核心资源的三种基本方法 &#xff1a; 1.1.1陈述式的资源管理方法&#xff1a; 1.1.1.1、优点&#xff1a; 1.1.1.2、缺点&#xff1a; 1.1.2、声明式资源管理方法 1.1.3、GUI式资源管理方法 1.2、陈述式资源管理方法 2. 对资源的增、删、…

[推荐]Linux安装与配置虚拟机之虚拟机服务器坏境配置

&#x1f3ac; 艳艳耶✌️&#xff1a;个人主页 &#x1f525; 个人专栏 &#xff1a;《Spring与Mybatis集成整合》《Vue.js使用》 ⛺️ 越努力 &#xff0c;越幸运。 一.操作系统 1. 简介 操作系统&#xff08;perating System&#xff0c;简称OS&#xff09;是一种系统软件…

决定放弃uniapp开发了,因为它实在是没有taro友好

被uniapp折腾了两天&#xff0c;实在是受不了它对vue3的支持和react的支持&#xff0c;可以这么说&#xff0c;uniapp完全没有支持vue3和react&#xff0c;这么说我觉得一点也不过分。相对于折腾了两天uniapp来讲&#xff0c;我使用taro只花了1个小时不到&#xff0c;就可以完美…

【java学习—八】单例设计模式(5)

文章目录 1. 相关概念2. 单例设计模式-饿汉式3. 单例设计模式-懒汉式4. 总结 1. 相关概念 单例&#xff1a;只有一个实例&#xff08;实例化对象&#xff09; 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式就像是经典的…

【鸿蒙软件开发】ArkTS通用事件

文章目录 前言一、点击事件1.1 基础介绍1.2 ClickEvent对象说明1.3 示例代码 二、触摸事件2.1 基础介绍2.2 ClickEvent对象说明2.3 示例代码 二、焦点事件2.2 基础介绍3.2 示例代码 总结 前言 在我们的ArkTS中有一些通用的事件&#xff0c;他们在所有的组件中都可以用&#xf…