Python Web开发(十):数据库表的关联

简介: Python Web开发(十):数据库表的关联

网络异常,图片无法展示
|

后端系统开发中, 数据库设计是重中之重。特别是前后端分离的系统, 后端的职责基本就是数据管理, 开发的代码几乎都是围绕数据操作的。虽然,我们教程不是专门讲数据库的, 但也必须讲解常用的 数据库表 和 表之间的关系 的设计 。

目前 使用的数据库系统 主要还是 关系型数据库什么是关系型数据库?就是建立在关系模型基础上的数据库。

大家耳熟能详的mysql、oracle、 sqlserver、SQLite 都是,而 mongodb、Cassandra不是 而关系型数据库,设计的一个难点就是 各种表之间的关联关系 。 常见的3种关联关系就是: 一对多 , 一对一 , 多对多

一、一对多

表之间 一对多 的关系,就是 外键 关联关系 在此models模块中,我们将qq删去:

网络异常,图片无法展示
|
因为在数据库中也有我们的qq数据,我们需要执行命令将其删去:

python manage.py makemigrations

网络异常,图片无法展示
|
然后执行命令运行qq删去:

python manage.py migrate

网络异常,图片无法展示
|
此时我们打开数据库,会发现其中的qq信息已经被我们删去了!
网络异常,图片无法展示
|

比如,我们的 BYSMS 系统中, 已经定义了 客户(Customer) 这张表 。如下所示:

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

现在我们还需要定义 药品(Medicine) 这张表,包括药品名称、编号和描述 这些信息。 这个也很简单,添加如下的类定义

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

网络异常,图片无法展示
|
接下来我们要定义 订单(Order) 这张表,这个Order表 包括 创建日期、客户、药品、数量。 其中: 客户字段对应的客户 只能是 Customer 中的某个客户记录 可以说: Order表里面 一条订单记录的客户 对应 Customer表里面的一条客户记录

而 多条 Order记录里面的客户 是可以对应 Customer 表里面 同一个客户记录的,反过来说,就是:一个客户记录可以对应多条订单记录

这就是一对多的关系,可以用如下图片表示:

网络异常,图片无法展示
|

像这种一对多的关系,数据库中是用 外键 来表示的。

如果一个表中 的 某个字段是外键,那就意味着 这个外键字段的记录的取值,只能是它关联表的某个记录的主键的值。(主键通常是id)

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

比如,我们这里,Customer、Medicine表均没有主键,但是在migrate之后,查看数据库记录就可以发现有一个id字段,且该字段是 主键 (primary key):

网络异常,图片无法展示
|
现在我们要定义 订单 表 Order,

其中客户字段就应该是一个外键,对应Customer表的主键,也就是id字段

Django中定义外键 的方法就是 Model类的该属性字段 值为 ForeignKey 对象,如下所示:

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 类。 意思就是告诉Django: Order表的 customer 字段 指向 Customer表的主键 的一个外键。
  • 另外一个参数 on_delete 指定了 当我们想 删除 外键指向的主键 记录时, 系统的行为。比如 我们要删除客户记录, 那么 Order表中 对应这个客户的订单记录 该如何处理呢?

on_delete 不同取值对应不同的做法,常见的做法如下:1. CASCADE

删除主键记录和 相应的外键表记录。

比如,我们要删除客户张三,在删除了客户表中张三记录同时,也删除Order表中所有这个张三的订单记录

2. PROTECT

禁止删除记录。

比如,我们要删除客户张三,如果Order表中有张三的订单记录,Django系统 就会抛出ProtectedError类型的异常,当然也就禁止删除 客户记录和相关的订单记录了。 除非我们将Order表中所有张三的订单记录都先删除掉,才能删除该客户表中的张三记录。

3. SET_NULL

删除主键记录,并且将外键记录中外键字段的值置为null。 当然前提是外键字段要设置为值允许是null。

比如,我们要删除客户张三时,在删除了客户张三记录同时,会将Order表里面所有的 张三记录里面的customer字段值置为 null。 但是上面我们并没有设置 customer 字段有 null=True 的参数设置,所以,是不能取值为 SET_NULL的。

然后将其应用到我们的数据库中: python manage.py makemigrations

网络异常,图片无法展示
|

最后在migrate一下python manage.py migrate

网络异常,图片无法展示
|
再在数据库里查看:

网络异常,图片无法展示
|

二、一对一

外键是 一对多 的关系, 也可以说是 多对一 的关系。

有的时候,表之间是 一对一 的关系。 比如,某个学校的学生 表 和学生的地址表,就形成一对一的关系,即 一条主键所在表的记录 只能对应一条 外键所在表的记录。

Django 中 用 OneToOneField 对象 实现 一对一 的关系,如下:

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 约束,表示在此表中,所有记录的该字段 取值必须唯一,不能重复。

三、多对多

数据库表还有一种 多对多 的关系。

在我们的 BYSMS系统中, 一个订单可以采购多种药品,就对应 Medicine表里面的多种药品;而一种药品也可以被多个订单采购, 那么Order表 和 Medicine表 之间就形成了多对多的关系。

其对应关系可以用下图来表示:

网络异常,图片无法展示
|
Django是通过 ManyToManyField 对象 表示 多对多的关系的。

如下所示:

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')

将其放到我们的model文件中:

网络异常,图片无法展示
|

指定Order表和 Medicine 表 的多对多关系, 其实Order表中并不会产生一个 叫 medicines 的字段。 Order表和 Medicine 表 的多对多关系 是 通过另外一张表, 也就是 through 参数 指定的 OrderMedicine 表 来确定的。 migrate的时候,Django会自动产生一张新表 (这里就是 common_ordermedicine)来 实现 order 表 和 medicine 表之间的多对多的关系。 大家可以执行下面两行命令 migrate 试一下:

python manage.py makemigrations common
python manage.py migrate

网络异常,图片无法展示
|
就会发现产生如下的一张新表 common_ordermedicine:
网络异常,图片无法展示
|
可以发现这张表中有 order_idmedicine_id 两个字段。 比如一个order表的订单id 为 1, 如果该订单中对应的药品有3种,它们的id分别 为 3,4,5。 那么就会有类似这样的这样3条记录在 common_order_medicine 表中。
网络异常,图片无法展示
|

四、实现代码

其中药品管理部分比较简单, 和前面的 customer.py 的代码 基本类似。

我们在 mgr 目录下面新建 medicine.py,处理 客户端发过来的 列出药品、添加药品、修改药品、删除药品 的请求。

如下所示:

from django.http import JsonResponse
# 导入 Medicine 对象定义
from  common.models import  Medicine
import json
def dispatcher(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})

网络异常,图片无法展示
|
实现了请求处理的模块后,我们可以在 mgr\urls.py 里面加上 对 medicine 的 请求处理 路由设置:

from django.urls import path
from mgr import customer,sign_in_out,medicine,order
urlpatterns = [
    path('customers', customer.dispatcher),
    path('medicines', medicine.dispatcher), # 加上这行
    path('signin', sign_in_out.signin),
    path('signout', sign_in_out.signout),
]

我的前端代码已经开发好了对药品的 增删改查处理, 所以可以和我们上面的代码进行集成测试了。运行服务,在界面上操作测试一下。

网络异常,图片无法展示
|
网络异常,图片无法展示
|
⬇️⬇️ ⬇️ 商务合作|交流学习|粉丝福利|Python全套资料⬇️ ⬇️ ⬇️ 欢迎联系~

目录
相关文章
|
2月前
|
SQL 关系型数据库 数据库
Python SQLAlchemy模块:从入门到实战的数据库操作指南
免费提供Python+PyCharm编程环境,结合SQLAlchemy ORM框架详解数据库开发。涵盖连接配置、模型定义、CRUD操作、事务控制及Alembic迁移工具,以电商订单系统为例,深入讲解高并发场景下的性能优化与最佳实践,助你高效构建数据驱动应用。
378 7
|
2月前
|
算法 Java Go
【GoGin】(1)上手Go Gin 基于Go语言开发的Web框架,本文介绍了各种路由的配置信息;包含各场景下请求参数的基本传入接收
gin 框架中采用的路优酷是基于httprouter做的是一个高性能的 HTTP 请求路由器,适用于 Go 语言。它的设计目标是提供高效的路由匹配和低内存占用,特别适合需要高性能和简单路由的应用场景。
279 4
|
6月前
|
缓存 JavaScript 前端开发
鸿蒙5开发宝藏案例分享---Web开发优化案例分享
本文深入解读鸿蒙官方文档中的 `ArkWeb` 性能优化技巧,从预启动进程到预渲染,涵盖预下载、预连接、预取POST等八大优化策略。通过代码示例详解如何提升Web页面加载速度,助你打造流畅的HarmonyOS应用体验。内容实用,按需选用,让H5页面快到飞起!
|
6月前
|
JavaScript 前端开发 API
鸿蒙5开发宝藏案例分享---Web加载时延优化解析
本文深入解析了鸿蒙开发中Web加载完成时延的优化技巧,结合官方案例与实际代码,助你提升性能。核心内容包括:使用DevEco Profiler和DevTools定位瓶颈、四大优化方向(资源合并、接口预取、图片懒加载、任务拆解)及高频手段总结。同时提供性能优化黄金准则,如首屏资源控制在300KB内、关键接口响应≤200ms等,帮助开发者实现丝般流畅体验。
|
前端开发 JavaScript Shell
鸿蒙5开发宝藏案例分享---Web页面内点击响应时延分析
本文为鸿蒙开发者整理了Web性能优化的实战案例解析,结合官方文档深度扩展。内容涵盖点击响应时延核心指标(≤100ms)、性能分析工具链(如DevTools时间线、ArkUI Trace抓取)以及高频优化场景,包括递归函数优化、网络请求阻塞解决方案和setTimeout滥用问题等。同时提供进阶技巧,如首帧加速、透明动画陷阱规避及Web组件初始化加速,并通过优化前后Trace对比展示成果。最后总结了快速定位问题的方法与开发建议,助力开发者提升Web应用性能。
|
3月前
|
数据采集 关系型数据库 MySQL
python爬取数据存入数据库
Python爬虫结合Scrapy与SQLAlchemy,实现高效数据采集并存入MySQL/PostgreSQL/SQLite。通过ORM映射、连接池优化与批量提交,支持百万级数据高速写入,具备良好的可扩展性与稳定性。
|
6月前
|
JSON 开发框架 自然语言处理
【HarmonyOS Next之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(三)
本文主要介绍了应用开发中的三大核心内容:生命周期管理、资源限定与访问以及多语言支持。在生命周期部分,详细说明了应用和页面的生命周期函数及其触发时机,帮助开发者更好地掌控应用状态变化。资源限定与访问章节,则聚焦于资源限定词的定义、命名规则及匹配逻辑,并阐述了如何通过 `$r` 引用 JS 模块内的资源。最后,多语言支持部分讲解了如何通过 JSON 文件定义多语言资源,使用 `$t` 和 `$tc` 方法实现简单格式化与单复数格式化,为全球化应用提供便利。
268 104
|
6月前
|
JavaScript 前端开发 API
【HarmonyOS Next之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(二)
本文介绍了HarmonyOS应用开发中的HML、CSS和JS语法。HML作为标记语言,支持数据绑定、事件处理、列表渲染等功能;CSS用于样式定义,涵盖尺寸单位、样式导入、选择器及伪类等特性;JS实现业务逻辑,包括ES6语法支持、对象属性、数据方法及事件处理。通过具体代码示例,详细解析了页面构建与交互的实现方式,为开发者提供全面的技术指导。
287 104
|
6月前
|
开发框架 编解码 JavaScript
【HarmonyOS Next之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(一)
该文档详细介绍了一个兼容JS的类Web开发范式的方舟开发框架,涵盖概述、文件组织、js标签配置及app.js等内容。框架采用HML、CSS、JavaScript三段式开发方式,支持单向数据绑定,适合中小型应用开发。文件组织部分说明了目录结构、访问规则和媒体文件格式;js标签配置包括实例名称、页面路由和窗口样式信息;app.js则描述了应用生命周期与对象管理。整体内容旨在帮助开发者快速构建基于方舟框架的应用程序。
291 102
|
3月前
|
存储 数据库 开发者
Python SQLite模块:轻量级数据库的实战指南
本文深入讲解Python内置sqlite3模块的实战应用,涵盖数据库连接、CRUD操作、事务管理、性能优化及高级特性,结合完整案例,助你快速掌握SQLite在小型项目中的高效使用,是Python开发者必备的轻量级数据库指南。
337 0

推荐镜像

更多