用户属性取决于用户的类型:Rails中实现最好的方法(User's attributes d

2019-11-03 23:45发布

我有一个基本的相关信息和活动的具体相关信息的用户。 比方说,用户是一名足球运动员,其外形看起来像:

User.rb #common infos
  firstname
  lastname
  email
  sex
  address_id

Footballer.rb #or something accurate
  position
  team
  age
  weight
  shoe_size
  ...

但是,如果用户是为义和团,其形象将组成如下:

#User.rb #same as footballer
  firstname
  lastname
  email
  sex
  address_id
  avatar

#Boxer.rb
  weight
  handicap
  league
  ...

什么是在铁轨这种逻辑整合的最佳方式? 配置文件将在呈现users#show.html我的应用程序可以方便地注册(仅电子邮件)和多个配置文件组成的团队,所以它看起来很复杂反转逻辑(通过创建一个Footballer has_one :basic_infosBoxer has_one :basic_infos )这将需要对每个模型的基本相关信息亮点(如全名和头像)的呼叫

我在这stucked,所以任何帮助将是非常欢迎

Answer 1:

我在这里看到下面的选项。

1.单个表继承( STI )

只是倾倒在一个表中的所有领域,使class Boxer < Userclass Footballer < User

优点:实现简单。

缺点:表变得臃肿。 域是共享的,即你的足球运动员将有weight等领域,反之亦然。

2.移动基站信息到另一个表

这就是你已经列出的选项。

优点:清洁表,字段的适当分离。

缺点:复杂的实现。 你必须确保总有一个且只有一个为每个实例的基本轮廓。 所以,你必须要注意:delete_all/:destroy_all协会macroses的选择,也许before_create/after_destroy为好。

下面是关于如何组织在你的代码的例子:

# models

class Profile < ActiveRecord::Base
  # you need to create
  #   t.integer :user_id
  #   t.string  :user_type
  # in a migration for 'profiles' table

  belongs_to :user, polymorphic: true
end

class User < ActiveRecord::Base
  # This will disable STI
  self.abstract_class = true
  has_one :profile, as: :user, dependent: :destroy

  def some_common_user_method
    # ...
  end

  def pick_profile
    self.profile && return self.profile
    if self.persisted?
      self.create_profile
    else
      self.build_profile
    end
  end
end

class Footballer < User
end

# controllers

class UsersController < ApplicationController
  before_action :set_klass
  before_action :set_user, except: [:index, :create]

  # ...

  def create
    @user = @klass.new
    @user.pick_profile
    # etc...
  end

  def destroy
    @user.destroy
  end

  private

  def set_klass
    # white-list search for class
    @klass = User.descendants.find{|k| k == params[:type].camelize.constantize}
  end

  def set_user
    @user = @klass.find(params[:id])
  end
end

# routes

# for URLs like /users/1?type=footballer
resources :users

# for URLs like /users/footballer/1
resources :users, path_prefix: '/users/:type'

3.添加序列化领域

只需添加details场的具体细节运动员JSON / YAML,并有共同的领域作为单独的数据库字段。

优点:实现简单。 数据库结构保持简单为好。

缺点:你将不能够进行有效的查询特定的运动员的领域,例如,你将需要获取每个记录以了解它的细节。

4.使用PostgreSQL相关序列列

同上,只是没有“缺点”的一部分。 您将能够做出序列化数据高效的查询。

缺点:您需要学习如何使用字段和查询它们。 无法与MySQL。



Answer 2:

貌似很适合STI 。 这里有一个坚实的教程中有关单表继承。

从本质上讲,你可以创建你的每一个不同的用户界面,所有从父类继承的子类User

# app/models/user.rb
class User < ActiveRecord::Base
  # validations, associations, methods, etc.
end

# app/models/footballer.rb 
class Footballer < User
  # custom validations, methods, etc.
end

# app/models/boxer.rb
class Boxer < User
  # custom validations, methods, etc.
end

我会建议使用annotate_models宝石加注释总结了每个型号的当前模式(其它文件中)。 它看起来像这样:

# == Schema Info
#
# Table name: users
#
#  id                  :integer(11)    not null, primary key
#  firstname           :string(255)
#  lastname            :string(255)
#  email               :string(255)
#  sex                 :string(255)
#  address_id          :integer(11)
#  avatar              :string(255)
#  weight              :integer(11)
#  handicap            :string(255)
#  league              :string(255)
#  position            :string(255)
#  team                :string(255)
#  age                 :integer(11)
#  weight              :float
#  shoe_size           :float
#  weight              :float
#  handicap            :float
#  league              :string(255)
#  type                :string(255)
#

class User < ActiveRecord::Base
  ...
end

注意如何User模型注释中包含的所有列,包括那些为FootballerBoxer的子类。 这是因为你需要的所有列添加到用户表,其中包括一个名为保留Rails的列type ,这将是正在创建的子类为每个记录的名称。

使用STI会给你处理特定的子类逻辑的灵活性。 我建议你看看我联系的教程,或约Rails内部STI的其他文件。



文章来源: User's attributes depend on user's type: best way to implement in Rails