ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

告别写死数据:Django连接数据库从零到企业级落地

告别写死数据:Django连接数据库从零到企业级落地 上周帮一个刚转行的朋友调代码他的项目里二十几个 Python 列表把所谓“系统数据”写得明明白白用户名单、库存清单、订单状态、菜单权限……数据全写在代码文件里页面跑起来很顺演示效果也不错。可一说到“企业级项目”他就开始犯嘀咕数据要改怎么办别人能连吗领导让加个报表统计难道还得继续往代码里堆现在这个问题有了一个不算复杂的答案——把“写死数据”换成“连接真实数据库”。这件事在过去是后端工程师的必修课如今随着 Django 这类 Web 框架把 ORM、迁移、连接配置全都封装好文科生、产品经理转开发、非科班同事跨过这道坎的门槛确实又低了一点。这篇内容我会把整个过程拆成能直接上手照做的步骤顺便把那些不写在文档里的坑也讲清楚。1. 写死数据到底坑在哪三个必须换库的现实理由1.1 需求一变写死数据就要“人肉翻代码”写死数据本身并不可耻我特别理解新手为什么喜欢它。刚接触 Web 开发时核心目标是先把页面跑起来把一个列表塞进模板里稍微循环一下就能展示学习曲线极低。这里不涉及数据库安装、连接配置、SQL 报错所有代码看起来都“非常合理”。但问题出现在需求开始变化的那一刻。假设你写了一个商品列表products [ {name: 移动电源, price: 99, stock: 50}, {name: 蓝牙耳机, price: 199, stock: 30}, ]你只是在页面上展示它一次部署两三个月的内部工具完全没问题。可一旦领导说“把移动电源改叫快充移动电源顺便价格调成 89”你得搜代码如果这个列表被三四个文件引用你改完其一可能漏掉另一个模板里的过滤条件。更麻烦的是过阵子你可能在里面加上“上下架状态”字段于是每个写死数据的地方都要同步调整结构。“写死”最伤人的不是写而是改改的时候你不知道哪个角落还藏着一份旧副本。数据库解决的是“数据与代码分离”的问题。数据进入表里字段结构由模型统一管理页面展示通过查询读取改数据就是改一条记录的事不用再翻着代码找“还有哪里写死了”。这一步在工程上叫解耦听起来玄乎实际做一次就懂。1.2 多用户并发时写死数据活不过第一轮演示如果只有你一个人操作写死列表还算体面。但企业级项目往往意味着多人同时使用比如人事在录员工、财务在看报销、管理员在调权限。这个场景下写死数据的两个底层弱点立刻暴露。第一重启即失。你以为把数据放在内存列表或代码文件里就算“系统里有数据”可一旦服务重启原有的修改全部归零。第二个更隐蔽也更致命不同用户进程之间看到的不是同一份数据。Python 里的普通列表和字典只在当前运行进程的内存中生效A 同事新增的记录B 同事的页面刷新一百遍也看不到。有人会说“那我存在 JSON 文件里数据库不就是个能读写的文件吗”话是没错但 JSON 文件不具备并发控制。两个用户同时写同一个文件晚到的覆盖早到的甚至文件损坏这种问题在真实项目里碰上一次就够你折腾半天。数据库天生就是处理这种并发访问的。它内部有事务、锁机制、隔离级别你能同时读写同一张表系统会帮你排队、加锁、保证一致性。你不需要理解实现细节只需要知道“把数据交给数据库远比自己在代码里维护一个字典可靠”。1.3 “企业级”三个字其实就藏在数据库的底层能力里很多人一听到“企业级”头就大觉得是审计报表、分布式微服务、大数据平台那一套。实际上对绝大多数中小项目来说“企业级”的含义非常朴素数据不丢、权限可控、多人可用、出了事故能恢复。这四点恰恰是数据库的看家本领。数据持久化解决了“不丢”用户账号和权限系统解决“可控”事务和锁解决“多人可用”备份和恢复工具解决“事故恢复”。你只要把数据从代码里搬进数据库这些能力就自动到手不用自己重写一遍。换句话说写死数据让你从零造了一个轮子而数据库是成熟的现成轮子你选哪个2. 数据库连接没你想得那么难文科生版三步拆解2.1 选型SQLite 起步MySQL 和 PostgreSQL 按需切换很多新手一上来就纠结“我应该学 MySQL 还是 PostgreSQL”这个选择其实可以往后放。在 Django 这类框架里更换数据库多数时候只是改一行配置你的代码几乎不用变。数据库安装成本适合场景一句话评价SQLite零安装一个文件学习、单机、数据量小最适合迈出第一步MySQL需要安装服务常见企业项目、团队协作资料最多招聘需求大PostgreSQL需要安装服务复杂查询、地理数据、JSON开源界的新宠功能更全面我个人给新手的建议是先用 SQLite 把流程走通目录下会生成一个 db.sqlite3 文件你不需要启动任何服务就能完成“连接数据库—建表—读写”的整个闭环。等你有把握了再在服务器上装一个 MySQL把连接配置改过去顺便体会一下“换库”其实没那么大工程。有人会担心“我学了 SQLite 是不是浪费时间”不会。SQL、模型、迁移这些核心知识完全通用。数据库真正的区别集中在高并发优化、存储引擎、权限模型这些层面那是后话你现在用不上。2.2 连接核心只有三件事连接、读写、关闭不管用什么数据库不管用什么语言连接你做的永远是三件事先建立连接再执行读写最后关闭连接。这和去银行办事一模一样取号排队建立连接递上身份证和单据验证账号权限业务办完执行增删改查离柜关闭连接。你不需要懂银行内部的结算系统同样也不需要一开始就把数据库原理吃透先建立这个心智模型后面就顺了。一个连接通常需要四个参数地址HOST、端口PORT、账号USER、密码PASSWORD再加一个数据库名NAME。说白了就是你拿着“门牌号和钥匙”去开仓库门。装一个图形化管理工具比如 DBeaver或者处理 SQLite 文件时用 DB Browser for SQLite你会更直观地看到连上了什么库、有哪些表、哪些字段。有一点值得强调很多人卡在“我明明用管理工具能连上数据库怎么程序里连不上”。差距往往出在驱动Driver上。图形工具自带驱动你的代码缺一个驱动库。可以把驱动理解成“翻译官”负责把你的代码语句翻译成数据库能懂的协议。装好驱动再写连接问题就解决一大半。2.3 ORM 就是翻译官先不碰 SQL 也能操作数据库“连接数据库是不是必须精通 SQL”这是我经常被问到的问题。答案很明确不一定。Django 自带一套 ORM对象关系映射它做的事是把“表”变成“类”把“行记录”变成“对象”把增删改查变成普通方法调用。打个比方你不会直接对仓库管理员喊货架编号和物理学移货规则你只需要说“把库存大于 0 的商品都拿来”。ORM 就是那个仓库管理员你调用Product.objects.filter(stock__gt0)它内部帮你翻译成 SQL把结果返回成 Python 对象。这一层封装对文科生特别友好因为你不用一开始就背诵SELECT、WHERE、JOIN的语法细节。但我也要说一句实在话ORM 不是让你永远不学 SQL。至少你得看懂数据模型里“表、字段、主键、外键”这些概念最好能在调试时认出简单查询语句。大多数时候你不需要手写 SQL可一旦遇到性能问题或复杂统计SQL 知识就是你的救命稻草。3. 手把手实操给“写死数据”项目装上真实数据库3.1 环境准备虚拟环境、Django 和数据库驱动实操之前先建一个干净的环境。为什么要用虚拟环境因为不同项目依赖的第三方包版本可能冲突虚拟环境相当于为每个项目单独准备一个“包运行室”互不干扰。在终端依次执行python -m venv venv source venv/bin/activate # Windows 下使用 venv\Scripts\activate pip install django pip install pymysql # 如果计划连 MySQL先装驱动先说说pymysql。如果你之后要连 MySQLPython 默认没有 MySQL 驱动需要额外安装。Django 官方推荐的是mysqlclient但它在 Windows 上编译安装偶尔会折腾人pymysql安装更省心性能对小项目足够。如果你想连 PostgreSQL把驱动换成psycopg2-binary即可。这里有一个容易踩的坑安装完成后如果用的是 PyMySQL通常需要在项目的__init__.py文件里加一行pymysql.install_as_MySQLdb()让它冒充 MySQLdb 接口Django 才能正常工作。忘记这一行启动服务时大概率会报No module named MySQLdb。3.2 配置连接替换 settings.py 里的 DATABASESDjango 新建项目后默认用的是 SQLite配置长这样DATABASES { default: { ENGINE: django.db.backends.sqlite3, NAME: BASE_DIR / db.sqlite3, } }如果切换到 MySQL改成DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: erp_demo, # 数据库名要提前在 MySQL 里建好 USER: erp_user, # 业务账号不建议用 root PASSWORD: StrongPass123!, HOST: 127.0.0.1, PORT: 3306, } }参数里NAME是你要操作的数据库名注意这个库必须事先存在。如果用 SQLite框架会自动创建文件但用 MySQL、PostgreSQL 时框架不会替你创建数据库。你先用管理工具或命令建好空库再运行项目否则报错“Unknown database”。连接配置本质上就是一张“路线图”。ENGINE告诉 Django 用哪套数据库方言HOST和PORT告诉它去哪里找你安装的数据库服务USER和PASSWORD是通行证。现在你可能会想“我没装 MySQL 怎么办”那就先保留 SQLite 配置把后面的模型和迁移流程走完再回头换 MySQL 感受差异。3.3 建表不靠手写 SQL用模型和迁移生成数据表数据要从代码里搬进数据库第一步是在某个 app 的models.py里定义模型。比如建立一个商品表from django.db import models class Product(models.Model): name models.CharField(max_length100) price models.DecimalField(max_digits10, decimal_places2) stock models.IntegerField(default0) created_at models.DateTimeField(auto_now_addTrue) def __str__(self): return self.name这里的CharField、DecimalField分别对应数据库里的字符类型、小数类型auto_now_addTrue表示插入时自动记录创建时间。定义完模型接下来执行两条命令python manage.py makemigrations python manage.py migratemakemigrations负责对比模型和数据库的当前状态生成一份变更“草稿”migrate负责把草稿真正执行到数据库里。你不需要手动写CREATE TABLE语句框架会根据模型翻译成对应的建表语句这是 Django 对新手最友好的地方之一。执行完migrate后你可以打开管理工具看一眼会发现多出了一张名为myapp_product的表字段和你定义的模型一一对应。存储数据的“货架”就这样搭好了下面要做的就是往货架上摆货。3.4 视图数据源切换从写死列表到 ORM 查询先回忆一下最初的写死写法def product_list(request): products [ {name: 移动电源, price: 99, stock: 50}, {name: 蓝牙耳机, price: 199, stock: 30}, ] return render(request, shop/product_list.html, {products: products})换成数据库查询后from .models import Product def product_list(request): products Product.objects.all() return render(request, shop/product_list.html, {products: products})模板几乎不用大改原来你遍历列表里的字典现在遍历的是 Product 对象。在模板里访问product.name、product.price变量名换一下就行{% for product in products %} tr td{{ product.name }}/td td{{ product.price }}/td td{{ product.stock }}/td /tr {% empty %} trtd colspan3暂无商品/td/tr {% endfor %}为了让页面真的有数据你可以先往库里塞几条记录。不用刻意去写导入脚本Django 自带 Admin 后台只要创建一个管理员账号就能登录后台可视化地增删改数据python manage.py createsuperuser然后把Product模型注册到admin.pyfrom django.contrib import admin from .models import Product admin.site.register(Product)启动开发服务登录/admin添加几条商品记录再刷新商品页面发现数据已经实时展示出来了。这一步的意义值得停下来体会现在你修改一条数据页面立刻跟着变不再需要改代码重新部署。所谓“从写死数据到连接真实数据库”核心转折就发生在这一刻。4. 接入数据库之后的坑与“企业级”习惯4.1 新手上线最容易踩的六个错误我只写实践里真实遇到的报错每一条后面附解决思路方便你当成速查表。现象常见原因解决思路启动后提示 “no such table”忘记执行迁移先python manage.py makemigrations再migrate别跳过中文存储乱码数据库或表字符集不是 utf8mb4建库时指定CHARACTER SET utf8mb4连接参数也加上charsetutf8mb4报错No module named MySQLdb装了 PyMySQL 但没激活在__init__.py里执行pymysql.install_as_MySQLdb()报错Access denied for user账号权限或主机限制不对检查 MySQL 里userhost的授权范围单独建业务号授权页面很卡后台日志出现 “too many connections”连接没关闭或连接池太小确认每次连接用完关闭Django 里配置CONN_MAX_AGE做持连接复用密码里有或/等特殊字符导致连接串解析失败连接串没有编码用urllib.parse.quote_plus对密码编码后再拼连接参数这六条里“连接没关闭”是最隐蔽的。很多人以为代码里查完数据就自动释放了其实如果创建连接后没有显式关闭连接会一直占用数据库资源。小项目一天访问量低可能无所谓一旦用户量上来数据库连接数会迅速满掉表现为整个系统突然变卡或直接拒绝服务。Django 默认在请求结束时关闭连接但如果用了长连接配置需要认真理解CONN_MAX_AGE的作用别随便设一个很大的数就完事。4.2 事务让核心操作要么全成要么全不干连接上数据库以后你手里多了一把“原子操作”的武器叫事务。事务解决的是“半成功”问题举一个经典场景用户下单时既要生成订单又要扣减库存。如果订单插入成功扣库存时发现余额不足程序抛异常那这条订单就是一笔“脏数据”账对不上。用 Django 的transaction.atomic()可以把几个操作包成一个整体中间任何一步失败整体回滚数据库回到操作前的状态from django.db import transaction with transaction.atomic(): order Order.objects.create(useruser, productproduct, amount1) product.stock - 1 product.save()我建议所有“涉及两条以上写操作”的地方都考虑包上事务。对文科生来说你不用掌握数据库的各种隔离级别细节只要记住一条原则一组必须同时成功或同时失败的操作放进同一个事务块里。这能帮你避开大量上线后的数据对不上问题。4.3 最小权限和备份没人催你但你最好自己先做“企业级”项目还有一个容易被忽略的部分是权限和备份。很多同学本地开发图省事直接拿 root 账号连数据库这套搬到团队环境就是事故隐患。想象一下所有人共用高权限账号有人不小心执行了一条删除语句连备份都没有哭都来不及。我的建议是从第一天就养成两个习惯第一为每个应用单独创建数据库账号只授权它需要的权限。以 MySQL 为例业务账号只需要增删改查不需要 DROP 和 GRANT用最小权限跑业务。第二定期备份哪怕只是手动备份也比不备份强。Django 项目可以定期执行python manage.py dumpdata backup.jsonMySQL 也可以用官方的mysqldump导出全部数据。备份最好落到定时任务里每天一次别把“应该有备份”当成“真的有备份”。我见过太多朋友在系统跑了一个月后才发现从来没备份过那种心跳加速的感觉不好受。4.4 从“能连上”到“用得好”下一步补什么当你完成标题里说的这件事——从写死数据到连接真实数据库——你已经比很多只敢写演示项目的人前进了一大步。接下来的学习方向也清晰了一是补一点基础 SQL尤其学会WHERE聚合查询能和 ORM 对照着写二是补一点索引知识发现页面查询变慢时知道是缺索引而不是盲目加机器三是补一点设计规范比如什么时候拆表、什么时候用外键、什么时候只存 ID。这三条不需要你现在猛学遇到实际问题时回来查就够了。真正的进步往往是在“能跑”之后被业务逼着解决的难题堆出来的。你先把“连接真实数据库”这条路走通后面的事就自然有了抓手。最后分享一个我个人的实际感受每次帮人从写死数据切到数据库最难的从来不是技术而是下决心重新组织自己的代码。数据一旦从代码里搬出来你会发现自己对项目的理解会突然上一个大台阶——你会开始想字段类型、想表关系、想谁有权限改数据而不是只盯着页面长什么样。这种思维方式的变化比多会几个技术点更值得高兴。
返回列表