我有一个基本信息和活动特定信息的用户。 假设用户是足球运动员,其简介将如下所示:
User.rb #common infos
firstname
lastname
email
sex
address_id
Footballer.rb #or something accurate
position
team
age
weight
shoe_size
...
但是,如果用户是Boxer,其个人资料将由以下内容组成:
#User.rb #same as footballer
firstname
lastname
email
sex
address_id
avatar
#Boxer.rb
weight
handicap
league
...
将此逻辑集成到rails中的最佳方法是什么?
该个人资料将在users#show.html
中呈现
我的应用程序具有轻松注册(仅限电子邮件)和由多个配置文件组成的团队,因此反转逻辑(通过创建Footballer has_one :basic_infos
和Boxer has_one :basic_infos
)似乎很复杂,这需要在每个模型上调用基本信息突出显示(如完整的名字和头像)
我坚持这一点,所以任何帮助都会非常受欢迎
答案 0 :(得分:2)
我在这里看到以下选项。
<强> 1。单表继承(STI)
只需将所有字段转储到一个表格中,然后制作class Boxer < User
,class Footballer < User
。
优点:实施简单。
缺点:表变得臃肿。字段是共享的,即您的足球运动员将拥有weight
和其他领域,反之亦然。
<强> 2。将基本信息移至另一个表
这是您已经概述的选项。
优点:干净的桌子,适当的田地分离。
缺点:复杂的实施。您必须确保每个实例始终只有一个基本配置文件。因此,您必须注意关联宏的:delete_all/:destroy_all
选项,也可能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。添加serialized field
只需添加details
字段,其中包含特定运动员详细信息的JSON / YAML,并将公共字段作为单独的数据库字段。
优点:实施简单。数据库结构也很简单。
缺点:您无法对特定运动员的领域进行有效查询,即您需要获取每条记录以了解其详细信息。
<强> 4。使用Postgresql-specific serialized columns
同上,只有没有&#34;缺点&#34;部分。您将能够对序列化数据进行有效查询。
缺点:您将需要学习如何使用这些字段并查询它们。 MySQL无法实现。
答案 1 :(得分:1)
看起来非常适合STI。关于单表继承的Here's a solid tutorial。
基本上,您可以为从父类# 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
继承的每个不同用户配置文件创建子类。
# == 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
我建议使用annotate_models gem添加一条注释,总结每个模型中的当前架构(以及其他文件)。它看起来像这样:
User
注意Footballer
模型注释如何包含所有列,包括Boxer
和type
子类的列。这是因为您需要将所有列添加到users表,包括名为{{1}}的保留Rails列,它将是要创建的每个记录的子类的名称。
使用STI将为您提供处理特定于子类的逻辑的灵活性。我建议你查看我链接的教程,或者在Rails中有关STI的任何其他文档。