尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Django构建可复现民宿数据分析系统

Django构建可复现民宿数据分析系统 简介本资源是一套基于Django框架开发的民宿房源数据分析与可视化系统完整源码专为Python初学者及本科毕业设计、课程设计、期末大作业提供高分实践范例。系统聚焦真实业务场景实现房源数据采集、清洗、统计分析与多维度可视化展示涵盖用户管理、数据看板、图表交互等核心功能界面采用MDUIBootstrap构建兼顾美观性与易用性。压缩包共807个文件含40个Python后端逻辑文件、80个HTML前端页面、321个JS交互脚本、147个CSS样式文件及配套字体、图标与配置文件整体体积11.88MB结构清晰、模块解耦便于理解MVC架构与前后端协同逻辑。目前已有773人学习下载资源经严格调试可直接运行附带完整项目目录说明与基础部署文档适合快速上手、二次开发或作为数据分析类毕设的技术参考模板。1. 用 Django 搭建民宿房源数据分析可视化系统不是做展示页而是让数据自己说话你手上有几百条 Airbnb 或小红书风格的民宿房源数据——带价格、位置、评分、评论数、房型、设施标签、入住时间等字段。但 Excel 筛不出“周末溢价超 40% 且评分 ≥4.8 的江景两居”SQL 查询写三遍还漏掉空值处理临时改个图表要重跑整个 notebook。这个毕业设计标题里的「Python Django 数据分析 可视化」本质是构建一个可交互、可回溯、可扩展的数据决策入口用户点选城市→自动聚合区域热力、价格分位、设施关联图管理员上传新 CSV → 后台触发清洗特征工程缓存更新开发者加个新指标如“高性价比指数评分×100/每平米单价”只需改一个 Python 函数不碰前端模板。它不是把 Matplotlib 图片贴进网页而是用 Pandas 做计算引擎、Django 做调度中枢、Plotly/D3 做渲染层三者在 request-response 生命周期里完成一次完整数据闭环。适合正在写毕设、需要体现工程能力与分析思维平衡的本科生也适合想快速验证本地生活类数据产品逻辑的初级数据工程师。2. 为什么选 Django 而非 Flask 或 Streamlit从数据流视角看框架选型逻辑2.1 数据分析场景对 Web 框架的隐性要求远超“能显示图表”很多同学看到“可视化”第一反应是 Streamlit——写几行st.bar_chart(df)确实快。但毕业设计验收时老师会问“数据从哪来怎么更新用户筛选条件如何影响后端计算缓存失效策略是什么” 这些问题直指数据生命周期管理能力。Streamlit 默认无状态、无数据库集成、无权限控制所有计算在 session 内存中完成一旦刷新页面所有中间结果丢失。而 Django 天然具备结构化数据管道models.py定义房源实体House,Facility,ReviewORM 自动处理 MySQL/PostgreSQL 的外键约束、索引优化、批量插入异步任务调度用 Celery Redis 实现“上传 CSV 后后台自动清洗”避免用户等待 2 分钟白屏细粒度权限控制区分admin可删数据、analyst只读导出、guest仅看聚合图表权限直接绑定到 Django Admin 或自定义 ViewURL 驱动的数据上下文/analysis/shanghai/?price_range300-800has_pooltrue这样的 URLDjango 的request.GET可直接映射为 Pandas 的df.query()条件字符串无需手动解析 JSON。提示不要被“Django 重”吓退。本项目实际只用到django.db,django.views.generic,django.contrib.auth三个模块其余如django.contrib.sessions可关闭。启动一个最小 Django 服务仅需 3 个文件settings.py精简版、urls.py、views.py总代码量 200 行。2.2 对比 FlaskDjango 的 ORM 和 Admin 是数据分析系统的“加速器”假设你要支持“按地铁站 500 米内房源统计”Flask 需手动写 SQLSELECT station_name, COUNT(*) as cnt FROM houses h JOIN stations s ON ST_DWithin(h.geom, s.geom, 500) GROUP BY station_name;而 Django 只需在models.py中定义地理字段需安装django.contrib.gis# models.py from django.contrib.gis.db import models class House(models.Model): name models.CharField(max_length100) location models.PointField() # 经纬度点 price models.DecimalField(max_digits8, decimal_places2) class Station(models.Model): name models.CharField(max_length50) location models.PointField()然后在 View 中# views.py from django.contrib.gis.db.models.functions import Distance from django.contrib.gis.geos import Point def station_analysis(request): center Point(121.47, 31.23) # 上海中心点 houses_nearby House.objects.filter( location__distance_lte(center, D(m500)) ).annotate( distanceDistance(location, center) ).order_by(distance)[:10] return render(request, analysis.html, {houses: houses_nearby})Django ORM 自动生成带空间索引的 SQL且Distance函数在 PostgreSQLPostGIS 下直接调用 C 扩展性能比纯 Python 计算经纬度距离快 12 倍以上。2.3 选型结论当数据源 1000 行、需支持多用户协作、要求结果可复现时Django 是更稳的基座维度StreamlitFlaskDjango数据持久化依赖外部 DB需手动连接同左无内置模型内置 ORM迁移脚本自动生成多用户隔离无原生支持需 hack session需集成 Flask-LoginUser模型开箱即用login_required装饰器一行启用分析逻辑复用函数级复用但无法跨 session 共享计算结果同左需 Redis 手动缓存cache_page装饰器直接缓存整个 View支持cache.set(key, df.to_dict())存 Pandas 结果部署复杂度streamlit run app.py即可但生产环境需反向代理需 WSGI 服务器Gunicorn Nginx同左但manage.py collectstatic自动处理前端资源本项目最终采用 Django 5.12024 年最新稳定版因其对asyncView 的原生支持已成熟可将耗时的数据清洗步骤如pandas.read_csvdf.dropna()放入async def避免阻塞主线程。而 Flask 5.x 的 async 支持仍处于实验阶段。3. 从原始 CSV 到可交互图表Django 后端数据流的四层处理链3.1 第一层数据接入 —— 用 Django Model 定义结构化 Schema拒绝“字典式开发”很多毕设代码把 CSV 当作纯文本处理pd.read_csv(data.csv)后直接.groupby()导致后续无法做字段校验、缺失值标记、类型转换。正确做法是先用 Django Model 描述业务语义# models.py from django.db import models from django.core.validators import MinValueValidator, MaxValueValidator class House(models.Model): CITY_CHOICES [ (sh, 上海), (bj, 北京), (gz, 广州), (sz, 深圳), ] city models.CharField(max_length2, choicesCITY_CHOICES) district models.CharField(max_length20) # 行政区如“徐汇区” name models.CharField(max_length100) price models.DecimalField( max_digits7, decimal_places2, validators[MinValueValidator(0)] ) rating models.FloatField( validators[MinValueValidator(0), MaxValueValidator(5)] ) review_count models.PositiveIntegerField(default0) has_pool models.BooleanField(defaultFalse) has_wifi models.BooleanField(defaultFalse) created_at models.DateTimeField(auto_now_addTrue) class Meta: ordering [-created_at] indexes [ models.Index(fields[city, district]), models.Index(fields[price]), ]执行python manage.py makemigrations python manage.py migrate后Django 自动生成带索引的数据库表。关键优势在于数据质量前置validators在保存前拦截非法值如价格为负、评分超 5查询加速indexes让filter(citysh, price__lt500)查询毫秒级返回语义清晰House.objects.filter(has_poolTrue).count()比df[df[has_pool]True].shape[0]更易理解业务意图。3.2 第二层数据清洗 —— 在 Django Command 中封装 Pandas 流程避免污染 View不要在 View 里写pd.read_csv()CSV 解析、空值填充、异常值过滤应封装为独立命令通过python manage.py import_houses --file data.csv触发# management/commands/import_houses.py import pandas as pd from django.core.management.base import BaseCommand from myapp.models import House class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument(--file, typestr, requiredTrue) def handle(self, *args, **options): df pd.read_csv(options[file]) # 清洗逻辑统一字段名、处理空值、价格单位转换万→元 df.columns df.columns.str.lower().str.replace( , _) df[price] df[price].fillna(0) * 10000 # 万转元 df[rating] df[rating].clip(0, 5) # 评分截断 # 批量创建1000 条数据 0.8 秒完成 houses [] for _, row in df.iterrows(): houses.append(House( cityrow[city], districtrow[district], namerow[name], pricerow[price], ratingrow[rating], review_countint(row.get(review_count, 0)), has_poolbool(row.get(has_pool, False)), has_wifibool(row.get(has_wifi, False)), )) House.objects.bulk_create(houses, batch_size500) self.stdout.write(f成功导入 {len(houses)} 条房源)注意bulk_create比循环save()快 20 倍以上且不触发pre_save信号避免意外副作用。若需信号如清洗后发通知改用transaction.atomic()create()。3.3 第三层分析计算 —— 用 Pandas 在 View 中实时聚合结果存入 Django Cache用户点击“查看上海价格分布”不应每次查库再算直方图。正确路径是View 接收参数 → 2. 从 DB 读取原始数据 → 3. Pandas 计算 → 4. 结果存入 Redis 缓存Key 为analysis:shanghai:price_hist→ 5. 返回 JSON 给前端绘图。# views.py from django.core.cache import cache from django.http import JsonResponse import pandas as pd import json def price_distribution(request): city request.GET.get(city, sh) cache_key fanalysis:{city}:price_hist # 先查缓存 cached cache.get(cache_key) if cached: return JsonResponse(cached) # 缓存未命中查库计算 houses House.objects.filter(citycity).values(price, rating, review_count) df pd.DataFrame(list(houses)) if df.empty: return JsonResponse({error: 无数据}, status404) # 核心分析价格分段统计 关联评分 df[price_bin] pd.cut(df[price], bins10, labelsFalse) result df.groupby(price_bin).agg({ price: [min, max], rating: mean, review_count: sum }).round(2).to_dict() # 缓存 1 小时 cache.set(cache_key, result, 3600) return JsonResponse(result)3.4 第四层可视化输出 —— 用 Plotly JSON 替代图片实现前端动态交互不要用plt.savefig()生成 PNG那样图表无法响应点击、缩放、悬停。正确做法是 View 返回 Plotly 的 JSON 结构前端用plotly.js渲染# views.py import plotly.express as px import plotly.utils def price_map_view(request): city request.GET.get(city, sh) houses House.objects.filter(citycity).values(district, price, rating) df pd.DataFrame(list(houses)) # 生成 Plotly Figure fig px.box(df, xdistrict, yprice, colorrating, titlef{city} 各区房价分布按评分分色, labels{price: 价格元/晚, district: 行政区}) # 转为 JSON前端可直接用 Plotly.react() graph_json json.dumps(fig, clsplotly.utils.PlotlyJSONEncoder) return render(request, map.html, {graph_json: graph_json})前端模板map.htmldiv idprice-box/div script srchttps://cdn.plot.ly/plotly-2.24.1.min.js/script script const graphData {{ graph_json|safe }}; Plotly.newPlot(price-box, graphData.data, graphData.layout); /script这样用户点击某个行政区的箱线图前端可捕获事件并触发下钻查询如“点击徐汇区 → 显示徐汇区 TOP10 房源列表”真正实现数据探索闭环。4. 本地快速启动与核心配置5 分钟跑通完整流程4.1 环境准备Python 3.9 Django 5.1 Pandas 2.0确保系统已安装 Python 3.9 或更高版本python --version然后执行# 创建虚拟环境推荐避免包冲突 python -m venv venv source venv/bin/activate # Linux/macOS # venv\Scripts\activate # Windows # 安装核心依赖注意Django 5.1 要求 Python ≥3.8 pip install django5.1.2 pandas2.0.3 plotly5.18.0 gunicorn21.2.0 # 初始化 Django 项目假设项目名为 house_analytics django-admin startproject house_analytics . python manage.py startapp core此时目录结构为house_analytics/ ├── manage.py ├── house_analytics/ │ ├── __init__.py │ ├── settings.py # 关键配置在此 │ ├── urls.py │ └── asgi.py └── core/ ├── __init__.py ├── admin.py ├── apps.py ├── models.py # 房源模型定义 ├── views.py # 分析逻辑 └── management/ # 自定义命令 └── commands/ └── import_houses.py4.2 数据库配置SQLite 足够毕业设计但需开启事务安全Django 默认使用 SQLite对千行级数据完全够用。修改settings.py中的DATABASES# settings.py import os from pathlib import Path BASE_DIR Path(__file__).resolve().parent.parent DATABASES { default: { ENGINE: django.db.backends.sqlite3, NAME: BASE_DIR / db.sqlite3, # 关键启用 WAL 模式提升并发读写性能 OPTIONS: { timeout: 20, # 防止锁表超时 init_command: PRAGMA journal_modeWAL;, } } }提示SQLite 在高并发写入时可能报database is locked。若测试中遇到将timeout提高到 30并确保import_houses命令执行时无其他进程访问 DB。4.3 URL 路由与核心 View 注册让/analysis/成为数据入口在core/urls.py中定义分析路由# core/urls.py from django.urls import path from . import views urlpatterns [ path(analysis/, views.analysis_home, nameanalysis_home), path(analysis/price-distribution/, views.price_distribution, nameprice_distribution), path(analysis/price-map/, views.price_map_view, nameprice_map_view), ]在主urls.py中包含# house_analytics/urls.py from django.contrib import admin from django.urls import path, include urlpatterns [ path(admin/, admin.site.urls), path(, include(core.urls)), # 所有分析接口挂载到根路径 ]4.4 启动服务并验证用 curl 测试第一个 API运行开发服务器python manage.py runserver此时访问http://127.0.0.1:8000/analysis/应看到首页。用 curl 测试数据接口# 获取上海价格分布返回 JSON curl http://127.0.0.1:8000/analysis/price-distribution/?citysh # 上传 CSV 数据需先准备 test_data.csv python manage.py import_houses --file test_data.csv若返回类似成功导入 1247 条房源说明数据链路已通。此时db.sqlite3文件大小应增长可用ls -lh db.sqlite3查看。5. 毕业设计答辩必答的三个技术细节与应对话术5.1 “为什么不用 ECharts 而用 Plotly”—— 抓住“可编程性”这个核心差异面试官常问可视化库选型理由。别只说“Plotly 更好看”。要指向工程本质ECharts 配置靠 JSON 字符串拼接option {xAxis: {type: category}, ...}修改一个坐标轴类型需手动改 keyIDE 无提示易拼错Plotly 靠 Python 对象链式调用fig.update_xaxes(typecategory).update_layout(title房价)PyCharm 能自动补全update_*方法参数类型有提示重构安全。更重要的是Plotly 的Figure对象可直接序列化为 JSON前端用Plotly.react()更新时只 diff 变化的属性如只重绘data数组不重建整个 DOM而 ECharts 每次setOption()都全量重绘大数据量下卡顿明显。答辩时可现场演示在views.py中加一行fig.update_traces(marker_colorred)刷新页面图表立刻变红——证明分析逻辑与渲染逻辑解耦符合软件工程的单一职责原则。5.2 “数据量大了怎么办”—— 展示你已考虑扩展边界而非仅满足当前需求当数据从 1000 行涨到 10 万行必须回答性能预案。不能只说“换 MySQL”。要分层说明查询层Django ORM 的only()和defer()只取必要字段如House.objects.filter(citysh).only(price,rating)减少网络传输量计算层用dask替代pandasimport dask.dataframe as dd; df dd.read_csv(big.csv)自动并行化groupby存储层Django 5.1 支持DatabaseConfig动态切换settings.py中可配置if DEBUG: DATABASES[default][ENGINE] django.db.backends.sqlite3 else: DATABASES[default][ENGINE] django.db.backends.postgresql答辩时强调“我预留了 PostgreSQL 连接参数只要修改DATABASE_URL环境变量无需改一行业务代码即可上线。”5.3 “如何保证分析结果可复现”—— 用 Django Migration 和 Requirements 锁定环境学术严谨性体现在可复现。你的requirements.txt必须精确到小版本# requirements.txt Django5.1.2 pandas2.0.3 plotly5.18.0 gunicorn21.2.0执行pip freeze requirements.txt生成。同时所有数据结构变更如新增has_aircon字段必须走makemigrations生成的0002_add_aircon_field.py文件要提交 Git。答辩时可展示git log -p migrations/查看字段演进历史git checkout old_commit python manage.py migrate回滚到旧版本数据自动降级。这比“我把 CSV 发给你”或“我截图了结果”更有说服力——你交付的是可审计、可回滚、可协作的数据系统而非一次性分析报告。提示在settings.py中加入DEBUG os.getenv(DEBUG, False).lower() true用环境变量控制调试模式避免误提交DEBUGTrue到生产环境。本文还有配套的精品资源点击获取
返回列表