☰
Django+Python+微信小程序数据分析项目实战:从搭建到答辩全流程指南
2026/10/8 2:29:54 网站建设 项目流程

这篇项目总结是我基于自己带过的学生毕业设计、以及日常给咨询者做技术选型指导时积累的经验写出来的。如果标题里那种“Django + Python + 微信小程序 + 数据分析”的组合你也正在搞,或者正打算用这套技术栈完成类似的课设、毕设、竞赛作品,这篇文章能帮你把整个项目的骨架搭起来,把最容易踩坑的地方提前避掉。

先说这个项目到底是什么:一个学生端的移动数据分析小程序,后端用 Django 提供接口,前端用微信小程序做界面,数据分析用 Python 的 Pandas 或 Django ORM 聚合查询在服务端完成,最终以图表形式呈现在手机端。它解决的典型需求是:学生不方便打开电脑看报表,但手里有手机,想随时随地看自己的学业数据(成绩趋势、课程通过率、考勤统计、短板科目分析等)。这类项目的受众很明确:要做课设/毕设的学生、想练全栈的数据分析初学者、以及想快速拿出一个“能演示、能答辩、能扩展”项目的开发者。

我下面从技术选型、后端设计、小程序端实现、数据来源、联调排错一条线讲完。你可以把这套东西当成一个可直接落地的脚手架,哪怕是从零开始,也能按着步骤出来一个能跑的项目。

1. 项目想清楚再动手:整体设计与技术选型思路

1.1 为什么选 Django + 小程序,而不是其他组合

很多同学上来会问:数据分析不是用 Flask 更轻吗?前端为什么非要用小程序,做 Web 页面不香吗?这是两个最常见的质疑,也是项目设计阶段最需要想明白的问题。

先回答 Django。虽然 Flask 也够用,但 Django 对这类“教学型+演示型”项目有几个实打实的优势:自带 Admin 后台,可以直接手动维护学生、课程、成绩数据,演示时打开后台就能证明数据不是伪造的;ORM 强大,写成绩统计时基本不用拼 SQL;自带用户认证体系,虽然小程序登录不走 Session 流程,但做管理员端、做权限预留很方便。更关键的是,网上 Django 的中文资料和学生项目案例非常多,遇到问题搜索成本低,这对做课设的人来说是隐性价值。

再说小程序。同样一个分析报表,做成网页需要用户打开浏览器输入地址、还得做响应式布局适配手机;做成小程序则是扫码即开,数据请求走 wx.request,UI 用现成组件,展示图表时用 ec-canvas 之类的组件包,开发效率反而比网页高。而且“小程序”这个关键词天然符合当前移动端产品趋势,答辩时比“我写了一个管理系统网页”的完成度感知更强。

所以选型逻辑不是“哪个技术最厉害”,而是“哪个组合能在最短时间内完成一个完整闭环、且体现 Python 数据分析能力”。Django 负责数据存取与分析,小程序负责移动端交付,Python 脚本负责模拟数据,这三者刚好对应了后端、前端、数据三个岗位能力,是这类项目最稳的组合。

1.2 数据分析到底在哪个层面实现

这是新手最糊涂的地方。很多人以为“Python 数据分析”就一定要写一堆 Pandas 代码、画一堆 Matplotlib 图。学生端小程序没法直接展示 Matplotlib 静态图,所以你要先想清楚:分析逻辑放在服务端,展示交给小程序图表组件。

我的建议是分成两层:

  • 基础统计层:用 Django ORM 的 aggregate、annotate 完成均分、及格率、分数区间、男女/专业分布等统计。这类需求本质是分组聚合,ORM 完全能搞定,查询结果转成 JSON 返回即可。
  • 深度分析层:如果要做成绩预测、学科相关性分析、聚类分组,可以在 Django 的视图中使用 Pandas 读取 ORM 数据,用 DataFrame 做计算,再把结果序列化成 JSON。

我倾向于在小程序端不写任何分析逻辑。小程序端只负责渲染。为什么?因为小程序运行在手机浏览器内核里,处理大数据集计算会卡顿,而且把分析逻辑写在客户端会导致每次改动都要审核发布。服务端算好了再返回 JSON 列表,小程序端只管画图,这个职责边界必须一开始就划清楚。

2. 后端接口的搭建:Django 从零到能跑

2.1 环境准备与项目初始化

我建议先建虚拟环境再装依赖,这一步省不掉。

mkdir student-analysis cd student-analysis python -m venv venv source venv/bin/activate # Windows下执行 venv\Scripts\activate pip install django djangorestframework django-cors-headers pandas openpyxl

版本选择上不用刻意追新,Django 4.x 或 3.x 都行,但 Django 4.x 对 Python 版本有要求,Python 3.8+ 才能跑。装完以后创建项目和应用:

django-admin startproject config . python manage.py startapp student python manage.py startapp analysis

这里我把应用拆分成了两个:student 管数据模型,analysis 管统计接口。实际项目里也可以只建一个 app,但拆分的好处是逻辑清晰,答辩时讲“分层设计”有素材。配置里记得做三件事:

  1. 把两个 app 加进INSTALLED_APPS。
  2. 在settings.py里配好 CORS,小程序请求不带 Cookie 时跨域问题不突出,但开发调试时用到浏览器会很需要。
  3. 设置时区。
# settings.py LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' USE_TZ = True CORS_ORIGIN_ALLOW_ALL = True CORS_ALLOW_CREDENTIALS = False

CORS_ORIGIN_ALLOW_ALL = True只适合开发调试。如果小程序正式上线,域名白名单必须收敛,这个后面我再说。

2.2 学生数据模型的建模思路

这类项目的核心表一般是:学生表、课程表、成绩表。选课信息可以合到成绩表里,考勤记录单独一张表也可以。课程设计阶段不需要建太多表,但要保证“能回答问题”——比如要能统计“不同专业学生的平均成绩差异”,那学生表里必须有专业字段;要能分析“成绩随时间变化趋势”,成绩表里必须关联学期字段。

下面是一份我常用的模型设计,字段不复杂但覆盖了绝大多数统计维度:

# student/models.py from django.db import models class Student(models.Model): stu_no = models.CharField('学号', max_length=20, unique=True) name = models.CharField('姓名', max_length=50) gender = models.CharField('性别', max_length=10, choices=[('男', '男'), ('女', '女')]) major = models.CharField('专业', max_length=50, db_index=True) grade = models.CharField('年级', max_length=20) enroll_year = models.IntegerField('入学年份') class Course(models.Model): course_code = models.CharField('课程代码', max_length=20, unique=True) name = models.CharField('课程名称', max_length=100) credit = models.FloatField('学分') category = models.CharField('课程类别', max_length=20, blank=True) class Score(models.Model): student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='scores') course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='scores') score = models.FloatField('成绩', db_index=True) semester = models.CharField('学期', max_length=20) exam_date = models.DateField('考试日期', null=True, blank=True) class Meta: unique_together = ('student', 'course', 'semester')

有几个设计细节容易被忽略:

  • related_name必须设置,后面用student.scores.all()取成绩时少写很多代码。
  • score字段加db_index,数据量大时按分数筛选会快一些。
  • unique_together防止一条记录重复录入,模拟数据生成时也方便按这个去重。

2.3 Django 查询接口与数据分析核心逻辑

数据接口我用的是 Django REST Framework(DRF)。虽然用手写 JsonResponse 也能行,但 DRF 的序列化器、装饰器、分页组件都很成熟,学一遍也不亏。如果项目时间紧,可以直接在 Django 视图中用 JsonResponse 返回,但我个人建议用 DRF,原因只有一个:后面加接口、加权限、加分页的时候,DRF 省下的时间远远大于学习成本。

核心统计接口实现:

# analysis/views.py from django.db.models import Avg, Count, Q from django.db.models.functions import TruncMonth from rest_framework.decorators import api_view from rest_framework.response import Response from student.models import Student, Score, Course @api_view(['GET']) def overview_stats(request): """总览统计:学生数、平均分、及格率、优秀率""" student_count = Student.objects.count() avg_score = Score.objects.aggregate(avg=Avg('score'))['avg'] total = Score.objects.count() passed = Score.objects.filter(score__gte=60).count() excellent = Score.objects.filter(score__gte=90).count() return Response({ 'student_count': student_count, 'avg_score': round(avg_score, 2) if avg_score else 0, 'pass_rate': round(passed / total * 100, 2) if total else 0, 'excellent_rate': round(excellent / total * 100, 2) if total else 0, })

这里是数据分析能力最直接的体现:不是写多复杂的算法,而是用聚合查询把原始数据加工成有意义的信息。及格率、优秀率这类指标,聚合查询一次搞定。

需要按专业统计成绩分布时,会用到Case条件聚合:

from django.db.models import Case, When, IntegerField score_distribution = Score.objects.annotate( bucket=Case( When(score__lt=60, then=Value('不及格')), When(score__lt=70, then=Value('及格')), When(score__lt=80, then=Value('中等')), When(score__lt=90, then=Value('良好')), default=Value('优秀'), output_field=CharField(), ) ).values('bucket').annotate(count=Count('id'))

Case和When是分组统计的利器,效果等同于 SQL 里的CASE WHEN ... THEN ... GROUP BY。做数据分析报表时,这种条件分组几乎绕不开。

如果要做更复杂的相关性分析,比如“平时成绩是否影响期末成绩”,我会把查询结果转成 Pandas DataFrame,再用df.corr()计算相关系数:

import pandas as pd from student.models import Score def course_correlation(request, course_id): scores = list(Score.objects.filter(course_id=course_id) .values('score', 'student_id')) df = pd.DataFrame(scores) # 模拟出一个平时成绩字段后再算相关性 df['daily_score'] = df['score'] * 0.85 + 10 corr = df['score'].corr(df['daily_score']) return Response({'correlation': round(corr, 4)})

这个例子只是为了展示 Pandas 能无缝接入 Django 视图,真实的“平时成绩差异分析”需要模型里多存两个字段。但你只要理解了这套模式——ORM 取数、Pandas 计算、Response 返回 JSON——后续接任何分析库都不怵。

3. 小程序端的设计与实现

3.1 页面结构与数据请求封装

小程序端我一般分成四个页面:首页(总览)、成绩趋势、学科分析、我的。底部 TabBar 四个入口,和绝大多数管理类小程序一致。页面结构如下:

pages/ index/ # 总览看板 trend/ # 趋势分析 analysis/ # 学科分析 profile/ # 个人中心与登录

小程序第一步是封装一个统一的请求方法。很多人喜欢在每个页面直接写wx.request,前端几百行下来到处都是重复代码,改一次 baseURL 想哭。我会在utils/request.js里封装:

// utils/request.js const BASE_URL = 'http://127.0.0.1:8000/api' function request(url, method = 'GET', data = {}) { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + url, method, data, header: { 'Content-Type': 'application/json' }, success: (res) => { if (res.statusCode === 200) { resolve(res.data) } else { wx.showToast({ title: '请求失败', icon: 'none' }) reject(res) } }, fail: reject }) }) } module.exports = { request }

封装完以后,页面里调用就变成:

const { request } = require('../../utils/request') Page({ async onLoad() { const stats = await request('/overview/') this.setData({ stats }) } })

注意:微信开发者工具里默认勾选了“不校验合法域名”,本地调试时可以请求http://127.0.0.1:8000。真机预览时一定要勾掉,并配置request合法域名,否则请求会直接失败。

3.2 登录与手机号获取的坑

这个部分是很多人的梦魇。标题里明晃晃写着“学生移动端数据分析小程序”,用户身份识别就绕不开微信登录。

微信登录的标准流程是:小程序端wx.login()拿到临时 code,发送到后端,后端用 code + appid + secret 去微信接口换 openid。openid 是用户在微信体系里的唯一标识,拿它当身份主键。

但我遇到过很多同学直接卡在“获取手机号”这一步。这里有一个从2023年前后就开始生效的变动:<button open-type="getPhoneNumber">拿到的e.detail.code是动态令牌,不能再像以前那样靠这个 code 直接换手机号,而是要走“手机号快速验证组件”,并且小程序主体必须是认证过的企业或个体户。个人主体小程序直接用这个组件会报错或者拿不到手机号。

所以我的建议是:课设项目不要强依赖手机号。用wx.login()拿 openid 创建或匹配用户即可,手机号可以作为扩展字段后续绑定,不参与核心流程。如果你一定想演示手机号能力,那就在profile页面放一个“绑定手机号”的按钮,用open-type="getPhoneNumber"触发,后端拿到 code 后再调用接口。但申报前先确认账号主体属性,很多同学做到最后发现个人小程序调不通,心态直接崩了。

登录接口的核心代码:

# student/views.py import requests from django.conf import settings from rest_framework.decorators import api_view from rest_framework.response import Response @api_view(['POST']) def login(request): code = request.data.get('code') url = 'https://api.weixin.qq.com/sns/jscode2session' params = { 'appid': settings.WECHAT_APPID, 'secret': settings.WECHAT_SECRET, 'js_code': code, 'grant_type': 'authorization_code' } resp = requests.get(url, params=params).json() openid = resp.get('openid') if not openid: return Response({'error': '登录失败'}, status=400) user, created = User.objects.get_or_create( username=openid, defaults={'nickname': '小程序用户'} ) token = generate_token(user) # 用 django-rest-framework 的 TokenAuthentication return Response({'token': token.key, 'user_id': user.id})

3.3 用图表组件把分析结果可视化

数据分析项目如果没有图表,演示效果会大打折扣。小程序里画图最常见的方案是echarts-for-weixin,也就是把 ECharts 的ec-canvas组件放进小程序里用。另一个轻量选择是lime-chart,封装更简单,适合新手。

我比较推荐 ec-canvas,因为 ECharts 的配置项海洋是通用的,网上现成的柱状图、折线图、饼图配置一抓一大把。在页面里使用:

<ec-canvas id="chart" canvas-id="chart" ec="{{ ec }}"></ec-canvas>
import * as echarts from '../../components/ec-canvas/echarts' function initChart(canvas, width, height, dpr) { const chart = echarts.init(canvas, null, { width, height, devicePixelRatio: dpr }) canvas.setChart(chart) chart.setOption({ tooltip: {}, xAxis: { type: 'category', data: ['第1学期', '第2学期', '第3学期'] }, yAxis: { type: 'value' }, series: [{ type: 'line', data: [72, 78, 85] }] }) return chart } Page({ data: { ec: { onInit: initChart } } })

图表组件初始化时,ec对象里的onInit方法会被自动调用。拿到后端返回的 JSON 数组后,把数据填进setOption的series.data里即可。需要留意的是:小程序 canvas 的层级问题偶尔会出现,一般用canvas-id区分多个图表即可。

4. 数据从哪来:模拟数据生成与真实数据采集

4.1 用 Python 脚本生成模拟学生数据

做数据分析项目,最尴尬的是跑起来没数据。与其手工录入几十条,不如写一个独立的 Python 脚本,批量生成几百个学生、几十门课程、几千条成绩记录。这样演示时“查询性能”和“图表效果”都有说服力。

我会用 Faker 库和随机数来生成:

pip install faker
# scripts/generate_data.py import os import django import random from faker import Faker os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') django.setup() from student.models import Student, Course, Score fake = Faker('zh_CN') majors = ['计算机科学', '软件工程', '数据科学', '人工智能'] courses = [ {'code': 'CS101', 'name': 'Python程序设计', 'credit': 3.5}, {'code': 'CS102', 'name': '数据结构', 'credit': 4.0}, {'code': 'MA101', 'name': '高等数学', 'credit': 5.0}, {'code': 'DS201', 'name': '数据分析基础', 'credit': 3.0}, ] def create_students(n=200): for _ in range(n): Student.objects.create( stu_no=fake.unique.ssn(), name=fake.name(), gender=random.choice(['男', '女']), major=random.choice(majors), grade=random.choice(['2022级', '2023级']), enroll_year=random.randint(2020, 2023) ) def create_scores(): students = list(Student.objects.all()) course_objs = [] for c in courses: obj, _ = Course.objects.get_or_create(code=c['code'], defaults=c) course_objs.append(obj) for student in students: for course in course_objs: for semester in ['2023-2024-1', '2023-2024-2']: # 用随机成绩模拟真实分布,60分以下的占比约15% score = random.randint(45, 100) if random.random() < 0.15 else random.randint(60, 100) Score.objects.create( student=student, course=course, score=score, semester=semester ) if __name__ == '__main__': create_students(200) create_scores() print('数据生成完成')

执行:

python manage.py migrate python scripts/generate_data.py

这里有个细节值得说:模拟数据的分布倾向不能太均匀。如果所有成绩都集中在80-100分,画出来的图表非常假。我在生成逻辑里特意让15%的学生成绩落在45-60,就是为了让“及格率”“不及格分布”这类指标有分析价值。数据分析项目最怕干净得毫无规律的数据。

4.2 数据库初始化与接口联调

数据库我默认用 SQLite,零配置、免安装,是课设最稳的选择。如果你担心“人均SQLite不够高级”,可以在部署到服务器时换成 MySQL,Django 的 ORM 在这种切换上基本无感。但本地开发阶段,SQLite 的读写性能足够支撑几千条记录。

数据生成完以后,先手动测接口。在浏览器访问http://127.0.0.1:8000/api/overview/,能返回 JSON 数据,再打开微信开发者工具联调。这个先后顺序不能反,否则你根本分不清是后端接口问题还是小程序解析问题。

联调时我最常用的工具是开发者工具的 Network 面板,而不是控制台。Network 能看到请求的 URL、状态码、返回体,接口报错了直接点开就看。很多人一上来在 Console 里打印 res,其实是绕了远路。

5. 实操过程:从 0 到 1 跑通一个分析场景

5.1 完整流程梳理:登录 → 请求 → 分析 → 展示

现在把整个链路串起来,我以一个“学生查看自己各学期平均分趋势”的场景做演示。这个场景非常典型,既涉及登录,又涉及分析,还涉及图表展示,一个流程下来整个项目就活了。

流程如下:

  1. 小程序启动,app.js的onLaunch里调用wx.login()获取 code,发送到后端/api/login/,拿到 token 后存储在wx.setStorageSync('token', token)。
  2. 用户进入“趋势”页面,onLoad时从缓存取 token,调用后端/api/my/trend/。
  3. 后端根据 token 识别用户(这里简化成按用户关联学生表),查询该学生所有成绩,按学期分组,计算平均分、最高分、最低分,返回 JSON。
  4. 小程序收到数据后,把学期列表和平均分列表分别塞进折线图的 x 轴和 series.data。
  5. 页面渲染出折线图,用户可以看到成绩浮动趋势。

5.2 关键代码实现

后端趋势接口:

# analysis/views.py @api_view(['GET']) def my_trend(request): student = request.user.student_profile scores = (Score.objects.filter(student=student) .values('semester') .annotate(avg=Avg('score'), max=Max('score'), min=Min('score')) .order_by('semester')) return Response(list(scores))

这里用到了values('semester').annotate(...),它的含义是“按学期分组,再对每一组求平均值、最大值、最小值”。Django 的values加annotate就是教科书级的“分组聚合”写法,所有做数据分析的后端都会用到。

小程序的请求部分:

// pages/trend/trend.js const { request } = require('../../utils/request') Page({ data: { xData: [], yData: [] }, async onLoad() { const token = wx.getStorageSync('token') if (!token) { wx.showToast({ title: '请先登录', icon: 'none' }) return } const data = await request('/my/trend/', 'GET', {}, token) this.setData({ xData: data.map(item => item.semester), yData: data.map(item => item.avg) }) } })

request封装里需要加上 token 到 header:

function request(url, method = 'GET', data = {}, token = '') { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + url, method, data, header: { 'Content-Type': 'application/json', 'Authorization': token ? `Token ${token}` : '' }, ... }) }) }

后端用 DRF 的 TokenAuthentication 认证时,请求头写Authorization: Token xxxx,这是固定格式。

6. 常见问题与排查技巧实录

6.1 查询结果为空、图表空白

这个大概是遇到最多的问题。你接口明明返回了数据,但小程序图表就是空白。排查顺序我建议是:

  • 先看 Network 里接口返回的 JSON 结构,确认字段名是否和前端解析一致。后端用semester,前端写成semester,有一字之差,图表就是空的。
  • 再看setOption的xAxis.data和series.data是否都是数组,this.setData里数据层级对不对。
  • 最后看 canvas 是否初始化成功。ec-canvas 的onInit只会在第一次渲染时触发,如果你的数据是异步加载,需要在拿到数据后手动chart.setOption,而不是在onInit里用空数据初始化后指望它自动更新。

实践中我最常踩的是最后一条。ECharts 图表初始化时机很早,异步数据回来之后一定要拿 chart 实例重新 setOption,否则永远画不出数据。

6.2 Django 跨域与 CSRF 问题

小程序请求后端时,如果提示跨域,或者 POST 接口 403,一般都是这两件事:

  • 跨域:安装django-cors-headers后,确保MIDDLEWARE里的CorsMiddleware放在CommonMiddleware前面,顺序错了会失效。
  • CSRF:小程序请求 POST 接口时没有带 CSRF Token,DRF 默认对 SessionAuthentication 启用 CSRF 校验。如果用了 TokenAuthentication,可以不做 Session 登录;如果是自定义 JSON 登录接口,记得用@csrf_exempt或在 DRF 中设置为permission_classes+authentication_classes。

最常见的做法是全接口走 DRF Token 认证,而不是 Session,这样 CSRF 基本不会卡你。

6.3 删除对象时的 QuerySet 陷阱

标题热词里提到的“django 执行查询-删除对象”,是一个典型坑。Django 提供了两种删除方式:

# 方式一:单个实例删除 score = Score.objects.get(id=1) score.delete() # 方式二:QuerySet 批量删除 Score.objects.filter(semester='2023-2024-1').delete()

两者看似都是删除,但对ForeignKey关联数据的影响完全不同。上面模型里Score外键指向Student和Course,我用的是on_delete=models.CASCADE,意味着删除学生时会级联删除成绩。批量删除 QuerySet 时,Django 会把所有相关对象先收集起来再执行删除,性能更好;但如果你在模型里重写了delete()方法做额外清理,批量删除不会调用单实例的delete()方法。

这是个冷门陷阱:很多人在模型里写了自定义删除逻辑,然后用QuerySet.delete()批量删除,发现自定义逻辑没执行。如果你需要删除时做额外操作,要么循环单删,要么重写QuerySet.delete()。

6.4 小程序真机预览连不上本地后端

本地用http://127.0.0.1:8000联调没问题,但手机预览时一定要把地址换成电脑的局域网 IP,比如http://192.168.1.5:8000。并且确保:

  1. Django 启动时不只在本地监听:python manage.py runserver 0.0.0.0:8000
  2. 电脑防火墙放行 8000 端口。
  3. 手机和电脑连同一个 Wi-Fi。
  4. 微信开发者工具里关闭“校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”。

第 4 条是真机预览阶段才能体验到的隐藏开关,很多人本地跑得好好的,一真机预览就懵。即便这样,真机预览也只能在开发模式下用,正式发布必须用 HTTPS 线上域名。

6.5 数据分析结果如何导成 Excel

学生项目经常需要“成果物”,图表之外再导出一份 Excel 统计表是加分项。用 Django 生成 Excel 最方便的是openpyxl:

from openpyxl import Workbook from django.http import HttpResponse def export_excel(request): wb = Workbook() ws = wb.active ws.append(['姓名', '专业', '平均分', '及格率']) students = Student.objects.annotate(avg=Avg('scores__score')) for s in students: ws.append([s.name, s.major, round(s.avg, 2), 1 if s.avg >= 60 else 0]) response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') response['Content-Disposition'] = 'attachment; filename="report.xlsx"' wb.save(response) return response

注意Student.objects.annotate(avg=Avg('scores__score'))里使用了RelatedManager的反向关系名scores,这也是前面强调related_name必须设置的原因。

写在后面:几个我反复踩过坑后的体会

这套“Django + Python + 小程序 + 数据分析”的组合,我前前后后带过好几个项目,自己也动手改过细节,最深的感觉是:项目的核心难度不在技术,而在把“数据分析”四个字落地成用户能感知的功能。如果你只是把后端查询结果列成表格,那叫“列表页”,不叫“数据分析”。真正的加分项是你能说出每个统计指标的意义——及格率是为了定位教学薄弱环节,成绩趋势是为了识别学生状态波动,专业对比是为了辅助选课决策。

最后再分享一个小技巧:数据分析接口返回的 JSON 结构,一定不要返回那种嵌套很深的对象,尽量平铺成简单的key-value或数组。小程序端处理扁平 JSON 最简单,画图的时候直接map出来就塞给图表组件。为了“标准化”而搞复杂的关联序列化,只会给自己添堵。

这个项目后续扩展的方向也很多:接入定时任务让模拟数据自动更新、加爬虫采集真实公开学业数据、用 Pandas 做成绩预测模型、生成 PDF 报告……基础框架搭好以后,每加一个分析功能其实就是往analysis/views.py里加一个接口,再往小程序里加一个页面而已。希望这篇文章能让你少走一段弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询