【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
表)。 - 同时给浏览器发
sessionid
Cookie(相当于快递单号给用户)。
小剧场: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 给外键关联自动生成的 “评论集合工具”,必须通过 具体用户对象 调用,用来快速获取该用户的所有评论~
记住:有外键关联才有它,通过用户对象才能找到它! 😊