如何在Rails 5中搭建LMS:课程与课时关联方案咨询
嘿,作为Rails新手能想到用关联来设计Course和Lesson,这点思路很对!下面一步步帮你搞定这两个模型的关联,还有前端创建课程时添加课时的实现方案:
首先用Rails的生成器快速创建模型,Lesson需要关联到Course,所以生成Lesson模型时加上course:references,它会自动帮我们添加外键字段和基础关联代码:
# 生成Course模型(带标题和描述字段) rails generate model Course title:string description:text # 生成Lesson模型(关联Course,带标题、文本内容、视频链接字段) rails generate model Lesson title:string content:text video_url:string course:references
然后执行迁移,把表结构同步到数据库:
rails db:migrate
接下来完善模型里的关联逻辑:
在app/models/course.rb里添加:
class Course < ApplicationRecord # 一个Course有多个Lesson,删除Course时自动删除关联的Lesson has_many :lessons, dependent: :destroy end
在app/models/lesson.rb里(其实生成器已经帮你加了belongs_to :course,确认一下就行):
class Lesson < ApplicationRecord belongs_to :course end
因为Lesson是属于Course的子资源,用嵌套路由更符合RESTful设计,也能让前端更方便地关联课程和课时。修改config/routes.rb:
Rails.application.routes.draw do # 嵌套路由:所有Lesson的操作都关联到对应的Course resources :courses do resources :lessons end end
这样路由就会是/courses/:course_id/lessons这种形式,能明确知道哪个课时属于哪个课程。
因为用了嵌套路由,Lesson的CRUD操作需要先找到对应的Course,所以修改app/controllers/lessons_controller.rb:
class LessonsController < ApplicationController before_action :set_course before_action :set_lesson, only: [:show, :edit, :update, :destroy] # GET /courses/:course_id/lessons def index @lessons = @course.lessons end # GET /courses/:course_id/lessons/1 def show end # GET /courses/:course_id/lessons/new def new @lesson = @course.lessons.build end # GET /courses/:course_id/lessons/1/edit def edit end # POST /courses/:course_id/lessons def create @lesson = @course.lessons.build(lesson_params) if @lesson.save redirect_to @course, notice: '课时创建成功!' else render :new end end # PATCH/PUT /courses/:course_id/lessons/1 def update if @lesson.update(lesson_params) redirect_to [@course, @lesson], notice: '课时更新成功!' else render :edit end end # DELETE /courses/:course_id/lessons/1 def destroy @lesson.destroy redirect_to @course, notice: '课时已删除!' end private # 先找到对应的Course def set_course @course = Course.find(params[:course_id]) end # 找到对应的Lesson def set_lesson @lesson = @course.lessons.find(params[:id]) end # 强参数,只允许指定字段 def lesson_params params.require(:lesson).permit(:title, :content, :video_url) end end
1. 先搞定Course的CRUD
用scaffold快速生成Course的创建、编辑页面:
rails generate scaffold Course title:string description:text
这样你就有了/courses/new页面来创建课程,创建后会跳转到课程详情页。
2. 在课程详情页添加课时表单
修改app/views/courses/show.html.erb,在课程信息下方添加课时列表和新增课时的表单:
<h1><%= @course.title %></h1> <p><%= @course.description %></p> <div class="lessons-section"> <h2>本课程的课时</h2> <% if @course.lessons.empty? %> <p>还没有课时,赶紧添加第一个吧!</p> <% else %> <ul> <% @course.lessons.each do |lesson| %> <li> <%= link_to lesson.title, course_lesson_path(@course, lesson) %> <span class="actions"> <%= link_to '编辑', edit_course_lesson_path(@course, lesson) %> <%= link_to '删除', course_lesson_path(@course, lesson), method: :delete, data: { confirm: '确定要删除这个课时吗?' } %> </span> </li> <% end %> </ul> <% end %> <h3>添加新课时</h3> <%= form_with(model: [@course, @course.lessons.build], local: true) do |form| %> <% if @lesson&.errors.any? %> <div class="alert alert-danger"> <h4><%= pluralize(@lesson.errors.count, '错误') %> 无法创建课时:</h4> <ul> <% @lesson.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <div class="form-group"> <%= form.label :title, '课时标题' %> <%= form.text_field :title, class: 'form-control' %> </div> <div class="form-group"> <%= form.label :content, '文本内容' %> <%= form.text_area :content, class: 'form-control', rows: 5 %> </div> <div class="form-group"> <%= form.label :video_url, '视频链接' %> <%= form.text_field :video_url, class: 'form-control' %> </div> <%= form.submit '创建课时', class: 'btn btn-primary' %> <% end %> </div> <%= link_to '编辑课程', edit_course_path(@course) %> | <%= link_to '返回课程列表', courses_path %>
- 给
dependent: :destroy点赞:这个选项会在删除课程时自动删除所有关联的课时,避免数据库里出现没有所属课程的“孤儿课时”。 - 如果你想在创建课程时直接添加第一个课时,可以在Course的new页面加入嵌套表单,但对于新手来说,先分步骤实现课程创建后再添加课时会更简单。
- 可以给Lesson的表单加一些前端验证,比如必填字段提示,提升用户体验。
这样一套下来,你就能实现课程和课时的关联,并且在前端完成课程创建、为课程添加课时的操作啦!
内容的提问来源于stack exchange,提问作者chubbymaus




