【Django】-8- 视图和模型的关联

一、🎉 评论和用户关联 (Model + 视图 + 关联建立)

1. 修改 Model :给评论加 "用户关联" 👥

python 复制代码
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 直接拿到用户!

    python 复制代码
    feedback = Feedback.objects.get(id=1)
    print(feedback.user.username)  # 输出评论用户的用户名
⑤ 如何根据用户找评论?
  • 用户对象 → .feedback_set 拿到所有评论!

    python 复制代码
    user = User.objects.get(username="小明")
    user.feedback_set.all()  # 小明的所有评论

2. 修改视图:控制评论发布逻辑 🔍

判断用户是否已发布评论

python 复制代码
from django.http import JsonResponse

def 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. 发布评论:和用户建立关联 🚀

发布时绑定用户

python 复制代码
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 找评论!

完整小剧场 🎬

  1. 用户发布评论

    小明登录后,填评论表单 → 点 "提交" → 前端发请求到后端。

  2. 视图检查

    后端视图 submit_feedback → 用 request.user 拿到小明 → 检查 user.feedback_set.all() → 发现没发过评论。

  3. 创建评论并关联

    Feedback.objects.create(..., user=request.user) → 评论和小明绑定。

  4. 后续查询

    • 想找 "这条评论是谁发的" → 评论.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 表记录

用户登录后访问其他页面(比如个人中心)时:

  1. 浏览器自动发送 sessionid="随机字符串"
  2. Django 用这个字符串查 django_session 表 → 找到 用户ID=101
  3. 用户ID=101auth_user 表 → 找到对应的用户记录(小明)。
  4. request.user 设为小明 → 视图函数就能用 request.user 访问他的信息啦~

小剧场:数据库联动全过程 🎭

  1. 注册用户

    调用 User.objects.create_user(...)auth_user 表新增 id=101, username="小明"new_user 代表这条记录。

  2. 调用 login
    login(request, new_user)django_session 表新增一条记录:session_key="abc123", session_data="用户ID=101" → 浏览器收到 sessionid=abc123

  3. 访问个人中心

    • 浏览器发请求时带 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 源码的核心代码(简化版):

python 复制代码
def login(request, user):
    # 1. 生成用户的 Session 信息(包含用户 ID 等)
    request.session[SESSION_KEY] = user.id  # 存用户 ID 到 Session
    request.session[BACKEND_SESSION_KEY] = "默认认证方式"
    request.session[HASH_SESSION_KEY] = user.get_session_auth_hash()  # 安全校验码

    # 2. 标记 Session 被修改了,必须保存到数据库!
    request.session.modified = True  # 这一步是关键!

关键在这两行代码:

  1. request.session[SESSION_KEY] = user.id

    • 作用:往 request.session 里 "写数据"(比如用户 ID)。
    • 类比:在快递单上写下 "收件人 = 小明",这张单子就有了实际意义。
  2. request.session.modified = True

    • 作用:告诉 Django"这个 Session 被改过了,赶紧存到数据库里!"
    • 类比:快递单填完后,喊一声 "快递员快来取!",单子就被正式入系统了。

Django 的 "自动保存机制" 在帮忙 🤖

Django 有个 "Session 中间件"django.contrib.sessions.middleware.SessionMiddleware),它会在请求结束时自动检查:

  • 如果 request.session.modifiedTrue → 立刻把 Session 数据存到数据库(django_session 表)。
  • 同时给浏览器发 sessionid Cookie(相当于快递单号给用户)。

小剧场:login() 创建 Session 的全过程 🎬

  1. 调用 login(request, new_user)

    函数执行 → 往 request.session 里写入 user.id=101 → 设 request.session.modified = True

  2. 中间件出手

    请求处理快结束时,Session 中间件检查到 modified=True → "哎呀,Session 改了,赶紧存!"

  3. 数据库新增记录
    django_session 表新增一行:session_key="ABC123", session_data="用户ID=101..."

  4. 浏览器收到 Cookie

    响应里带着 Set-Cookie: sessionid=ABC123 → 浏览器存起来,下次访问自动带上。

总结:login() 是 "触发者",中间件是 "执行者"

login() 函数本身不直接创建 Session,而是通过:

  1. request.session 写数据 → 2. 标记 modified=True → 3. 触发中间件自动保存到数据库。

就像你在快递单上填好信息并喊 "发货",快递系统(中间件)就自动帮你完成后续流程~ 这就是 Django 封装的 "懒人福利" 呀! 😎

四、🔍 feedback_set.all() 藏在哪里?

先看 "诞生条件":外键是前提 🔗

feedback_set 不是凭空出现的,必须满足这个条件:

你的评论模型(比如 Feedback)里,有一个外键字段关联了 User 模型:

python 复制代码
# models.py 里的评论模型
from django.db import models
from django.contrib.auth.models import User

class Feedback(models.Model):
    # 关键!外键关联 User 模型
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    content = models.TextField()  # 评论内容

有了这个外键,Django 会 自动给 User 模型 "附加" 一个 feedback_set 属性,用来存放这个用户的所有评论~

藏在 "用户对象" 里:随用户出现 🧑💻

feedback_set.all() 必须通过 具体的用户对象 调用,比如:

1. 从数据库查一个用户

python 复制代码
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 拿到登录用户,再调用它:

python 复制代码
# 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
python 复制代码
class Feedback(models.Model):
    user = models.ForeignKey(
        User, 
        on_delete=models.CASCADE,
        related_name="my_comments"  # 自定义名字!
    )

之后就可以用新名字调用啦:

python 复制代码
user.my_comments.all()  # 是不是更顺口~

小剧场:feedback_set.all() 的工作日常 🎭

  1. 用户发评论

    小明(user.id=1)发了 2 条评论 → 数据库 Feedback 表中,这两条评论的 user_id 都是 1。

  2. 调用 feedback_set.all()

    后端代码 user = User.objects.get(id=1) → 拿到小明的用户对象 → 调用 user.feedback_set.all()

  3. 返回结果

    Django 自动去 Feedback 表查 user_id=1 的所有记录 → 返回这 2 条评论 → 可以在模板里循环显示啦~

总结:feedback_set.all() 藏在 "用户对象" 里

它是 Django 给外键关联自动生成的 "评论集合工具",必须通过 具体用户对象 调用,用来快速获取该用户的所有评论~

记住:有外键关联才有它,通过用户对象才能找到它! 😊