view.py
一、注册 register 和 登录 login
python
def login(request):
# 定义登录视图函数,接收request对象作为参数
# request包含了用户请求的所有信息(如请求方法、POST数据、session等)
if request.method == 'GET':
# 判断请求方法是否为GET
# GET请求通常用于获取页面,POST用于提交数据
return render(request, 'login.html')
# 如果是GET请求,直接渲染并返回登录页面
# render函数会将login.html模板与数据结合,生成完整的HTML页面返回给用户
else:
# 如果不是GET请求,那就是POST请求(用户提交了登录表单)
username = request.POST.get('username')
# 从POST请求数据中获取username字段的值
# request.POST类似一个字典,get方法获取键对应的值
password = request.POST.get('password')
# 从POST请求数据中获取password字段的值
try:
# 尝试执行以下代码,如果出错就跳转到except
User.objects.get(username=username, password=password)
# 使用Django ORM在User表中查询
# 查找同时满足username和password条件的记录
# 如果找到一条记录,说明用户名和密码正确
# 如果找不到,会抛出异常
request.session['username'] = username
# 将用户名存入session(会话)中
# session是服务器端记录用户状态的机制
# 设置session后,后续请求可以通过request.session获取用户信息
# 这实现了"登录状态"的维持
return redirect('/app/home')
# 重定向到首页
# redirect函数会返回一个302状态码,让浏览器跳转到指定URL
# 用户登录成功后,直接进入系统首页
except:
# 如果上面的try块中代码执行出错(比如用户不存在或密码错误)
return errorResponse.errorResponse(request, '账号或密码错误')
# 调用errorResponse模块的errorResponse方法
# 返回一个错误页面,提示"账号或密码错误"
# 这是一个自定义的错误处理函数
# ============================================================================
def register(request):
# 定义注册视图函数
# 函数名后的注释'#注册'说明了这个函数的功能
if request.method == 'GET':
# 如果是GET请求(用户点击注册链接)
return render(request, 'register.html')
# 渲染并返回注册页面
else:
# 如果是POST请求(用户提交了注册表单)
username = request.POST.get('username')
# 获取表单中的用户名
password = request.POST.get('password')
# 获取表单中的密码
confirmPassword = request.POST.get('confirmPassword')
# 获取表单中的确认密码字段
try:
# 尝试执行以下代码
User.objects.get(username=username)
# 在User表中查询是否已存在相同的用户名
# 如果找到了,说明用户名已被注册,会抛出异常吗?不,找到了不会抛异常
# 这里逻辑需要仔细分析
except:
# 如果上面的get操作抛出异常(即没有找到该用户名)
# 说明用户名可用,进入注册流程
# 检查输入是否为空
if not username or not password or not confirmPassword:
# 如果用户名、密码或确认密码任一为空
# not username 当username为空字符串时返回True
return errorResponse.errorResponse(request, '不允许为空值')
# 返回错误提示:不允许为空值
# 检查两次密码是否一致
if password != confirmPassword:
# 比较密码和确认密码是否相等
return errorResponse.errorResponse(request, '两次密码不一致')
# 返回错误提示:两次密码不一致
# 所有验证通过,创建新用户
User.objects.create(username=username, password=password)
# 使用Django ORM的create方法在User表中创建一条新记录
# 插入用户名和密码
return redirect('/app/login')
# 注册成功后重定向到登录页面
# 让用户用刚注册的账号登录
# 如果try块中没有抛出异常(即找到了同名用户)
return errorResponse.errorResponse(request, '该账号已存在')
# 返回错误提示:该账号已存在
GET请求通常用于获取页面,POST用于提交数据
登录成功后,当用户提交登录表单,服务器验证用户名密码正确后,会执行:
python
return redirect('/app/home')
这行代码的作用是:
-
告诉浏览器:"你刚才的请求处理完了,现在请你重新发送一个请求,去访问
/app/home这个地址" -
浏览器收到这个指令后,会自动向
/app/home发送新的请求 -
最终用户看到的是首页内容
通俗理解:就像你去服务台办事,工作人员处理完后告诉你:"请去3号窗口领结果",然后你就走向3号窗口。
二、首页home
python
def home(request):
# 定义首页视图函数,处理用户访问系统首页的请求
# request参数包含了当前HTTP请求的所有信息(用户session、请求方法、POST数据等)
username = request.session.get('username')
# 从session会话中获取当前登录用户的用户名
# request.session是Django提供的会话对象,用于在多次请求间保持用户状态
# get('username')方法尝试获取键为'username'的值
# 如果用户已登录,这里会拿到用户名;如果未登录,会返回None
# 这是在登录成功时通过request.session['username'] = username存储的
userInfo = User.objects.get(username=username)
# 根据获取到的用户名,从数据库User表中查询该用户的完整信息
# User是之前在models.py中定义的用户模型类
# objects.get()方法会返回满足条件的唯一一条记录
# 这里拿到了当前登录用户的所有信息(如用户名、性别、地址、头像等)
a5Len, commentsLenTitle, provienceDicSort = getHomeData.getHomeTagData()
# 调用getHomeData模块中的getHomeTagData函数,获取首页标签数据
# 返回三个值:
# - a5Len:5A级景点的数量
# - commentsLenTitle:评论数量最多的景点标题(热门景点)
# - provienceDicSort:按省份统计的景点数量分布(字典类型,已排序)
scoreTop10Data, saleCountTop10 = getHomeData.getAnthorData()
# 调用getHomeData模块中的getAnthorData函数,获取其他统计数据
# 返回两个值:
# - scoreTop10Data:评分最高的前10个景点数据
# - saleCountTop10:销量最高的前10个景点数据
year, mon, day = getHomeData.getNowTime()
# 调用getHomeData模块中的getNowTime函数,获取当前日期
# 返回年、月、日三个整数值
# 用于在首页显示当前时间
geoData = getHomeData.getGeoData()
# 调用getHomeData模块中的getGeoData函数,获取地理分布数据
# 返回各地理位置的统计数据,用于在地图上展示景点分布
userBarCharData = getHomeData.getUserCreateTimeData()
# 调用getHomeData模块中的getUserCreateTimeData函数,获取用户注册时间分布数据
# 返回用户按注册时间统计的数据,用于柱状图展示用户增长情况
return render(request, 'home.html', {
# 调用render函数渲染首页模板
# 第一个参数request:当前请求对象
# 第二个参数'home.html':要渲染的模板文件
# 第三个参数是字典类型,向模板传递数据
'userInfo': userInfo,
# 将当前登录用户的完整信息传递给模板
# 模板中可以通过{{ userInfo.username }}等显示用户信息
'a5Len': a5Len,
# 将5A级景点数量传递给模板
# 用于在首页展示统计数据
'commentsLenTitle': commentsLenTitle,
# 将评论最多的景点标题传递给模板
# 用于展示热门景点
'provienceDicSort': provienceDicSort,
# 将省份景点分布数据传递给模板
# 用于生成按省份统计的图表
'scoreTop10Data': scoreTop10Data,
# 将评分前10的景点数据传递给模板
# 用于展示高分景点榜单
'nowTime': {
# 将当前日期信息封装成字典传递给模板
'year': year, # 当前年份
'mon': getPublicData.monthList[mon - 1], # 当前月份
# getPublicData.monthList是一个预定义的月份名称列表
# mon是1-12的整数,减1后作为索引获取中文月份名
'day': day # 当前日期
},
'geoData': geoData,
# 将地理分布数据传递给模板
# 用于在地图组件中展示景点分布
'userBarCharData': userBarCharData,
# 将用户注册时间分布数据传递给模板
# 用于生成用户增长趋势的柱状图
'saleCountTop10': saleCountTop10
# 将销量前10的景点数据传递给模板
# 用于展示热门销量榜单
})
session的技术定义
Session(会话) 是服务器端用来临时保存用户状态的一种机制。当用户访问网站时,服务器会为每个用户创建一个唯一的会话,并在会话中存储该用户的相关信息。
session解决了什么问题?
HTTP协议是无状态的,意思是:每次请求都是独立的,服务器不认识你是谁。
举个例子:
-
你登录了网站(第一次请求)
-
你点击"个人中心"(第二次请求)
-
服务器不知道第二次请求和第一次是同一个用户
session的出现就是为了解决这个问题------让服务器能记住你是谁。
session在您的项目中的具体应用
登录时存储session
在login函数中:
python
request.session['username'] = username
这行代码做了什么?
-
用户登录成功后,服务器在session中记录下他的用户名
-
相当于服务器在自己的内存里记了一笔:"用户张三已经登录了"
首页读取session
在home函数中:
python
username = request.session.get('username')
这行代码做了什么?
-
用户访问首页时,服务器从session中取出之前存储的用户名
-
就知道"哦,是张三在访问"
退出时清除session
在logOut函数中:
python
del request.session['username'] # 或者 request.session.flush()
这行代码做了什么?
-
用户退出时,服务器删除session中的记录
-
相当于收回房卡,用户变成未登录状态
为什么需要session?
| 场景 | 没有session会怎样 | 有session如何解决 |
|---|---|---|
| 登录状态 | 每次请求都要重新输入密码 | 登录一次,后续请求自动识别 |
| 购物车 | 刷新页面购物车就空了 | 购物车数据保存在session中 |
| 权限控制 | 无法区分普通用户和管理员 | session中记录用户角色 |
| 个性化推荐 | 不知道用户喜好 | session关联用户历史行为 |
景点信息表(TravelInfo)详细设计
表结构
| 字段名 | 数据类型 | 约束 | 说明 |
|---|---|---|---|
| id | INT | 主键,自增 | 景点唯一标识 |
| title | VARCHAR(255) | 默认空字符串 | 景区名称 |
| level | VARCHAR(255) | 默认空字符串 | 景区等级(如5A、4A) |
| discount | VARCHAR(255) | 默认空字符串 | 折扣信息 |
| saleCount | VARCHAR(255) | 默认空字符串 | 销量 |
| province | VARCHAR(255) | 默认空字符串 | 所在省份 |
| star | VARCHAR(255) | 默认空字符串 | 热度评分 |
| detailAddress | VARCHAR(255) | 默认空字符串 | 详细地址 |
| shortIntro | VARCHAR(255) | 默认空字符串 | 简短介绍 |
| detailUrl | VARCHAR(255) | 默认空字符串 | 详情页URL |
| score | VARCHAR(255) | 默认空字符串 | 用户评分 |
| price | VARCHAR(255) | 默认空字符串 | 价格 |
| commentsLen | VARCHAR(255) | 默认空字符串 | 评论数量 |
| detailIntro | VARCHAR(2555) | 默认空字符串 | 详细介绍 |
| img_list | VARCHAR(2550) | 默认空字符串 | 图片列表(JSON格式) |
| comments | TEXT | 默认空字符串 | 用户评论(JSON格式) |
| cover | VARCHAR(2555) | 默认空字符串 | 封面图片URL |
| createTime | DATE | 自动添加 | 数据爬取时间 |
字段设计说明
为什么很多字段用VARCHAR而不是数字类型?
-
爬取的数据可能包含非数字字符,如"¥120起"、"5折"
-
用VARCHAR可以原样存储,避免类型转换错误
-
后续使用时再根据需要进行转换
为什么用JSON格式存储复杂数据?
-
img_list和comments是列表结构,不适合用关系表存储 -
JSON格式可以完整保留数据结构,方便解析
-
避免了创建额外的关联表,简化设计
用户信息表(User)详细设计
表结构
| 字段名 | 数据类型 | 约束 | 说明 |
|---|---|---|---|
| id | INT | 主键,自增 | 用户唯一标识 |
| username | VARCHAR(255) | 默认空字符串 | 用户名 |
| password | VARCHAR(255) | 默认空字符串 | 密码(明文存储,有待改进) |
| sex | VARCHAR(255) | 默认空字符串 | 性别 |
| address | VARCHAR(255) | 默认空字符串 | 地址 |
| avatar | FileField | 默认头像路径 | 头像图片 |
| textarea | VARCHAR(255) | 默认空字符串 | 个人简介 |
| createTime | DATE | 自动添加 | 账号创建时间 |
字段设计说明
密码为什么存明文?
-
当前项目为毕业设计,简化了密码处理
-
实际生产环境应该使用哈希加密存储
头像字段为什么用FileField?
-
Django的FileField自动处理文件上传
-
数据库中存储的是文件路径,实际文件保存在
media/avatar/目录下
三、个人信息页面changeSelfInfo
python
def changeSelfInfo(request):
# 定义修改个人信息的视图函数
# 这个函数处理用户修改个人资料(如性别、地址、头像、个人简介等)的请求
# 参数 request 包含了当前HTTP请求的所有信息(请求方法、POST数据、session等)
username = request.session.get('username')
# 从 session 会话中获取当前登录用户的用户名
# request.session.get('username') 尝试获取键为 'username' 的值
# 这个值是在用户登录时通过 request.session['username'] = username 存储的
# 如果用户未登录,这里会返回 None(但中间件已经拦截了未登录用户)
userInfo = User.objects.get(username=username)
# 根据获取到的用户名,从数据库 User 表中查询该用户的完整信息
# User 是 models.py 中定义的用户模型类
# objects.get() 方法返回满足条件的唯一一条记录
# 如果用户名不存在,会抛出异常(但这里用户名一定存在,因为是从 session 来的)
# userInfo 包含了该用户的所有字段:用户名、密码、性别、地址、头像、个人简介等
year, mon, day = getHomeData.getNowTime()
# 调用 getHomeData 模块中的 getNowTime 函数,获取当前日期
# 返回年、月、日三个整数值
# 用于在页面顶部显示当前时间
if request.method == 'POST':
# 判断请求方法是否为 POST
# GET 请求:用户访问修改信息页面,显示表单
# POST 请求:用户提交修改后的信息,需要处理更新
getChangeSelfInfoData.changeSelfInfo(username, request.POST, request.FILES)
# 调用 getChangeSelfInfoData 模块中的 changeSelfInfo 函数
# 这个函数负责实际的更新操作
# 传入三个参数:
# username: 当前用户的用户名,用于定位要更新的用户记录
# request.POST: 包含表单中所有文本字段的数据(性别、地址、个人简介等)
# request.FILES: 包含上传的文件数据(如头像图片)
# 函数内部会更新数据库中该用户的对应字段
userInfo = User.objects.get(username=username)
# 重新从数据库查询该用户的更新后信息
# 因为上面的 changeSelfInfo 函数已经更新了数据库
# 所以需要重新获取最新的用户数据,用于渲染页面时显示更新后的内容
return render(request, 'changeSelfInfo.html', {
# 调用 render 函数渲染模板并返回 HTTP 响应
# 第一个参数 request:当前请求对象
# 第二个参数 'changeSelfInfo.html':要渲染的模板文件
# 第三个参数是一个字典,向模板传递数据
'userInfo': userInfo,
# 将当前用户的完整信息传递给模板
# 模板中可以通过 {{ userInfo.username }} 显示用户名
# 通过 {{ userInfo.sex }} 显示性别(如果是 GET 请求,显示原有值)
# 通过 {{ userInfo.address }} 显示地址
# 通过 {{ userInfo.avatar }} 显示头像
# 通过 {{ userInfo.textarea }} 显示个人简介
'nowTime': {
# 将当前日期信息封装成字典传递给模板
'year': year,
# 当前年份,如 2024
'mon': getPublicData.monthList[mon - 1],
# 当前月份的中文名称
# mon 是 1-12 的整数
# getPublicData.monthList 是一个预定义的月份名称列表
# 列表索引从 0 开始,所以需要 mon - 1
# 例如 mon=5 时,monthList[4] 得到 'May'
'day': day
# 当前日期(一个月中的第几天)
},
})
四、数据表格tableData 和 添加评论addComments
python
def tableData(request):
# 定义景点数据表格视图函数
# 这个函数用于展示所有景点的数据表格页面
# 参数 request 包含当前HTTP请求的所有信息
username = request.session.get('username')
# 从 session 会话中获取当前登录用户的用户名
# 这是在用户登录时存储在 session 中的,用于识别当前用户
userInfo = User.objects.get(username=username)
# 根据用户名从数据库 User 表中查询该用户的完整信息
# 包含用户名、性别、地址、头像等,用于页面顶部显示用户信息
year, mon, day = getHomeData.getNowTime()
# 调用 getHomeData 模块中的 getNowTime 函数获取当前日期
# 返回年、月、日三个整数值,用于页面显示当前时间
talbeData = getPublicData.getAllTravelInfoMapData()
# 调用 getPublicData 模块中的 getAllTravelInfoMapData 函数
# 获取所有景点的完整数据,返回经过处理的景点对象列表
# 注意变量名拼写:talbeData 应该是 tableData
return render(request, 'tableData.html', {
# 调用 render 函数渲染模板并返回 HTTP 响应
# 第一个参数 request:当前请求对象
# 第二个参数 'tableData.html':要渲染的模板文件
# 第三个参数是字典,向模板传递数据
'userInfo': userInfo,
# 将当前用户信息传递给模板,用于显示用户名和头像
'nowTime': {
# 将当前日期信息封装成字典传递给模板
'year': year,
'mon': getPublicData.monthList[mon - 1],
# 将数字月份转换为英文月份名称
'day': day
},
'talbeData': talbeData
# 将所有景点数据传递给模板
# 模板中通过循环遍历这个列表,生成表格的每一行
# 显示每个景点的标题、等级、省份、评分、价格等信息
})
# ============================================================================
def addComments(request, id):
# 定义添加评论的视图函数
# 这个函数用于处理用户给景点添加评论的请求
# 参数 request:当前HTTP请求
# 参数 id:从URL中捕获的景点ID,如访问 /addComments/123 时,id=123
# URL配置中的 <int:id> 会将路径中的数字作为id参数传递给这个函数
username = request.session.get('username')
# 从 session 中获取当前登录用户的用户名
userInfo = User.objects.get(username=username)
# 根据用户名查询当前用户的完整信息
year, mon, day = getHomeData.getNowTime()
# 获取当前日期,用于页面显示
travelInfo = getAddCommentsData.getTravelById(id)
# 调用 getAddCommentsData 模块中的 getTravelById 函数
# 根据传入的景点ID,从数据库查询该景点的信息
# 用于在评论页面显示景点名称、图片等
if request.method == 'POST':
# 判断请求方法是否为 POST
# GET 请求:用户访问评论页面,显示评论表单
# POST 请求:用户提交评论表单,需要处理评论保存
getAddCommentsData.addComments({
# 调用 getAddCommentsData 模块中的 addComments 函数
# 传入一个字典参数,包含评论所需的所有信息
'id': id,
# 景点ID,标识是对哪个景点进行评论
'rate': int(request.POST.get('rate')),
# 从 POST 数据中获取评分值
# request.POST.get('rate') 获取表单中 name="rate" 字段的值
# int() 将字符串转换为整数,因为表单提交的都是字符串
# 评分通常是 1-5 的整数
'content': request.POST.get('content'),
# 从 POST 数据中获取评论内容
# 这是用户填写的评论文本
'userInfo': userInfo,
# 当前用户的信息,用于记录是谁发表的评论
'travelInfo': travelInfo
# 当前景点的信息,用于记录是对哪个景点发表的评论
})
return redirect('/app/tableData')
# 评论保存成功后,重定向到数据表格页面
# 让用户返回景点列表,可以看到刚评论的景点
return render(request, 'addComments.html', {
# 如果是 GET 请求,渲染评论表单页面
# 如果是 POST 请求且验证失败(虽然代码中没有验证失败的情况),也会渲染表单
'userInfo': userInfo,
# 将当前用户信息传递给模板
'nowTime': {
'year': year,
'mon': getPublicData.monthList[mon - 1],
'day': day
},
# 将当前日期传递给模板
'travelInfo': travelInfo,
# 将景点信息传递给模板,用于显示要评论的是哪个景点
'id': id
# 将景点ID传递给模板
# 可能在表单中作为隐藏字段,或者在URL中保持
})
函数功能总结
| 函数 | 请求方式 | 功能 | 数据流向 |
|---|---|---|---|
| tableData | GET | 显示所有景点的数据表格 | 从数据库获取所有景点,渲染到表格页面 |
| addComments | GET | 显示评论表单 | 根据ID获取景点信息,显示评论页面 |
| addComments | POST | 处理评论提交 | 接收评分和评论内容,保存到数据库,重定向回表格页 |
这两个函数共同实现了景点数据展示 和用户评论功能,让用户可以浏览所有景点并为感兴趣的景点添加评论。
景点数据表格功能
tableData函数 负责展示所有景点的数据表格页面。当用户访问这个页面时,函数首先从会话中获取当前登录用户的用户名,然后查询该用户的完整信息用于页面顶部显示。接着获取当前日期,并调用公共数据模块的函数获取所有景点的完整数据。最后将这些数据打包传递给tableData.html模板进行渲染。
最终呈现给用户的是一个包含所有景点信息的表格,每行显示一个景点的标题、等级、省份、评分、价格等字段,用户可以在表格中浏览所有景点数据。
添加评论功能
addComments函数处理用户给景点添加评论的请求,支持两种请求方式:
当用户通过GET请求访问评论页面时(如点击某个景点的"添加评论"按钮),函数会从会话中获取当前用户信息,从URL参数中获取景点ID,根据这个ID查询该景点的详细信息,然后将用户信息、景点信息、当前日期传递给addComments.html模板,渲染出一个评论表单页面。页面上会显示景点名称等信息,并提供评分选择(通常是1-5星)和评论内容输入框。
当用户在表单中填写完评分和评论内容并点击提交后,浏览器发送POST请求。函数接收到POST请求后,从请求数据中提取评分和评论内容,连同用户信息、景点信息一起打包,调用专门的处理函数将这些数据保存到数据库中。保存成功后,页面自动重定向回数据表格页面,让用户可以继续浏览其他景点。
两个功能的关联
这两个功能是紧密联系的:用户在数据表格页面浏览景点时,可以为感兴趣的景点点击"添加评论"按钮,跳转到评论页面;评论提交后又会返回表格页面。这样形成了一个完整的功能闭环,让用户既能浏览信息,又能参与互动。
添加评论getAddCommentsData.py
python
from app.models import TravelInfo
# 从当前项目的 app 应用中导入 TravelInfo 模型类
#
# app:这是您自己创建的 Django 应用名称,在 INSTALLED_APPS 中注册过
# models:这是 Django 应用中默认存放模型定义的文件(models.py)
# TravelInfo:这是在 models.py 中定义的模型类,对应数据库中的景点信息表
#
# 导入后,就可以在代码中使用 TravelInfo 类来操作数据库中的景点数据
# 例如:TravelInfo.objects.all() 查询所有景点
# TravelInfo.objects.create() 创建新景点记录
from app.utils.getHomeData import getNowTime
# 从 app 应用的 utils 包中的 getHomeData 模块导入 getNowTime 函数
#
# app:当前 Django 应用
# utils:这是一个自定义的 Python 包,通常用于存放工具函数
# 在项目中应该有一个 utils 文件夹,里面包含 __init__.py 文件
# getHomeData:这是 utils 包中的一个 Python 模块文件(getHomeData.py)
# getNowTime:这是在 getHomeData.py 中定义的一个函数,用于获取当前时间
#
# 导入后,可以直接调用 getNowTime() 函数来获取当前的年、月、日信息
# 这个函数在多个视图函数中都被使用,所以被抽取到公共模块中
import json
# 导入 Python 标准库中的 json 模块
# json 是 Python 内置的库,不需要额外安装
#
# json 模块提供了处理 JSON 数据格式的功能:
# - json.loads():将 JSON 字符串解析为 Python 对象(列表、字典)
# - json.dumps():将 Python 对象转换为 JSON 字符串
#
# 在项目中,景点表中的 img_list 和 comments 字段是以 JSON 字符串形式存储的
# 从数据库读取时需要解析为 Python 对象才能使用
# 将数据存入数据库时需要将 Python 对象转换为 JSON 字符串
from app.models import TravelInfo
# 从 app 应用的 models 模块中导入 TravelInfo 模型类
# TravelInfo 对应数据库中的景点信息表,包含景点的所有信息
from app.utils.getHomeData import getNowTime
# 从 app 应用的 utils 包中的 getHomeData 模块导入 getNowTime 函数
# getNowTime 函数用于获取当前的年、月、日信息
import json
# 导入 Python 的 json 模块,用于处理 JSON 数据的序列化和反序列化
# 景点表中的 img_list 和 comments 字段都是以 JSON 字符串格式存储的
def getTravelById(id):
# 定义根据景点 ID 获取景点信息的函数
# 参数 id:要查询的景点的主键 ID
travel = TravelInfo.objects.get(id=id)
# 使用 Django ORM 的 get 方法从数据库中查询主键 ID 等于传入 id 的景点记录
# get 方法返回满足条件的唯一一条记录,如果不存在或存在多条会抛出异常
travel.img_list = json.loads(travel.img_list)
# 将景点对象中的 img_list 字段从 JSON 字符串解析为 Python 列表
# travel.img_list 原本是从数据库取出的 JSON 字符串,如 '["url1.jpg","url2.jpg"]'
# json.loads() 将其转换为 Python 列表,如 ['url1.jpg', 'url2.jpg']
# 这样在代码中就可以方便地遍历和操作图片列表
travel.comments = json.loads(travel.comments)
# 同样,将 comments 字段从 JSON 字符串解析为 Python 列表
# comments 字段存储的是该景点的所有用户评论,每个评论是一个字典
# 解析后变成 [{'author':'张三', 'content':'很好', ...}, ...] 格式
return travel
# 返回处理后的景点对象,此时 img_list 和 comments 已经是 Python 列表
# 调用者可以直接使用这些列表数据
def addComments(commentData):
# 定义添加评论的函数,处理用户提交的评论数据
# 参数 commentData 是一个字典,包含了评论所需的所有信息
# 这个字典由视图函数 addComments 在 POST 请求处理时构建并传入
# 函数开头的注释说明了评论数据的结构
# 'author': author, # 评论作者(这个字段实际被下面的 username 替代了)
# 'content': content, # 评论内容
# 'date': date, # 评论日期
# 'score': score # 评分
# authorId # 作者ID(注释中有但实际没用到?)
year, month, day = getNowTime()
# 调用 getNowTime 函数获取当前的年、月、日
# 用于生成评论的日期,格式如 '2024-03-19'
travelInfo = commentData['travelInfo']
# 从 commentData 字典中获取景点信息对象
# travelInfo 是调用 getTravelById 获取的景点对象,此时它的 comments 已经是 Python 列表
travelInfo.comments.append({
# 向景点的评论列表中添加一条新评论
# travelInfo.comments 是一个列表,append 方法向列表末尾添加新元素
'author': commentData['userInfo'].username,
# 从 commentData 中获取用户信息对象,再获取其 username 属性
# 这是发表评论的用户名
'score': commentData['rate'],
# 从 commentData 中获取评分值(rate)
# 这是用户给景点的打分,通常是 1-5 的整数
'content': commentData['content'],
# 从 commentData 中获取评论内容
# 这是用户填写的评论文本
'date': str(year) + '-' + str(month) + '-' + str(day),
# 拼接当前日期,格式为 "年-月-日"
# 将年、月、日三个整数转换为字符串并用短横线连接
'userId': commentData['userInfo'].id,
# 从 commentData 中获取用户信息对象,再获取其 id 属性
# 这是发表评论的用户ID,可用于后续查询该用户的所有评论
})
travelInfo.comments = json.dumps(travelInfo.comments)
# 将更新后的评论列表(现在是 Python 列表)转换回 JSON 字符串
# json.dumps() 执行序列化,如将 [{'author':'张三',...}] 转换为 JSON 字符串
# 因为数据库中的 comments 字段存储的是 JSON 字符串,不是 Python 列表
travelInfo.img_list = json.dumps(travelInfo.img_list)
# 同样,将图片列表也转换回 JSON 字符串
# 虽然这次操作没有修改图片列表,但为了保持数据一致性也进行了序列化
# 确保存入数据库时所有字段都是正确的格式
travelInfo.save()
# 调用 Django ORM 的 save 方法,将更新后的景点对象保存回数据库
# save() 方法会自动生成 UPDATE SQL 语句,更新数据库中的对应记录
# 此时,新添加的评论就永久保存到数据库中了
函数功能总结
| 函数 | 输入 | 输出 | 主要功能 |
|---|---|---|---|
| getTravelById | 景点ID | 景点对象 | 根据ID查询景点,并将JSON字段解析为Python列表 |
| addComments | 评论数据字典 | 无返回值 | 将新评论添加到景点评论列表,并保存回数据库 |
这两个函数配合工作:getTravelById负责从数据库读取并解析数据,addComments负责将用户提交的评论整合到景点数据中并写回数据库,共同实现了评论功能的完整数据操作。
五、
python
def cityChar(request):
# 定义城市景点分析图表视图函数
# 这个函数处理城市景点分布和等级分布图表的请求
username = request.session.get('username')
# 从 session 会话中获取当前登录用户的用户名
userInfo = User.objects.get(username=username)
# 根据用户名从数据库 User 表中查询该用户的完整信息
year, mon, day = getHomeData.getNowTime()
# 调用 getHomeData 模块中的 getNowTime 函数获取当前日期
Xdata, Ydata = getEchartsData.cityCharDataOne()
# 调用 getEchartsData 模块中的 cityCharDataOne 函数
# 获取城市景点分布数据(按省份统计)
# Xdata: 省份名称列表
# Ydata: 对应省份的景点数量列表
resultData = getEchartsData.cityCharDataTwo()
# 调用 getEchartsData 模块中的 cityCharDataTwo 函数
# 获取景点等级分布数据(按5A、4A等统计)
# resultData: 包含等级名称和数量的字典列表
return render(request, 'cityChar.html', {
# 渲染 cityChar.html 模板并返回响应
'userInfo': userInfo,
# 传递用户信息到模板
'nowTime': {
'year': year,
'mon': getPublicData.monthList[mon - 1],
'day': day
},
# 传递当前日期到模板,用于页面顶部显示
'cityCharOneData': {
'Xdata': Xdata,
'Ydata': Ydata
},
# 传递城市景点分布数据到模板
# 用于生成柱状图或饼图,展示各省份景点数量
'cityCharTwoData': resultData
# 传递景点等级分布数据到模板
# 用于生成饼图,展示各等级景点占比
})
# ============================================================================
def rateChar(request):
# 定义评分分析图表视图函数
# 这个函数处理景点评分分布图表的请求,支持按城市筛选
username = request.session.get('username')
# 获取当前登录用户用户名
userInfo = User.objects.get(username=username)
# 获取当前用户完整信息
year, mon, day = getHomeData.getNowTime()
# 获取当前日期
cityList = getPublicData.getCityList()
# 调用 getPublicData 模块中的 getCityList 函数
# 获取所有有景点数据的城市列表,用于下拉框
travelList = getPublicData.getAllTravelInfoMapData(cityList[0])
# 默认选择第一个城市,获取该城市的所有景点数据
# getAllTravelInfoMapData 函数会处理 JSON 字段
charOneData = getEchartsData.getRateCharDataOne(travelList)
# 调用 getEchartsData 模块中的 getRateCharDataOne 函数
# 获取星级分布数据(基于 travel.star 字段)
# 返回包含星级名称和数量的字典列表
charTwoData = getEchartsData.getRateCharDataTwo(travelList)
# 调用 getEchartsData 模块中的 getRateCharDataTwo 函数
# 获取分数分布数据(基于 travel.score 字段)
# 返回包含分数和数量的字典列表
if request.method == 'POST':
# 判断请求方法是否为 POST
# 用户通过下拉框选择城市并提交表单时会触发 POST 请求
travelList = getPublicData.getAllTravelInfoMapData(request.POST.get('province'))
# 从 POST 数据中获取用户选择的省份
# 调用 getAllTravelInfoMapData 函数获取该省份的所有景点数据
charOneData = getEchartsData.getRateCharDataOne(travelList)
# 重新获取该省份的星级分布数据
charTwoData = getEchartsData.getRateCharDataTwo(travelList)
# 重新获取该省份的分数分布数据
return render(request, 'rateChar.html', {
# 渲染 rateChar.html 模板并返回响应
'userInfo': userInfo,
# 传递用户信息
'nowTime': {
'year': year,
'mon': getPublicData.monthList[mon - 1],
'day': day
},
# 传递当前日期
'cityList': cityList,
# 传递城市列表到模板,用于生成下拉选择框
'charOneData': charOneData,
# 传递星级分布数据到模板
# 用于生成饼图或柱状图
'charTwoData': charTwoData
# 传递分数分布数据到模板
# 用于生成另一个饼图或柱状图
})
# ============================================================================
def priceChar(request):
# 定义价格销量分析图表视图函数
# 这个函数处理价格分布、销量分布和折扣分布图表的请求
username = request.session.get('username')
# 获取当前登录用户用户名
userInfo = User.objects.get(username=username)
# 获取当前用户完整信息
year, mon, day = getHomeData.getNowTime()
# 获取当前日期
cityList = getPublicData.getCityList()
# 获取所有有景点数据的城市列表
travelList = getPublicData.getAllTravelInfoMapData()
# 获取所有城市的景点数据(全国数据)
xData, yData = getEchartsData.getPriceCharDataOne(travelList)
# 调用 getEchartsData 模块中的 getPriceCharDataOne 函数
# 获取价格分布数据
# xData: 价格区间列表,如 ['免费','100元以内',...]
# yData: 对应各区间的景点数量列表
x1Data, y1Data = getEchartsData.getPriceCharDataTwo(travelList)
# 调用 getEchartsData 模块中的 getPriceCharDataTwo 函数
# 获取销量分布数据
# x1Data: 销量区间列表,如 ['300份以内','600份以内',...]
# y1Data: 对应各区间的景点数量列表
disCountPieData = getEchartsData.getPriceCharDataThree(travelList)
# 调用 getEchartsData 模块中的 getPriceCharDataThree 函数
# 获取折扣分布数据
# disCountPieData: 包含折扣类型和数量的字典列表
return render(request, 'priceChar.html', {
# 渲染 priceChar.html 模板并返回响应
'userInfo': userInfo,
# 传递用户信息
'nowTime': {
'year': year,
'mon': getPublicData.monthList[mon - 1],
'day': day
},
# 传递当前日期
'cityList': cityList,
# 传递城市列表到模板(虽然当前代码中 priceChar.html 可能没有使用城市筛选)
'echartsData': {
# 将所有图表数据封装在一个字典中传递给模板
'xData': xData,
# 价格分布横坐标数据
'yData': yData,
# 价格分布纵坐标数据
'x1Data': x1Data,
# 销量分布横坐标数据
'y1Data': y1Data,
# 销量分布纵坐标数据
'disCountPieData': disCountPieData
# 折扣分布饼图数据
}
})
# ============================================================================
def commentsChar(request):
# 定义评论分析图表视图函数
# 这个函数处理评论时间分布、评论评分分布和评论数量分布图表的请求
username = request.session.get('username')
# 获取当前登录用户用户名
userInfo = User.objects.get(username=username)
# 获取当前用户完整信息
year, mon, day = getHomeData.getNowTime()
# 获取当前日期
xData, yData = getEchartsData.getCommentsCharDataOne()
# 调用 getEchartsData 模块中的 getCommentsCharDataOne 函数
# 获取评论时间分布数据(折线图用)
# xData: 日期列表,按倒序排列
# yData: 对应日期的评论数量列表
commentsScorePieData = getEchartsData.getCommentsCharDataTwo()
# 调用 getEchartsData 模块中的 getCommentsCharDataTwo 函数
# 获取评论评分分布数据(饼图用)
# commentsScorePieData: 包含评分和数量的字典列表
x1Data, y1Data = getEchartsData.getCommentsCharDataThree()
# 调用 getEchartsData 模块中的 getCommentsCharDataThree 函数
# 获取评论数量分布数据(柱状图用)
# x1Data: 评论条数区间列表,如 ['1000条以内','2000条以内',...]
# y1Data: 对应各区间的景点数量列表
return render(request, 'commentsChar.html', {
# 渲染 commentsChar.html 模板并返回响应
'userInfo': userInfo,
# 传递用户信息
'nowTime': {
'year': year,
'mon': getPublicData.monthList[mon - 1],
'day': day
},
# 传递当前日期
'echartsData': {
# 将所有图表数据封装在一个字典中传递给模板
'xData': xData,
# 评论时间分布横坐标数据
'yData': yData,
# 评论时间分布纵坐标数据
'commentsScorePieData': commentsScorePieData,
# 评论评分分布饼图数据
'x1Data': x1Data,
# 评论数量分布横坐标数据(柱状图)
'y1Data': y1Data
# 评论数量分布纵坐标数据(柱状图)
}
})
# ============================================================================
def recommendation(request):
# 定义推荐页面视图函数
# 这个函数处理个性化推荐,调用协同过滤算法为用户生成推荐列表
username = request.session.get('username')
# 获取当前登录用户用户名
userInfo = User.objects.get(username=username)
# 获取当前用户完整信息
year, mon, day = getHomeData.getNowTime()
# 获取当前日期
try:
# 尝试执行推荐算法
# 如果成功,返回个性化推荐结果
user_ratings = getUser_ratings()
# 调用函数获取用户的评分数据
# 这里假设有一个 getUser_ratings 函数可以获取用户的历史评分
recommended_items = user_bases_collaborative_filtering(userInfo.id, user_ratings)
# 调用基于用户的协同过滤算法
# 传入当前用户ID和所有用户的评分数据
# 算法返回推荐的景点ID列表
resultDataList = getRecommendationData.getAllTravelByTitle(recommended_items)
# 根据推荐的景点ID列表,从数据库获取完整的景点信息
# 返回景点对象列表,用于页面展示
except:
# 如果推荐算法执行失败(如用户没有历史数据、算法异常等)
resultDataList = getRecommendationData.getRandomTravel()
# 调用备用函数,随机获取一些景点作为推荐
# 这是冷启动问题的简单处理方案
return render(request, 'recommendation.html', {
# 渲染 recommendation.html 模板并返回响应
'userInfo': userInfo,
# 传递用户信息
'nowTime': {
'year': year,
'mon': getPublicData.monthList[mon - 1],
'day': day
},
# 传递当前日期
'resultDataList': resultDataList
# 传递推荐结果列表到模板
# 模板中通过循环遍历展示每个推荐的景点
})
# ============================================================================
def detailIntroCloud(request):
# 定义景点介绍词云图页面视图函数
# 这个函数渲染景点介绍词云图页面
username = request.session.get('username')
# 获取当前登录用户用户名
userInfo = User.objects.get(username=username)
# 获取当前用户完整信息
year, mon, day = getHomeData.getNowTime()
# 获取当前日期
return render(request, 'detailIntroCloud.html', {
# 渲染 detailIntroCloud.html 模板并返回响应
'userInfo': userInfo,
# 传递用户信息
'nowTime': {
'year': year,
'mon': getPublicData.monthList[mon - 1],
'day': day
}
# 传递当前日期
# 词云图的数据可能在前端通过 Ajax 获取,或者后端通过其他方式传递
})
# ============================================================================
def commentContentCloud(request):
# 定义评论内容词云图页面视图函数
# 这个函数渲染评论内容词云图页面
# 注意:这个函数中有重复代码和未使用的变量
username = request.session.get('username')
# 获取当前登录用户用户名
userInfo = User.objects.get(username=username)
# 获取当前用户完整信息
a5Len, commentsLenTitle, provienceDicSort = getHomeData.getHomeTagData()
# 获取首页标签数据
# a5Len: 5A级景区数量
# commentsLenTitle: 评论最多的景区名称
# provienceDicSort: 分布最多的省份
# 这些变量在当前函数中可能没有使用
scoreTop100Data, saleCountTop100 = getHomeData.getAnthorData2()
# 获取评分和销量最高的数据(各300条)
# scoreTop100Data: 随机选取的300个满分景点
# saleCountTop100: 销量最高的300个景点
# 这些变量在当前函数中可能没有使用
year, mon, day = getHomeData.getNowTime()
# 获取当前日期
geoData = getHomeData.getGeoData()
# 获取地理分布数据
# 这个变量在当前函数中可能没有使用
userBarCharData = getHomeData.getUserCreateTimeData()
# 获取用户创建时间分布数据
# 这个变量在当前函数中可能没有使用
return render(request, 'commentContentCloud.html', {
# 第一次渲染 commentContentCloud.html 模板
# 注意:下面传递了很多可能不需要的数据
'userInfo': userInfo,
# 传递用户信息
'a5Len': a5Len,
# 传递5A景区数量(可能不需要)
'commentsLenTitle': commentsLenTitle,
# 传递评论最多的景区名称(可能不需要)
'provienceDicSort': provienceDicSort,
# 传递分布最多的省份(可能不需要)
'scoreTop10Data': scoreTop100Data,
# 传递评分最高的300个景点(变量名是Top10但实际是300)
'nowTime': {
'year': year,
'mon': getPublicData.monthList[mon - 1],
'day': day
},
# 传递当前日期
'geoData': geoData,
# 传递地理分布数据(可能不需要)
'userBarCharData': userBarCharData,
# 传递用户创建时间分布数据(可能不需要)
'saleCountTop10': saleCountTop100
# 传递销量最高的300个景点(变量名是Top10但实际是300)
})
# 注意:下面的代码永远不会被执行,因为上面已经有 return 了
year, mon, day = getHomeData.getNowTime()
# 再次获取当前日期(重复代码)
return render(request, 'commentContentCloud.html', {
# 第二次渲染同一个模板(永远不会执行)
'userInfo': userInfo,
'nowTime': {
'year': year,
'mon': getPublicData.monthList[mon - 1],
'day': day
}
})