You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Ruby on Rails课程订阅系统中为课时添加完成状态

Hey there! Let's break down how to implement the "mark lesson as completed" feature for subscribed users. Here's a step-by-step solution tailored to your existing models:

Step 1: Add a CompletedLesson Model

First, we need a way to track which lessons a user has finished. Since this is a many-to-many relationship between User and Lesson, create a join model to store completion status:

Run this Rails generator to create the model and migration:

rails generate model CompletedLesson user:references lesson:references completed_at:datetime
rails db:migrate

This adds a table with user_id, lesson_id, and a completed_at timestamp to record when the lesson was marked done.

Step 2: Update Model Associations

Update your existing models to include associations with the new CompletedLesson model:

User Model

class User < ApplicationRecord
  # Your existing associations
  has_many :subscriptions
  has_many :courses, through: :subscriptions

  # New associations for completed lessons
  has_many :completed_lessons
  has_many :completed_lessons_list, through: :completed_lessons, source: :lesson
end

Lesson Model

class Lesson < ApplicationRecord
  belongs_to :course

  # New association
  has_many :completed_lessons
  has_many :users_who_completed, through: :completed_lessons, source: :user
end

CompletedLesson Model

Add a uniqueness validation to prevent users from marking the same lesson completed multiple times accidentally:

class CompletedLesson < ApplicationRecord
  belongs_to :user
  belongs_to :lesson

  validates :user_id, uniqueness: { scope: :lesson_id, message: "has already completed this lesson" }
end
Step 3: Add a Controller Action to Handle Completion

Create an action to toggle the completion status of a lesson. We'll add this to the LessonsController (you could also make a dedicated CompletedLessonsController if preferred):

class LessonsController < ApplicationController
  before_action :authenticate_user! # Ensure user is logged in
  before_action :validate_subscription, only: [:mark_completed]

  def mark_completed
    lesson = Lesson.find(params[:id])
    completed_lesson = current_user.completed_lessons.find_or_initialize_by(lesson: lesson)

    if completed_lesson.persisted?
      # If already completed, let's allow un-marking it
      completed_lesson.destroy
      render json: { status: "unmarked", message: "Lesson marked as incomplete" }
    else
      completed_lesson.completed_at = Time.current
      completed_lesson.save!
      render json: { status: "marked", message: "Lesson marked as completed" }
    end
  end

  private

  # Ensure the user is subscribed to the lesson's course
  def validate_subscription
    lesson = Lesson.find(params[:id])
    unless current_user.subscriptions.exists?(course_id: lesson.course_id)
      render json: { error: "You must subscribe to the course first to mark lessons as completed" }, status: :forbidden
    end
  end
end
Step 4: Add a Route for the Action

Update your config/routes.rb to map the action to a URL:

resources :lessons do
  post :mark_completed, on: :member
end

This creates a route like /lessons/:id/mark_completed that accepts POST requests.

Step 5: Frontend Button Implementation

Add a button in your lesson view that triggers the action. Here's an example using Rails ERB with Turbo for seamless updates:

<% lesson = @lesson %>
<% if lesson_completed?(lesson, current_user) %>
  <%= button_to "Mark as Incomplete", mark_completed_lesson_path(lesson), 
                method: :post, 
                class: "btn btn-secondary",
                data: { lesson_id: lesson.id } %>
<% else %>
  <%= button_to "Mark as Completed", mark_completed_lesson_path(lesson), 
                method: :post, 
                class: "btn btn-success",
                data: { lesson_id: lesson.id } %>
<% end %>

Add a helper method in app/helpers/application_helper.rb to simplify checking completion status:

module ApplicationHelper
  def lesson_completed?(lesson, user)
    user.completed_lessons.exists?(lesson_id: lesson.id)
  end
end

For AJAX-style updates (no page reload), add this JavaScript (using Turbo):

document.addEventListener('turbo:submit-end', (event) => {
  const form = event.target;
  if (form.action.includes('/mark_completed')) {
    const button = form.querySelector('button');
    if (event.detail.success) {
      if (button.textContent.includes('Completed')) {
        button.textContent = 'Mark as Incomplete';
        button.classList.replace('btn-success', 'btn-secondary');
      } else {
        button.textContent = 'Mark as Completed';
        button.classList.replace('btn-secondary', 'btn-success');
      }
    }
  }
});
Key Notes
  • Security: The validate_subscription method ensures only users who've subscribed to the course can mark lessons as completed—this prevents unauthorized access.
  • Uniqueness: The validation in CompletedLesson stops duplicate entries for the same user and lesson.
  • Flexibility: The toggle functionality lets users un-mark lessons if they want to revisit them.

内容的提问来源于stack exchange,提问作者Cristian Echeverria

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:20:40