创新实训2024.05.26日志:落地基于硬盘的数据库服务

news2024/11/16 0:54:48

1. 需求任务列表

以下描述易学大模型软件的web应用的功能。

  1. 用户注册
    1. 用户邮箱,密码,验证码
    2. 开启官方邮箱,用来发验证码(QQ 网易都支持开启smtp协议,找教程,用邮箱不用手机号是为了省买发短信云服务的钱)
    3. 验证码缓存于redis,5min内有效
    4. 验证密码长度,验证码是否正确
    5. 新用户信息保存于mysql,密码使用md5第三方库进行加密
  2. 用户登录
    1. 用户邮箱与密码登录
    2. 校验身份
    3. 根据用户身份信息(也即邮箱,但不要包含密码)生成token
    4. 将token附带在响应中,发回客户端
  3. 用户完善身份信息
    1. 如姓名、年龄、性别
    2. 对自己的简单介绍等
  4. 用户创建专属知识库
    1. 用户可以创建属于自己的知识库,独立于其他用户的知识库存在
    2. 用户可以批量上传任意格式的文件,并将其加入知识库中
  5. 用户创建新会话
    1. 用户可以舍弃之前的上下文,创建一个全新的会话
    2. 该会话用户自己独立的上下文环境
  6. 用户还原历史会话
    1. 用户可以还原之前的历史会话信息,联系上下文继续与大模型chat
  7. 用户进行文件上传会话
    1. 用户可以上传一个或多个文件,但不添加至向量知识库,仅就这几个文件与大模型对话
  8. 用户创建易学知识博客
    1. 仿照csdn等博客网站,可以创建标题,提供博客标签,选择博客封面等进行博客撰写
    2. 支持markdown富文本编辑器
    3. 以markdown格式在网页端显示
  9. 易学贴吧/论坛
    1. 按时间分页显示近期的博客
    2. 用户可以收藏某一篇易学博客
    3. 用户可以在一篇易学博客下进行评论
  10. 管理员后台
    1. 可以查看任意用户的知识库、博客、与大模型的历史会话
    2. 可以封禁某用户及其邮箱,并以邮件的方式通知该用户
    3. 可以解禁某用户及其邮箱,并以邮件的方式通知该用户

2. 硬盘数据库选型

Sqlite(轻量级嵌入式数据库)+SqlAlchemy(Python的orm框架)

Sqlite官网:SQLite Home Page

SqlAlchemy官网:SQLAlchemy - The Database Toolkit for Python

2.1. Sqlite

作为一个嵌入式数据库,Sqlite不以服务进程的方式存在,而是作为文件嵌入在应用进程中。

这里的db文件就是Sqlite的嵌入式数据库文件,每次需要操作数据库时,打开该文件进行curd,操作数据库的会话提交一个事务时(commit),保存修改到该文件。

2.2. SqlAlchemy

SqlAlchemy是Python的一个orm框架,支持多种数据库驱动程序。官方统一地将连接数据库的函数封装了起来(对于所有驱动,都是同一个函数):

from sqlalchemy import create_engine
engine = create_engine("sqlite://", echo=True)

例如sqlite的连接方式就是”sqlite://{你的sqlite的db文件的路径}”。当然,其他数据库也可以,比如通过mysqlconnector进行Mysql数据库的连接。

值得一提的是,SqlAlchemy支持了两种非常强大的机制:类型映射以及依赖管理。此外,他还提供了相当多的语法糖。

类型映射

ORM Mapped Class Configuration — SQLAlchemy 2.0 Documentation

可以将python中的数据类型映射到对应数据库支持的数据类型,采用一个关键词叫做Mapped:

id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
fullname: Mapped[str] = mapped_column(String(30))
nickname: Mapped[Optional[str]]

例如,也许你不知道字符串类型在Sqlite数据库中的具体形式,那么你就可以用Mapped[str]来将python的字符串类型自动映射到Sqlite的字符串类型上,同样的,int,bool,byte等等都可以进行映射。

依赖管理

可以说是关系型数据库外键(Foreign Key)的一个重要机制。

假设现在一个用户(User)有多个与大模型的会话(Conversation),那么显然Conversation表中应该包含对应于用户表主键的外键(例如,User.id)。依赖管理机制可以显式地管理这种一对一、一对多、多对一、多对多的依赖。

在User表的python DDL中:

conversations: Mapped[List["Conversation"]] = relationship(back_populates='owner')  # 拥有的会话集合

List[”Conversation”]意味着一个用户可以有多个会话。

在Conversation的python DDL中:

owner: Mapped["User"] = relationship(back_populates='conversations')

“User”意味着一个会话只能属于一个用户。

CURD语法糖

这种有很多,比如增:

session.add(Conversation(id=conv_id, conv_name=nc.conv_name, create_time=datetime.datetime.utcnow(),
                         user_id=nc.user_id))
session.commit()

这里可以通过对象类型反解析出在哪个表插入数据。

查:

result = session.query(Record).filter(Record.conv_id == conv_id).all()

filter过滤器将充当where的条件,all返回所有满足条件的记录的列表。

其他功能,自查官方文档即可。

3. 数据库表设计

基于目前的需求,设计数据库表共七张:

  1. User:用户信息
  2. Conversation:用户与大模型会话信息
  3. Blog:用户博客信息
  4. KnowledgeBase:用户知识库metaData
  5. Comment:用户博客评论信息
  6. Record:用户与大模型对话记录信息
  7. Administrator:管理员信息

3.1. User表

class User(Base):
    __tablename__ = 'User'

    id: Mapped[str] = mapped_column(primary_key=True)
    email: Mapped[str] = mapped_column()  # 一个邮箱只能注册一个账号
    password: Mapped[str] = mapped_column()
    name: Mapped[str] = mapped_column()
    is_active: Mapped[bool] = mapped_column(default=True)  # 是否被封禁
    age: Mapped[int] = mapped_column()
    sex: Mapped[str] = mapped_column()
    description: Mapped[str] = mapped_column()

    conversations: Mapped[List["Conversation"]] = relationship(back_populates='owner')  # 拥有的会话集合
    blogs: Mapped[List["Blog"]] = relationship(back_populates='owner')  # 拥有的博客集合
    comments: Mapped[List["Comment"]] = relationship(back_populates='owner')  # 拥有的评论集合
    knowledge_bases: Mapped[List["KnowledgeBase"]] = relationship(back_populates='owner')  # 拥有的知识库集合

    def __repr__(self):
        return f'<User(id={self.id}, email={self.email}, name={self.name})>'

3.2. Conversation表

class Conversation(Base):
    __tablename__ = 'Conversation'

    id: Mapped[str] = mapped_column(primary_key=True)
    conv_name: Mapped[str] = mapped_column()
    create_time: Mapped[datetime.datetime] = mapped_column(default=datetime.datetime.utcnow())
    user_id: Mapped[str] = mapped_column(ForeignKey('User.id'))

    owner: Mapped["User"] = relationship(back_populates='conversations')
    records: Mapped[List["Record"]] = relationship(back_populates='conversation')

    def __repr__(self):
        return f'<Conversation(id={self.id}, conv_name={self.conv_name})>'

3.3. Blog表

class Blog(Base):
    __tablename__ = "Blog"

    id: Mapped[str] = mapped_column(primary_key=True)
    title: Mapped[str] = mapped_column()
    content: Mapped[Text] = Column(Text)
    create_time: Mapped[datetime.datetime] = mapped_column(default=datetime.datetime.utcnow())
    user_id: Mapped[str] = mapped_column(ForeignKey('User.id'))

    owner: Mapped["User"] = relationship(back_populates='blogs')
    comments: Mapped[List["Comment"]] = relationship(back_populates='blog')

    def __repr__(self):
        return f'<Blog(id={self.id}, title={self.title})>'

3.4. Comment表

class Comment(Base):
    __tablename__ = "Comment"

    id: Mapped[str] = mapped_column(primary_key=True)
    content: Mapped[str] = mapped_column()
    create_time: Mapped[datetime.datetime] = mapped_column(default=datetime.datetime.utcnow())
    user_id: Mapped[str] = mapped_column(ForeignKey('User.id'))
    blog_id: Mapped[str] = mapped_column(ForeignKey('Blog.id'))

    owner: Mapped["User"] = relationship(back_populates='comments')
    blog: Mapped["Blog"] = relationship(back_populates='comments')

    def __repr__(self):
        return f'<Comment(id={self.id}, content={self.content})>'

3.5. KnowledgeBase表

class KnowledgeBase(Base):
    __tablename__ = "KnowledgeBase"

    id: Mapped[str] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column()
    description: Mapped[str] = mapped_column()
    create_time: Mapped[datetime.datetime] = mapped_column(default=datetime.datetime.utcnow())
    user_id: Mapped[str] = mapped_column(ForeignKey('User.id'))

    owner: Mapped["User"] = relationship(back_populates='knowledge_bases')

    def __repr__(self):
        return f'<KnowledgeBase(id={self.id}, name={self.name})>'

3.6. Administrator表

class Administrator(Base):
    __tablename__ = "Administrator"

    id: Mapped[str] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column()
    password: Mapped[str] = mapped_column()

    def __repr__(self):
        return f'<Administrator(id={self.id}, name={self.name})>'

3.7. Record表

class Record(Base):
    __tablename__ = "Record"

    id: Mapped[str] = mapped_column(primary_key=True)
    content: Mapped[str] = mapped_column()
    is_ai: Mapped[bool] = mapped_column()
    conv_id: Mapped[str] = mapped_column(ForeignKey('Conversation.id'))

    conversation: Mapped["Conversation"] = relationship(back_populates='records')

    def __repr__(self):
        return f'<Record(id={self.id}, content={self.content})>'

4. ER图

5. 依赖倒置原则下的数据库连接

仿照Spring的容器机制,我们希望一个组件的生命周期是伴随web应用的启动而创建,伴随web应用的结束而销毁。其中数据库连接就是这样的一个组件。

所有这样的组件,均在component包下,数据库连接引擎只是其中一个:

# DB_engine.py

db_lock = Lock()
engine = None

def init_db_conn():
    global engine, db_lock
    db_lock.acquire()
    engine = create_engine(f'sqlite:///{SQLITE_CONNECTION["location"]}')

    # 注册连接监听 连接时开启外键约束(默认不开启)
    @event.listens_for(engine, "connect")
    def enable_foreign_keys(dbapi_connection, connection_record):
        cursor = dbapi_connection.cursor()
        cursor.execute("PRAGMA foreign_keys=ON;")
        logging.info("Sqlite数据库已开启外键约束")
        cursor.close()

    db_lock.release()

其中,全局变量engine即为数据库连接引擎。在web应用初始化时,我们调用init_db_conn,随后每次操作数据库,均使用该engine。

这里需要注意的是,Sqlite数据库连接时默认不开启外键约束(例如你可以在User根本不存在的情况下插入一条Conversation),因此我们开启连接时的监听,通过SQL语句打开外键约束选项。

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

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

相关文章

初识C语言——第二十四天

函数的基本使用和递归 1.函数是什么 2.库函数 3.自定义函数 4.函数参数 5.函数调用 6.函数的嵌套调用和链式访问 7.函数的声明和定义 函数是什么 C语言中函数的分类 1.库函数 2.自定义函数 库函数&#xff1a; 简单的总结,C语言常用的库函数都有&#xff1a; #includ…

关于数据库和数据表的基础SQL

目录 一. 数据库的基础SQL 1. 创建数据库 2. 查看当前有哪些数据库 3. 选中数据库 4. 删除数据库 5. 小结 二. 数据表的基础SQL 1. 创建数据表 2. 查看当前数据库中有哪些表 3. 查看指定表的详细情况(查看表的结构) 4. 删除表 5. 小结 一. 数据库的基础SQL 1. 创建…

YOLOv10 | 手把手教你利用yolov10训练自己数据集(含环境搭建 + 参数解析 + 数据集查找 + 模型训练、推理、导出)

一、前言 本文内含YOLOv10网络结构图 各个创新模块手撕结构图 训练教程 推理教程 参数解析 环境搭建 数据集获取等一些有关YOLOv10的内容&#xff01; 目录 一、 前言 二、整体网络结构图 三、空间-通道分离下采样 3.1 SCDown介绍 3.2 C2fUIB介绍 3.3 PSA介绍 …

【QT八股文】系列之篇章3 | QT的多线程以及QThread与QObject

【QT八股文】系列之篇章3 | QT的多线程 前言4. 多线程为什么需要使用线程池线程池的基础知识python中创建线程池的方法使用threading库队列Queue来实现线程池使用threadpool模块&#xff0c;这是个python的第三方模块&#xff0c;支持python2和python3 QThread的定义QT多线程知…

【kubernetes】探索k8s集群中金丝雀发布后续 + 声明式资源管理yaml

目录 一、K8S常见的发布方式 1.1蓝绿发布 1.2灰度发布&#xff08;金丝雀发布&#xff09; 1.3滚动发布 二、金丝雀发布 三、声明式管理方法 3.1YAML 语法格式 3.1.1查看 api 资源版本标签 3.1.2查看资源简写 3.2YAML文件详解 3.2.1Deployment.yaml 3.2.2Pod.yaml …

数据结构(五)

数据结构&#xff08;五&#xff09; 常见的排序算法内部排序交换插入选择归并基数 外部排序基于归并的 常见的排序算法 内部排序 交换 冒泡&#xff1a;每一次运行总会将最小的或者最大的放到前面&#xff0c;如果需要交换&#xff0c;一直在交换 快速排序*&#xff1a;经过…

《C++ Primer Plus》第十一章复习题和编程练习

这里写目录标题 一、复习题二、编程练习 一、复习题 1. 使用成员函数为Stonewt类重载乘法运算符&#xff0c;该运算符将数据成员与double类型的值相乘。注意&#xff0c;当用英石和磅表示时&#xff0c;需要进位。也就是说&#xff0c;将10英石8磅乘以2等于21英石2磅。 答&am…

Python语言基础学习(下)

目录 一、顺序语句 二、条件语句 (1) if (2) if - else (3) if - elif - else 缩进和代码块 空语句 pass 三、循环语句 while 循环 for 循环 continue break 四、函数 创建函数 调用函数 函数返回 函数变量 函数递归 关键字参数 五、列表和元组 创建列表 …

创新实训2024.05.25日志:Web应用技术选型

我们的web应用使用python web的fastapi框架&#xff0c;通过uvicorn开启web服务。 1. refs 官网文档&#xff1a;FastAPI (tiangolo.com) github&#xff1a;https://github.com/tiangolo/fastapi 2. 环境配置 python:3.11 uvicorn:0.29.0 pip install "uvicorn[stan…

【启程Golang之旅】基本变量与类型讲解

欢迎来到Golang的世界&#xff01;在当今快节奏的软件开发领域&#xff0c;选择一种高效、简洁的编程语言至关重要。而在这方面&#xff0c;Golang&#xff08;又称Go&#xff09;无疑是一个备受瞩目的选择。在本文中&#xff0c;带领您探索Golang的世界&#xff0c;一步步地了…

嵌入式全栈开发学习笔记---C语言笔试复习大全25(实现学生管理系统)

目录 实现学生管理系统 第一步&#xff1a;结构体声明 第二步&#xff1a;重命名结构体 第三步&#xff1a;限定可以存储的最大学生数目 第四步&#xff1a;定义结构体指针数组和定义一个整型变量存放当前的人数 第五步&#xff1a;设计欢迎界面 第六步&#xff1a;设计…

【设计模式】JAVA Design Patterns——Command(事务模式)

&#x1f50d;目的 将请求封装为对象&#xff0c;从而使你可以将具有不同请求的客户端参数化&#xff0c;队列或记录请求&#xff0c;并且支持可撤销操作。 &#x1f50d;解释 真实世界例子 有一个巫师在地精上施放咒语。咒语在地精上一一执行。第一个咒语使地精缩小&#xff0…

从关键新闻和最新技术看AI行业发展(2024.5.6-5.19第二十三期) |【WeThinkIn老实人报】

写在前面 【WeThinkIn老实人报】旨在整理&挖掘AI行业的关键新闻和最新技术&#xff0c;同时Rocky会对这些关键信息进行解读&#xff0c;力求让读者们能从容跟随AI科技潮流。也欢迎大家提出宝贵的优化建议&#xff0c;一起交流学习&#x1f4aa; 欢迎大家关注Rocky的公众号&…

C++之std::is_trivially_copyable(平凡可复制类型检测)

目录 1.C基础回顾 1.1.平凡类型 1.2.平凡可复制类型 1.3.标准布局类型 2.std::is_trivially_copyable 2.1.定义 2.2.使用 2.3.总结 1.C基础回顾 在C11中&#xff0c;平凡类型&#xff08;Trivial Type&#xff09;、平凡可复制类型&#xff08;TrivialCopyable&#x…

深入理解与防御跨站脚本攻击(XSS):从搭建实验环境到实战演练的全面教程

跨站脚本攻击&#xff08;XSS&#xff09;是一种常见的网络攻击手段&#xff0c;它允许攻击者在受害者的浏览器中执行恶意脚本。以下是一个XSS攻击的实操教程&#xff0c;包括搭建实验环境、编写测试程序代码、挖掘和攻击XSS漏洞的步骤。 搭建实验环境 1. 安装DVWA&#xff…

8.什么是HOOK

程序编译的本质是&#xff0c;首先计算机它只能看得懂机器码也就是只能看得懂数字&#xff0c;机器码学起来很费劲然后就创造了编译器这个东西&#xff0c;编译器它懂机器语言所以它可以跟机器沟通&#xff0c;而我们人可以跟编译器沟通&#xff0c;人跟编译器的语言就是各种各…

GBDT、XGBoost、LightGBM算法详解

文章目录 一、GBDT (Gradient Boosting Decision Tree) 梯度提升决策树1.1 回归树1.2 梯度提升树1.3 Shrinkage1.4 调参1.5 GBDT的适用范围1.6 优缺点 二、XGBoost (eXtreme Gradient Boosting)2.1 损失函数2.2 正则项2.3 打分函数计算2.4 分裂节点2.5 算法过程2.6 参数详解2.7…

不拍视频,不直播怎么在视频号卖货赚钱?开一个它就好了!

大家好&#xff0c;我是电商糖果 视频号这两年看着抖音卖货的热度越来越高&#xff0c;也想挤进电商圈。 于是它模仿抖音推出了自己的电商平台——视频号小店。 只要商家入驻视频号小店&#xff0c;就可以在视频号售卖商品。 具体怎么操作呢&#xff0c;需要拍视频&#xf…

leedcode【142】. 环形链表 II——Java解法

Problem: 142. 环形链表 II 思路解题方法复杂度Code性能 思路 1.用快慢指针找到相遇的点&#xff08;快指针一次走一步&#xff0c;慢指针一次走两步&#xff09; 2.一个指针从head开始&#xff0c;一个指针从相遇点开始&#xff0c;一次一步&#xff0c;相遇处即为环入口 解题…

Vmware 17安装 CentOS9

前言 1、提前下载好需要的CentOS9镜像&#xff0c;下载地址&#xff0c;这里下载的是x86_64 2、提前安装好vmware 17&#xff0c;下载地址 &#xff0c;需要登录才能下载 安装 1、创建新的虚拟机 2、在弹出的界面中选择对应的类型&#xff0c;我这里选择自定义&#xff0c;点…