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

资讯详情

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

Python+Django智慧农业管理系统开发实战:从模型设计到部署上线

Python+Django智慧农业管理系统开发实战:从模型设计到部署上线 1. 这套基于PythonDjango的智慧农业管理系统是怎么来的这套基于PythonDjango的智慧农业管理系统从接到需求到部署上线我前后花了两个多月。客户是郊区的蔬菜种植基地几百亩大棚几十套温湿度、土壤墒情和光照传感器过去全靠在值班室盯仪表、做纸质台账数据散落得到处都是。他们要的东西听起来不复杂——一个网页能看每块地的实时环境参数能查历史曲线超阈值自动告警顺便把施肥、打药、采收这些农事记录也给管起来。但真正动手做的时候发现这里面的门道比预想多得多传感器数据怎么稳定接收、时间序列数据在MySQL里怎么存才不卡、告警规则怎么配置才灵活、还有最容易被忽略的——给非技术人员用的后台界面绝不能是Django默认那套又生硬又难用的样子。整个项目交付物也不只是代码源码、毕业论文文档、部署文档、讲解视频一个都不能少相当于一个完整的毕业设计工程包。这篇文章我就按实际开发的顺序把业务边界、数据建模、核心功能实现、Admin后台定制、服务器部署的完整过程包括踩过的坑、写过的代码、改过的配置全部摊开来讲。正在做智慧农业方向毕业设计或者准备用Django接类似管理系统的同学可以直接对照这套思路少走弯路。2. Django在这个项目里的选型理由以及我否掉其他方案的原因2.1 智慧农业系统的真实业务边界先别急着写代码得把智慧农业管理系统这七个字的边界搞清楚。这个系统不是做一个传感器数据大屏就完事它要管的东西至少分四块。第一块是基础档案。地块是系统的最小管理单元每个地块有面积、位置、土壤类型、当前种植作物地块上安装了传感器设备设备有编号、类型、安装时间、运行状态。第二块是环境数据。传感器定时上报温度、湿度、光照、土壤含水量、CO₂浓度等指标这些数据要能按时间和地块维度查询。第三块是告警。每类环境指标都有阈值范围比如西红柿花期夜间温度不能低于12℃超了就要触发告警。第四块是农事记录。施肥、打药、浇水、采收这些操作要有记录方便后面追溯产量和品质问题。如果还要再往前延伸那就是产量预测、病虫害识别这类偏算法的功能但那个属于另一个项目的范畴。我建议在需求阶段就把边界划清楚否则后面需求会无限膨胀。这套系统的核心价值就两句话把环境监测从人工看仪器变成系统自动看把农事台账从本子记变成系统记。2.2 为什么是Django不是Flask也不是Spring Boot技术选型的时候我其实对比过三个方向Python的Flask、Python的Django、Java的Spring Boot。Flask胜在轻量但一个管理系统的麻烦在于——认证、ORM、Admin后台、表单处理这些基础部件缺一不可用Flask就得自己拼。一套管理系统做下来Flask项目的代码量会膨胀到比Django还难维护因为车轱辘全得自己造。Spring Boot在企业级应用里确实强但考虑到这个项目体量不大、后续维护成本、以及Python生态里数据处理和图表类的库更丰富我最终选了Django。Django有一个杀手级优势是自带Admin后台和ORM。Admin后台意味着你不需要为最简单的增删改查页面写一行前端代码Django自动生成管理界面ORM让数据库操作全部走Python对象换数据库基本不用改业务代码。对于智慧农业这种大量围绕数据表的业务模型Django的Model定义方式几乎和业务概念一一对应——一个地块是一张表一个传感器是一张表农事记录是一张表开发效率非常高。还有一个客观条件是Python生态。农业系统后期想做数据分析、接到机器学习的预测模型Python在做特征工程和模型推理时会方便很多。与其后期跨语言对接不如一开始就在同一个生态里把路子铺好。2.3 项目目录结构的划分思路我用的是Django标准项目结构然后按功能模块拆了5个appfarm/ ├── manage.py ├── config/ # 项目配置settings、urls ├── accounts/ # 用户登录、权限管理 ├── plots/ # 地块与设备管理 ├── monitoring/ # 环境数据采集与展示 ├── alerts/ # 告警规则与告警记录 └── operations/ # 农事操作记录创建app就一条命令的事情python manage.py startapp plots把不同业务拆到独立app里最大的好处是后面加功能不用动老代码。比如后期想加一个投入品管理模块直接新建一个app注册到INSTALLED_APPS就能挂上去和现有模块互不干扰。项目名我用的是config而不是默认的farm同名目录纯属个人习惯减少目录嵌套的混淆感。3. 数据库模型设计从地块、传感器到告警规则的完整建表思路3.1 六个核心模型的前后端字段设计数据模型是整个系统的地基这一块我花费的时间是最多的。设计得好不好直接决定后面写业务逻辑是顺畅还是处处别扭。地块模型(Plot)class Plot(models.Model): name models.CharField(verbose_name地块名称, max_length50, uniqueTrue) area models.FloatField(verbose_name面积(亩), default0) soil_type models.CharField(verbose_name土壤类型, max_length20, blankTrue) location models.CharField(verbose_name位置描述, max_length100, blankTrue) status models.CharField( verbose_name地块状态, max_length10, choices[(planting, 种植中), (fallow, 休耕), (preparing, 备耕)], defaultplanting) created_at models.DateTimeField(auto_now_addTrue) class Meta: db_table plot verbose_name 地块 verbose_name_plural verbose_name这里的verbose_name一定不要省后面Admin界面和Django自动生成的表单都会读它写好了能省掉大量前端工作量。status字段用choices而不是单独建一张字典表因为状态集合几乎不会频繁变化硬拆表反而增加复杂度。传感器设备模型(Device)class Device(models.Model): device_no models.CharField(verbose_name设备编号, max_length30, uniqueTrue) device_type models.CharField( verbose_name设备类型, max_length20, choices[(temp_humi, 温湿度), (soil, 土壤墒情), (light, 光照), (co2, CO₂浓度)]) plot models.ForeignKey(Plot, verbose_name所属地块, on_deletemodels.CASCADE, related_namedevices) status models.BooleanField(verbose_name运行状态, defaultTrue) installed_date models.DateField(verbose_name安装日期, nullTrue, blankTrue)设备编号用uniqueTrue是为了让硬件上报数据时能准确匹配设备这个字段在正式项目里通常会直接用硬件的出厂编号或MAC地址避免自己编号和硬件对不上。on_deletemodels.CASCADE表示地块删除时设备一并删除在这个内部系统的场景里是合理的——地块都没了设备档案留着没有意义。环境监测数据模型(EnvironmentRecord)class EnvironmentRecord(models.Model): device models.ForeignKey(Device, verbose_name设备, on_deletemodels.CASCADE, related_namerecords) temperature models.FloatField(verbose_name温度(℃), nullTrue, blankTrue) humidity models.FloatField(verbose_name湿度(%), nullTrue, blankTrue) light_intensity models.FloatField(verbose_name光照(lux), nullTrue, blankTrue) soil_moisture models.FloatField(verbose_name土壤含水量(%), nullTrue, blankTrue) co2_concentration models.FloatField(verbose_nameCO₂浓度(ppm), nullTrue, blankTrue) recorded_at models.DateTimeField(verbose_name采集时间, db_indexTrue) class Meta: db_table environment_record ordering [-recorded_at]这个表是整个系统数据量增长最快的表。一台传感器如果5分钟上报一次一天就是288条数据按50台设备算一天接近1.5万条一年下来就是500多万条。所以recorded_at字段我加了db_indexTrue后面按时间范围查询会快很多。另一个细节是各环境指标允许null因为不同传感器采集的指标不一样比如土壤墒情传感器只上报土壤含水量其他字段就是空的硬给默认值反而会污染数据。告警规则模型(AlertRule)class AlertRule(models.Model): device_type models.CharField(verbose_name设备类型, max_length20) indicator models.CharField(verbose_name指标, max_length30) min_value models.FloatField(verbose_name下限值, nullTrue, blankTrue) max_value models.FloatField(verbose_name上限值, nullTrue, blankTrue) is_active models.BooleanField(verbose_name启用, defaultTrue) notify_sms models.BooleanField(verbose_name短信通知, defaultFalse) updated_at models.DateTimeField(auto_nowTrue)告警规则没有直接外键到某个地块而是关联设备类型和指标名称。原因是同一种作物在不同生长阶段阈值会变规则设计成可配置比写死在代码里灵活得多管理员在后台改一改就能适配新的种植计划。告警记录模型(AlertLog)class AlertLog(models.Model): device models.ForeignKey(Device, on_deletemodels.CASCADE) indicator models.CharField(max_length30) value models.FloatField() alert_type models.CharField(max_length10, choices[(low, 偏低), (high, 偏高)]) content models.TextField(verbose_name告警内容) is_handled models.BooleanField(verbose_name是否处理, defaultFalse) created_at models.DateTimeField(auto_now_addTrue)农事操作模型(FarmingOperation)class FarmingOperation(models.Model): plot models.ForeignKey(Plot, on_deletemodels.CASCADE) op_type models.CharField(max_length20, choices[ (fertilize, 施肥), (pesticide, 打药), (irrigate, 浇水), (harvest, 采收)]) op_date models.DateField(verbose_name操作日期) crop models.CharField(verbose_name作物, max_length30, blankTrue) detail models.TextField(verbose_name操作详情, blankTrue) operator models.CharField(verbose_name操作人, max_length20) created_at models.DateTimeField(auto_now_addTrue)这六个模型基本覆盖了系统沾边的所有业务。实际开发中我建议你先把模型定义完、makemigrations和migrate跑通再去做任何视图和页面。模型的调整成本在项目后期会越来越大前期多花时间把字段和关系想清楚后面写逻辑会顺畅非常多。3.2 外键关系、related_name和时间的几个设计细节谈几个具体的设计取舍。第一个是related_name。上面的代码里我写了related_namedevices和related_namerecords有了它正向和反向查询写起来都很优雅plot Plot.objects.get(id1) devices plot.devices.all() # 这个地块下的所有设备 device Device.objects.get(id1) records device.records.filter(recorded_at__date2024-05-01) # 设备某天的数据如果不写related_nameDjango默认用设备_set这种命名代码会显得啰嗦查起来也费劲。第二个是DateTimeField的auto_now和auto_now_add的区别。auto_now_add只在记录创建时写入一次用于创建时间auto_now每次保存都会更新用于更新时间。这两个参数不能同时加在一个字段上也不能手动赋值记住这个就行。我实际用下来像告警规则里的updated_at用auto_now很合适而告警记录的created_at用auto_now_add就好。第三个是索引。环境记录表数据量上来之后光给recorded_at加索引可能还不够更合理的做法是加一个(device, recorded_at)的联合索引。但我在项目初期先只给recorded_at加索引等数据量确实到瓶颈了再补。对于毕业设计或者中小企业内部系统来说这个量级的优化已经足够不建议一上来就堆索引反而拖慢写入速度。3.3 用Django自带的migration管理数据库变更模型改完之后直接跑两条命令python manage.py makemigrations python manage.py migratemakemigrations生成迁移文件migrate执行迁移。这里有个小建议迁移文件一定要纳入Git版本管理不要随手删。我有一次改模型改得急把之前生成的迁移文件删了重新生成了一版结果服务器上的数据库和本地的迁移历史对不上最后只能重建库白白折腾了半天。迁移文件是数据库结构的变更日志删掉的代价是丢失对表结构的完整追溯记录。4. 环境监测与告警模块从数据上报到阈值判断的实现细节4.1 传感器数据上报接口一个轻量但可靠的HTTP上报方案传感器数据上报是整个系统最先要实现的功能因为没数据后面的一切都白搭。考虑到项目里的传感器是用ESP32通过WiFi上报的硬件端代码要尽量简单我用了一个HTTP POST接口# monitoring/views.py from django.views.decorators.csrf import csrf_exempt from django.http import JsonResponse from django.utils import timezone import json from plots.models import Device from .models import EnvironmentRecord csrf_exempt def report_data(request): if request.method ! POST: return JsonResponse({code: 1, msg: 仅支持POST}) try: data json.loads(request.body) device_no data.get(device_no) device Device.objects.get(device_nodevice_no) record EnvironmentRecord.objects.create( devicedevice, temperaturedata.get(temperature), humiditydata.get(humidity), light_intensitydata.get(light_intensity), soil_moisturedata.get(soil_moisture), co2_concentrationdata.get(co2_concentration), recorded_attimezone.now() ) check_alerts(device, record) return JsonResponse({code: 0, msg: ok}) except Device.DoesNotExist: return JsonResponse({code: 2, msg: 设备不存在}) except Exception as e: return JsonResponse({code: 3, msg: str(e)})这个接口没有做身份认证因为传感器设备在局域网内运行安全风险可控。但如果你要把接口暴露到公网至少得加一个token校验——设备在请求头带上预分配的token服务端比对通过后才接受数据这个我写在部署文档里作为安全建议了。硬件端ESP32的上报逻辑很简单连上WiFi后组装一个JSON字典通过HTTP POST发送。上报频率我建议设成5分钟一次。太快会造成数据冗余、数据库膨胀太慢又无法及时发现环境异常。5分钟这个值在数据密度和实时性之间比较平衡。4.2 前端曲线图用ECharts展示环境趋势历史数据展示我用的是ECharts。Django后端只负责提供一个JSON接口把指定时间段的数据按时间排序返回# monitoring/views.py def device_chart_data(request, device_id): days int(request.GET.get(days, 7)) end_date timezone.now() start_date end_date - timezone.timedelta(daysdays) records EnvironmentRecord.objects.filter( device_iddevice_id, recorded_at__range[start_date, end_date] ).order_by(recorded_at) data { times: [r.recorded_at.strftime(%m-%d %H:%M) for r in records], temperature: [r.temperature for r in records], humidity: [r.humidity for r in records], soil_moisture: [r.soil_moisture for r in records], } return JsonResponse(data)前端模板里初始化一个ECharts折线图data从上面接口里取option里设置x轴是时间、y轴是温度/湿度用双y轴就能同时展示两条曲线。这个实现看着简单但有两个坑必须提醒。时间数据的时区问题。Django的settings里要设对TIME_ZONE和USE_TZ。我建议用USE_TZTrue配合TIME_ZONEAsia/Shanghai。这两个参数如果没配好存进数据库的时间会比北京时间差8个小时查出来的曲线全都对不上。这个坑我排了好几个小时才定位到后面在部署文档里专门标红了。大数据量时的渲染卡顿。如果查询一个月的温湿度数据返回几千条记录ECharts要逐个绘制点页面会明显卡。一个朴素有效的办法是前端做降采样——把5000条数据抽成500条比如每10条取1条趋势本身还是能清晰呈现的。真要做高精度分析那是后端大数据的事情不在这个系统的范围内。4.3 告警判断逻辑关键是规则可配置告警规则判断放在数据上报接口里同步做。每次新数据入库后取该设备类型对应的所有启用规则逐条比对# alerts/utils.py from .models import AlertRule, AlertLog def check_alerts(device, record): rules AlertRule.objects.filter(device_typedevice.device_type, is_activeTrue) for rule in rules: value getattr(record, rule.indicator, None) if value is None: continue if rule.min_value is not None and value rule.min_value: AlertLog.objects.create( devicedevice, indicatorrule.indicator, valuevalue, alert_typelow, contentf设备{device.device_no}的{rule.indicator}偏低: {value}, 低于阈值{rule.min_value}) elif rule.max_value is not None and value rule.max_value: AlertLog.objects.create( devicedevice, indicatorrule.indicator, valuevalue, alert_typehigh, contentf设备{device.device_no}的{rule.indicator}偏高: {value}, 高于阈值{rule.max_value})这个设计的核心是规则可配置。阈值不在代码里写死而是放到AlertRule表里管理员在后台就能改。比如5月份温度阈值定的是28℃6月改成30℃不用动一行代码。告警的提醒方式我分了两级。第一级是系统内告警记录登录后台就能看到自动标红未处理。第二级是短信通知AlertRule表里预留了notify_sms字段实际接入短信服务时就是在check_alerts里加一段调用短信API的逻辑。项目前期用系统内提醒加邮件通知就够了短信通道资费敏感建议后面有预算再上。5. Admin后台的定制让非技术人员在几十秒内上手5.1 注册模型与列表页优化Django自带Admin是这套系统最省心的部分但默认界面太朴素给客户用之前一定要定制。最基础的是在admin.py里注册模型并用list_display指定列表页显示的字段# plots/admin.py from django.contrib import admin from .models import Plot, Device admin.register(Device) class DeviceAdmin(admin.ModelAdmin): list_display (device_no, device_type, plot, status, installed_date) list_filter (device_type, status, plot) search_fields (device_no, plot__name) list_editable (status,) admin.register(Plot) class PlotAdmin(admin.ModelAdmin): list_display (name, area, soil_type, status, created_at) search_fields (name,)list_filter会在右侧生成筛选栏search_fields可以做关键字搜索list_editable可以让status字段直接在列表页通过下拉框切换状态不需要点进详情页。对于环境数据这种强时间属性的列表还有一个非常实用的配置是date_hierarchy# monitoring/admin.py from django.contrib import admin from .models import EnvironmentRecord admin.register(EnvironmentRecord) class EnvironmentRecordAdmin(admin.ModelAdmin): list_display (device, temperature, humidity, soil_moisture, recorded_at) list_filter (recorded_at, device__device_type) date_hierarchy recorded_atdate_hierarchy会在列表页顶部生成一个可以按年、月、日逐级下钻的时间导航条。这个功能在查看环境历史数据时特别好用点一下就筛出某一天的所有记录客户反馈学习成本几乎为零。5.2 几个值得收藏的Admin定制写法再往上一点可以做几个高价值的定制。只读字段。EnvironmentRecord的环境数据是系统自动写入的不应该允许人工编辑。用readonly_fields一指定就完事class EnvironmentRecordAdmin(admin.ModelAdmin): readonly_fields (device, temperature, humidity, soil_moisture, light_intensity, co2_concentration, recorded_at)列表页显示自定义内容。如果想把温度超过35℃的记录在列表里标红可以给模型加一个方法然后在list_display里引用from django.utils.html import format_html class EnvironmentRecord(models.Model): ... admin.display(description温度) def temperature_colored(self): if self.temperature and self.temperature 35: return format_html(span stylecolor:red{}/span, self.temperature) return self.temperature if self.temperature is not None else -自定义批量操作按钮。比如告警记录列表里加一个标记为已处理的操作admin.action(description标记为已处理) def mark_handled(modeladmin, request, queryset): queryset.update(is_handledTrue) class AlertLogAdmin(admin.ModelAdmin): list_display (device, indicator, value, alert_type, is_handled, created_at) list_filter (alert_type, is_handled) actions [mark_handled]这三组写法基本覆盖了Admin定制里90%的高频需求配上Django Admin自带的增删改查页面非技术人员用起来完全没压力。5.3 权限分组配置给不同角色开不同的门系统使用者大致分三种角色超级管理员、基地管理员、普通操作员。用Django自带的Group和Permission机制就能快速实现。在Admin后台创建三个组分别勾选不同app的增删改查权限即可。比如操作员组只勾选FarmingOperation和EnvironmentRecord的查看权限基地管理员加上增删改权限超级管理员保留全部权限。权限这里有一个要正视的问题Django默认权限是按模型的不能精确到只能编辑某个地块的数据。如果后期出现这个管理员只管A区地块这种对象级需求就得引入django-guardian这类第三方库。项目前期建议先别上等需求真来了再考虑过早引入复杂权限机制只会拖慢开发节奏。6. 部署实战宝塔面板、MySQL、Nginx配合跑通Django项目6.1 环境准备与Python版本选择部署环节我选择的是宝塔面板主要原因是项目交付阶段需要快速给出一个可访问的演示环境宝塔的可视化界面能直接看到Nginx、MySQL、PHP这些服务运行状态对非运维人员非常友好。装好宝塔后在软件商店里安装Nginx和MySQL另外再装一个Python项目管理器用它来创建Python版本。Python版本强烈建议选3.10或3.11不要选最新的3.12。原因是部分第三方库可能还没有针对新版本完成适配编译安装时会报错。Python装好之后把项目代码上传到服务器创建虚拟环境并安装依赖python3 -m venv venv source venv/bin/activate pip install -r requirements.txt pip install uwsgirequirements.txt文件在项目里一定要维护好这直接决定部署是否顺畅。我习惯用pipreqs按项目实际import生成精确的依赖列表而不是用pip freeze后者会把本地环境里一堆无关的包也带进来。6.2 settings.py上线前必须改的四个地方部署和本地的最大区别在settings.py。上线前必须检查这四个地方DEBUG False # 关闭调试模式 ALLOWED_HOSTS [你的域名或IP] # 允许访问的域名 TIME_ZONE Asia/Shanghai # 时区必须调整 USE_TZ True # 数据库连接改为MySQL DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: farm_db, USER: farm_user, PASSWORD: 替换成强密码, HOST: 127.0.0.1, PORT: 3306, } }DEBUG关闭后Django不再处理静态文件所以还需要配置STATIC_ROOT并执行collectstaticSTATIC_ROOT os.path.join(BASE_DIR, staticfiles) python manage.py collectstatic这一步很多人会漏掉结果前端页面样式全丢。DEBUGTrue时开发服务器会自动托管静态文件切换成False之后这个能力就没了必须靠collectstatic把散落在各app里的静态资源集中到一个目录再交给Nginx处理。6.3 uwsgi与Nginx的配置uwsgi是Django和Nginx之间的桥梁。创建一个uwsgi.ini[uwsgi] chdir /www/wwwroot/farm module config.wsgi:application master true processes 4 threads 2 socket 127.0.0.1:8001 vacuum true daemonize /www/wwwroot/farm/uwsgi.logNginx配置里的关键block如下server { listen 80; server_name your_domain.com; location /static/ { alias /www/wwwroot/farm/staticfiles/; } location / { include uwsgi_params; uwsgi_pass 127.0.0.1:8001; } }配置完成后执行uwsgi --ini uwsgi.ini启动服务访问域名就能看到系统页面。这里最常见的报错是502 Bad Gateway一般是uwsgi没启动或者端口写错。排查思路很固定先看uwsgi日志tail -f /www/wwwroot/farm/uwsgi.log再看Nginx的错误日志绝大多数问题都是这两个文件里的路径或IP没写对。6.4 上线后必查的三个细节系统跑起来之后有三件事必须马上做。第一数据库自动备份。宝塔面板有计划任务功能配置每天凌晨3点备份一次MySQL数据库保留最近7天的备份。农业环境数据是这套系统最值钱的部分传感器天天在攒数据服务器硬盘出一次故障没备份就是灾难。第二更换SECRET_KEY。本地开发用的那一串SECRET_KEY在线上一定要换掉用python -c import secrets; print(secrets.token_urlsafe(50))生成一个新的写进环境变量。SECRET_KEY泄露会有被构造恶意请求的风险生产环境这是大忌。第三检查媒体文件目录。系统里有上传图片的场景比如农事记录里拍照片MEDIA_ROOT和MEDIA_URL要配置好并且Nginx里同样要加一个location指向media目录否则上传成功但前端永远显示不出图片排查起来还很隐蔽。提示部署文档一定要按照从一台空服务器开始的标准来写。你以为的省去已知步骤往往就是读者卡住的地方。我用这个标准重写了一遍部署文档后接手的人基本没再问过环境问题。6.5 Django连接MySQL那个经典报错Django连接MySQL需要mysqlclient这个包在服务器上安装时经常编译报错提示缺libmysqlclient-dev。这个问题的解决办法是先用apt安装系统依赖再装pip包sudo apt update sudo apt install default-libmysqlclient-dev build-essential pip install mysqlclient还有一个替代方案是装PyMySQL然后在settings.py里加上import pymysql; pymysql.install_as_MySQLdb()。但如果能直接装mysqlclient就优先用mysqlclient性能和兼容性都更省心。7. 做完一个项目后的复盘文档、踩坑与扩展方向7.1 交付文档的价值不亚于代码项目交付时包含毕业论文文档、部署文档和讲解视频。很多开发者会轻视这部分但实际经验告诉我一套系统交付价值里文档至少占三成。客户或者毕业设计的导师拿到手第一件事不是跑代码而是看文档能不能让他快速理解系统、顺利跑起来系统。把环境搭建、数据库初始化、账号配置、功能说明都写清楚能省掉后面大量答疑时间。如果文档写得好客户对你的专业度评价会直接上一个台阶。7.2 开发过程中踩过最深的三个坑第一个坑是时区。开发到一半发现数据库里所有时间都比北京时间快了8小时排查了好几个小时最后发现是settings.py里USE_TZTrue但TIME_ZONEUTC改成Asia/Shanghai重启服务就好了。这个坑几乎每个Django新手都会踩一次越早遇到越好。第二个坑是MySQL版本兼容。服务器上安装mysqlclient时编译报错卡了很久才发现是缺libmysqlclient-dev这个系统依赖。后来在部署文档里直接写清楚先apt安装依赖再pip install后来的人没再卡过。第三个坑是静态文件。第一次部署完登录页面能打开但样式全乱。排查后发现是Nginx没有配置/static/的location指向staticfiles目录。本地开发时Django会自动处理静态文件所以这个问题特别隐蔽不部署到线上根本发现不了。这三个坑我都单独写到了部署文档的常见问题章节后续接手的人基本没再卡住。7.3 后续值得做的扩展方向系统做完之后如果还有时间和预算我建议优先考虑两个扩展。一是把环境数据和农作物生长阶段建模推出生长期指导建议比如根据积温判断作物发育进度二是引入简单的数据分析基于历史温湿度数据做病虫害概率预测。这两个方向都是在现有Django框架上新增app就能落地的不需要动底层架构Python生态里的pandas、sklearn也都现成可用。我个人在实际操作中的体会是像智慧农业这类管理系统技术本身并不复杂真正的难度在于把业务逻辑想清楚——地块、设备、数据、规则、告警、台账这几层关系怎么组织明白代码只是把想清楚的业务表达出来而已。Django恰好是这种业务驱动型项目的好搭档它把最常见的轮子都造好了让你把精力花在业务建模上而不是重复造基础设施。如果你现在正准备用PythonDjango做类似的系统建议从数据模型设计开始模型理顺了后面每一步都会顺很多。
返回列表