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

Rails技术咨询:如何对用户、患者及医生角色进行建模?

Great question! Building a doctor-patient booking app like Practo/ZocDoc in Rails requires a solid user modeling strategy that balances simplicity, scalability, and separation of concerns. Let's break down the two most common approaches you can take, along with pros, cons, and actionable code examples.

1. Single Table Inheritance (STI) - The Simplest Approach

STI is perfect when your Patient and Doctor share most core attributes (like name, email, password) but have a few unique fields. It uses a single users table with a type column to distinguish between user roles.

Step-by-Step Implementation

1.1 Generate the Base User Model

Start with a User model that includes shared attributes plus fields unique to doctors (like approval status) and patients:

# Generate via Rails generator
rails generate model User name:string email:string:uniq password_digest:string type:string approved:boolean:default=>false date_of_birth:date phone_number:string specialization:string education:text hospital:string
rails db:migrate

1.2 Create Patient and Doctor Subclasses

These inherit from User and add role-specific validations or logic:

# app/models/user.rb
class User < ApplicationRecord
  has_secure_password # Or use Devise for authentication
  # Shared validations
  validates :name, :email, presence: true
end

# app/models/patient.rb
class Patient < User
  # Patient-specific validations
  validates :date_of_birth, :phone_number, presence: true
  # Associations: A patient has many appointments
  has_many :appointments, foreign_key: :patient_id, dependent: :destroy
end

# app/models/doctor.rb
class Doctor < User
  # Doctor-specific validations
  validates :specialization, :education, :hospital, presence: true
  # Default scope to only show approved doctors to patients
  default_scope { where(approved: true) }
  # Associations: A doctor has many appointments
  has_many :appointments, foreign_key: :doctor_id, dependent: :destroy
end

1.3 Handle Authentication & Authorization

Use Devise for authentication (recommended) and add role checks in controllers. For example, a doctor's dashboard controller:

# app/controllers/doctors/dashboard_controller.rb
class Doctors::DashboardController < ApplicationController
  before_action :authenticate_user!
  before_action :ensure_doctor
  before_action :ensure_approved

  def index
    @upcoming_appointments = current_user.appointments.where('start_time > ?', Time.now)
    @patients = current_user.appointments.distinct.pluck(:patient)
  end

  private

  def ensure_doctor
    redirect_to root_path, alert: "Access denied." unless current_user.is_a?(Doctor)
  end

  def ensure_approved
    redirect_to root_path, alert: "Your account is pending admin approval." unless current_user.approved?
  end
end

1.4 Route Setup

Use namespaces to separate patient and doctor routes:

# config/routes.rb
Rails.application.routes.draw do
  devise_for :users

  namespace :doctors do
    get 'dashboard', to: 'dashboard#index'
    resources :appointments, only: [:index, :update]
  end

  namespace :patients do
    get 'dashboard', to: 'dashboard#index'
    resources :appointments, only: [:new, :create, :index]
  end

  root 'home#index'
end

Pros & Cons of STI

  • Pros: Minimal code, fast queries (no joins), easy to implement.
  • Cons: The users table will have empty columns (e.g., patients don't need specialization), which can get messy as roles grow.

2. Separate User + Associated Models - The Scalable Approach

If your Patient and Doctor have drastically different attributes (or you plan to add more roles like admins later), use a standalone User model for authentication, with separate Patient and Doctor models linked via one-to-one associations.

Step-by-Step Implementation

2.1 Generate Core Models

# User model (only authentication-related fields)
rails generate model User name:string email:string:uniq password_digest:string
rails db:migrate

# Patient model (patient-specific fields)
rails generate model Patient user:references date_of_birth:date phone_number:string
rails db:migrate

# Doctor model (doctor-specific fields + approval status)
rails generate model Doctor user:references specialization:string education:text hospital:string approved:boolean:default=>false
rails db:migrate

2.2 Define Associations

# app/models/user.rb
class User < ApplicationRecord
  has_secure_password
  has_one :patient, dependent: :destroy
  has_one :doctor, dependent: :destroy

  # Helper methods to check role
  def patient?
    patient.present?
  end

  def doctor?
    doctor.present?
  end
end

# app/models/patient.rb
class Patient < ApplicationRecord
  belongs_to :user
  validates :date_of_birth, :phone_number, presence: true
  has_many :appointments, dependent: :destroy
end

# app/models/doctor.rb
class Doctor < ApplicationRecord
  belongs_to :user
  validates :specialization, :education, :hospital, presence: true
  default_scope { where(approved: true) }
  has_many :appointments, dependent: :destroy
end

2.3 Handle Registration

When a user signs up, let them select their role and create the associated model:

# app/controllers/users/registrations_controller.rb (override Devise)
class Users::RegistrationsController < Devise::RegistrationsController
  def create
    super do
      if params[:user][:role] == 'patient'
        resource.create_patient(patient_params)
      elsif params[:user][:role] == 'doctor'
        resource.create_doctor(doctor_params)
      end
    end
  end

  private

  def patient_params
    params.require(:patient).permit(:date_of_birth, :phone_number)
  end

  def doctor_params
    params.require(:doctor).permit(:specialization, :education, :hospital)
  end
end

Pros & Cons of This Approach

  • Pros: Clean, normalized database, easy to add new roles later, no empty columns.
  • Cons: Requires more code, queries need joins (slight performance hit), and you'll have to manage two models per user during registration.

3. Key Considerations Beyond Modeling

  • Approval Workflow: Add an admin interface to approve/reject doctor accounts. You can send email notifications using Action Mailer when a doctor's status changes.
  • Authorization: Use gems like Pundit or CanCanCan to define granular permissions (e.g., doctors can only edit their own appointments, patients can't access doctor dashboards).
  • Appointment Model: Create an Appointment model with belongs_to :patient and belongs_to :doctor (or belongs_to :user, polymorphic: true if using STI) to link patients and doctors.
  • Dashboard Customization: Use partials and layout inheritance to keep patient/doctor dashboards distinct without repeating code.

4. Which Approach Should You Choose?

  • Go with STI if your patient and doctor share most attributes and you want to get up and running quickly.
  • Go with separate models if your roles have very different data needs, or you anticipate adding more roles (like clinic admins) in the future.

Either way, both patterns are well-supported in Rails and will work for your booking app use case!

内容的提问来源于stack exchange,提问作者Mohamed Ahmed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:01:38