【Django】-8- 视图和模型的关联
一、🎉 评论和用户关联 (Model + 视图 + 关联建立)
1. 修改 Model :给评论加 “用户关联” 👥
from django.db import models
from django.contrib.auth.models import User # 引入用户模型class Feedback(models.Model):# 关键!给评论关联用户user = models.ForeignKey(User, # 关联的模型(用户表)default=1, # 默认关联 ID=1 的用户(可根据需求改)on_delete=models.CASCADE, # 用户被删,评论也跟着删)# 其他字段(质量、态度、速度等)quality = models.IntegerField()attitude = models.IntegerField()speed = models.IntegerField()text = models.TextField()anonymous = models.BooleanField()
① 软关联 vs 硬关联
ForeignKey是 硬关联 :评论必须 “绑定” 一个真实用户(数据库里存用户 ID )。- 如果想做 “软关联”(比如存用户名但不严格绑定),可以用
CharField存用户名,但推荐用ForeignKey!
② default=1 :默认值
- 用户没选关联时,默认关联到 ID=1 的用户(比如管理员)。
- 可以改成
default=None,但要处理 “空值” 逻辑~
③ on_delete=models.CASCADE :关联方删除怎么办?
- 如果用户(关联方)被删了,评论(关联对象)也会 级联删除 (一起删掉)。
- 其他选项:
PROTECT(阻止删用户,报错)、SET_NULL(评论的 user 设为 null ,需允许 null )
④ 如何根据评论找用户?
- 评论对象 →
.user直接拿到用户!feedback = Feedback.objects.get(id=1) print(feedback.user.username) # 输出评论用户的用户名
⑤ 如何根据用户找评论?
- 用户对象 →
.feedback_set拿到所有评论!user = User.objects.get(username="小明") user.feedback_set.all() # 小明的所有评论
2. 修改视图:控制评论发布逻辑 🔍
判断用户是否已发布评论
from django.http import JsonResponsedef submit_feedback(request):# 1. 拿到当前登录用户user = request.user# 2. 检查用户是否已发布过评论obj_list = user.feedback_set.all() # 用户的所有评论if obj_list: # 已经发过评论return JsonResponse({"code": -1,"msg": "你已经发布过评论内容了"})# 3. 没发过 → 继续处理发布逻辑...
request.user:当前登录的用户(如果没登录,可能是匿名用户,需处理!)。user.feedback_set.all():Django 自动生成的 “反向关联”,拿到用户的所有评论。- 如果
obj_list不为空 → 说明用户发过评论 → 返回错误,阻止重复发布!
3. 发布评论:和用户建立关联 🚀
发布时绑定用户
from .models import Feedback # 引入评论模型def submit_feedback(request):# 假设前端传的数据在 data 里data = {"quality": 5,"attitude": 5,"speed": 5,"text": "服务超棒!","anonymous": False}# 关键!创建评论时,关联当前用户Feedback.objects.create(quality=data['quality'],attitude=data['attitude'],speed=data['speed'],text=data['text'],anonymous=data['anonymous'],user=request.user # 绑定当前登录用户)return JsonResponse({"code": 0,"msg": "评论发布成功"})
user=request.user:发布评论时,直接把当前登录用户绑定到评论!- 这样评论就和用户 “锁死” 啦~ 后续可以通过
评论.user找用户,或用户.feedback_set找评论!
完整小剧场 🎬
用户发布评论:
小明登录后,填评论表单 → 点 “提交” → 前端发请求到后端。视图检查:
后端视图submit_feedback→ 用request.user拿到小明 → 检查user.feedback_set.all()→ 发现没发过评论。创建评论并关联:
用Feedback.objects.create(..., user=request.user)→ 评论和小明绑定。后续查询:
- 想找 “这条评论是谁发的” →
评论.user.username→ 得到 “小明”。 - 想找 “小明发过哪些评论” →
小明.feedback_set.all()→ 得到所有评论。
- 想找 “这条评论是谁发的” →
必做小事:迁移数据库 🔧
改了 Model 后,必须执行命令,让数据库同步修改(修改模板需要迁移,而前端返回的data直接放到数据库)
二、🗄️ login 函数如何关联 User 表?数据库联动揭秘!
User 表是啥?数据库里的 “用户花名册”📖
Django 自带的 User 表(在数据库里通常叫 auth_user)就像图书馆的 “读者花名册”:
- 每一行记录对应一个用户(比如 “小明、小红”)。
- 字段包括
id(唯一编号,比如 101)、username(用户名)、password(加密后的密码)等。
当你用 User.objects.create_user(...) 创建 new_user 时:
- 数据库的
auth_user表会 新增一条新记录(比如id=101, username="小明")。 new_user就是这条记录在代码里的 “代言人”(模型实例)。
login 函数如何 “绑定” 数据库?3 步联动 ⚙️
第一步:从 new_user 里拿 “用户 ID”(花名册编号)
new_user 里藏着数据库 User 表的 id(比如 new_user.id=101),login 函数会先 “偷偷记下” 这个 ID:
# 源码里的关键操作(简化版)
user_id = new_user.id # 拿到数据库里的用户编号,比如 101
类比:图书馆借书时,先查读者的 “会员卡编号”(对应 User 表的 id)。
第二步:把 “用户 ID” 存到 Session 表(借书登记本)
Django 有个专门的 django_session 表,像图书馆的 “借书登记本”,记录 “谁借了书”(谁登录了):
login 函数会在这个表里新增一条记录,内容是:
session_key: "随机字符串" # 相当于“借书凭证号”
session_data: "用户ID=101, 认证方式=...", # 存在用户 ID
expire_date: "2025-09-03" # 过期时间(默认2周)
同时,浏览器会收到 sessionid="随机字符串" 的 Cookie(相当于把 “借书凭证号” 给用户揣着)。
第三步:后续请求时,通过 Session 找到 User 表记录
用户登录后访问其他页面(比如个人中心)时:
- 浏览器自动发送
sessionid="随机字符串"。 - Django 用这个字符串查
django_session表 → 找到用户ID=101。 - 用
用户ID=101查auth_user表 → 找到对应的用户记录(小明)。 - 把
request.user设为小明 → 视图函数就能用request.user访问他的信息啦~
小剧场:数据库联动全过程 🎭
注册用户:
调用User.objects.create_user(...)→auth_user表新增id=101, username="小明"→new_user代表这条记录。调用
login:login(request, new_user)→django_session表新增一条记录:session_key="abc123", session_data="用户ID=101"→ 浏览器收到sessionid=abc123。访问个人中心:
- 浏览器发请求时带
sessionid=abc123。 - Django 查
django_session表 → 得到用户ID=101。 - 查
auth_user表 → 找到id=101的小明 →request.user变成小明。 - 个人中心视图显示 “小明的个人信息”。
- 浏览器发请求时带
总结:三张表的 “合作舞蹈” 💃
auth_user表:存用户的 “原始信息”(用户名、密码等)。django_session表:存 “登录状态”(哪个sessionid对应哪个用户 ID)。login函数:是 “牵线红娘”,把new_user(对应auth_user记录)和Session表绑定,让后续请求能通过sessionid找到用户。
这就是 login 函数和数据库 User 表的 “暗箱操作”~ 全程自动,不用你手动写 SQL,Django 都帮你搞定啦! 🎉
三、🎭 为什么 login() 函数能 “召唤” 出 Session?
先回忆:Session 是啥?
Session 就像 “快递单号” :
- 浏览器端:存着
sessionid=ABC123(类似快递单号贴在包裹上)。 - 服务器端:
django_session表存着 “单号 ABC123 对应用户小明”(类似快递柜系统记录)。
有了这个 “单号”,服务器才能记住 “谁登录过”~
login() 函数里藏着 “创建 Session 的密码”🔑
看 Django 源码的核心代码(简化版):
def login(request, user):# 1. 生成用户的 Session 信息(包含用户 ID 等)request.session[SESSION_KEY] = user.id # 存用户 ID 到 Sessionrequest.session[BACKEND_SESSION_KEY] = "默认认证方式"request.session[HASH_SESSION_KEY] = user.get_session_auth_hash() # 安全校验码# 2. 标记 Session 被修改了,必须保存到数据库!request.session.modified = True # 这一步是关键!
关键在这两行代码:
request.session[SESSION_KEY] = user.id- 作用:往
request.session里 “写数据”(比如用户 ID)。 - 类比:在快递单上写下 “收件人 = 小明”,这张单子就有了实际意义。
- 作用:往
request.session.modified = True- 作用:告诉 Django“这个 Session 被改过了,赶紧存到数据库里!”
- 类比:快递单填完后,喊一声 “快递员快来取!”,单子就被正式入系统了。
Django 的 “自动保存机制” 在帮忙 🤖
Django 有个 “Session 中间件”(django.contrib.sessions.middleware.SessionMiddleware),它会在请求结束时自动检查:
- 如果
request.session.modified是True→ 立刻把 Session 数据存到数据库(django_session表)。 - 同时给浏览器发
sessionidCookie(相当于快递单号给用户)。
小剧场:login() 创建 Session 的全过程 🎬
调用
login(request, new_user):
函数执行 → 往request.session里写入user.id=101→ 设request.session.modified = True。中间件出手:
请求处理快结束时,Session 中间件检查到modified=True→ “哎呀,Session 改了,赶紧存!”数据库新增记录:
django_session表新增一行:session_key="ABC123", session_data="用户ID=101..."。浏览器收到 Cookie:
响应里带着Set-Cookie: sessionid=ABC123→ 浏览器存起来,下次访问自动带上。
总结:login() 是 “触发者”,中间件是 “执行者”
login() 函数本身不直接创建 Session,而是通过:
- 往
request.session写数据 → 2. 标记modified=True→ 3. 触发中间件自动保存到数据库。
就像你在快递单上填好信息并喊 “发货”,快递系统(中间件)就自动帮你完成后续流程~ 这就是 Django 封装的 “懒人福利” 呀! 😎
四、🔍 feedback_set.all() 藏在哪里?
先看 “诞生条件”:外键是前提 🔗
feedback_set 不是凭空出现的,必须满足这个条件:
你的评论模型(比如 Feedback)里,有一个外键字段关联了 User 模型:
# models.py 里的评论模型
from django.db import models
from django.contrib.auth.models import Userclass Feedback(models.Model):# 关键!外键关联 User 模型user = models.ForeignKey(User, on_delete=models.CASCADE)content = models.TextField() # 评论内容
有了这个外键,Django 会 自动给 User 模型 “附加” 一个 feedback_set 属性,用来存放这个用户的所有评论~
藏在 “用户对象” 里:随用户出现 🧑💻
feedback_set.all() 必须通过 具体的用户对象 调用,比如:
1. 从数据库查一个用户
from django.contrib.auth.models import User# 查一个用户(比如 ID=1 的用户)
user = User.objects.get(id=1)# 调用 feedback_set.all() → 拿到这个用户的所有评论
user_comments = user.feedback_set.all() # 🌟 在这里!
2. 用当前登录用户(request.user)
在视图函数里,通过 request.user 拿到登录用户,再调用它:
# views.py 里的视图函数
def my_comments(request):# 当前登录用户(已登录状态)current_user = request.user# 拿到该用户的所有评论user_comments = current_user.feedback_set.all() # 🌟 也在这里!return render(request, 'my_comments.html', {'comments': user_comments})
为什么叫 feedback_set?可以改名吗? 📛
- 名字是 Django 自动生成的:
小写模型名 + _set(评论模型是Feedback→ 小写是feedback→ 所以叫feedback_set)。 - 想改个好听的名字?可以在定义外键时加
related_name:
class Feedback(models.Model):user = models.ForeignKey(User, on_delete=models.CASCADE,related_name="my_comments" # 自定义名字!)
之后就可以用新名字调用啦:
user.my_comments.all() # 是不是更顺口~
小剧场:feedback_set.all() 的工作日常 🎭
用户发评论:
小明(user.id=1)发了 2 条评论 → 数据库Feedback表中,这两条评论的user_id都是 1。调用
feedback_set.all():
后端代码user = User.objects.get(id=1)→ 拿到小明的用户对象 → 调用user.feedback_set.all()。返回结果:
Django 自动去Feedback表查user_id=1的所有记录 → 返回这 2 条评论 → 可以在模板里循环显示啦~
总结:feedback_set.all() 藏在 “用户对象” 里
它是 Django 给外键关联自动生成的 “评论集合工具”,必须通过 具体用户对象 调用,用来快速获取该用户的所有评论~
记住:有外键关联才有它,通过用户对象才能找到它! 😊
