ruby-on-rails - 关系模型未按预期工作
问题描述
我使用迁移创建了以下 Active Record 架构,但关系与架构不对应。我尝试过重置、删除、创建和迁移,但在 Rails C 中,如果我创建一个用户 u.User.create!(...),然后查询 u.groups 或 u.genres,我会得到“未定义的方法”
谢谢你的帮助
ActiveRecord::Schema.define(version: 20180603211047) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "genres", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "tag"
t.bigint "user_id"
t.index ["user_id"], name: "index_genres_on_user_id"
end
create_table "genres_users", id: false, force: :cascade do |t|
t.bigint "user_id", null: false
t.bigint "genre_id", null: false
end
create_table "groups", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name"
t.bigint "user_id"
t.index ["user_id"], name: "index_groups_on_user_id"
end
create_table "groups_users", id: false, force: :cascade do |t|
t.bigint "user_id", null: false
t.bigint "group_id", null: false
end
create_table "playlists", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name"
t.string "link"
t.text "description"
t.bigint "group_id"
t.index ["group_id"], name: "index_playlists_on_group_id"
end
create_table "users", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "name"
t.string "token"
t.date "birthday"
t.string "link"
t.string "playlistId"
t.string "country"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
add_foreign_key "genres", "users"
add_foreign_key "groups", "users"
add_foreign_key "playlists", "groups"
end
这里是模型:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
#before_action :authenticate_user!
has_and_belongs_to_many :genres, :through => :genres_users
has_and_belongs_to_many :groups, :through => :groups_users
include Enumerable
end
class Genre < ApplicationRecord
has_and_belongs_to_many :users, :through => :genres_users
end
class Group < ApplicationRecord
has_and_belongs_to_many :users, :through => :groups_users
has_one :playlist
end
class Playlist < ApplicationRecord
belongs_to :group
end
关系是组有用户,用户有流派(最喜欢的流派!),这些是通过连接表(每个用户有多个流派和每个用户多个组)具有和属于关系。每个组都有一个播放列表,并且会有多个播放列表
解决方案
[在OP澄清后编辑]
关系是组有用户,用户有流派(最喜欢的流派!),这些是通过连接表(每个用户有多个流派和每个用户多个组)具有和属于关系。每个组都有一个播放列表,并且会有多个播放列表
首先,您不需要user_id
关于组或流派的专栏,因为这不是设置的工作方式。
class Genre < ApplicationRecord
has_many :favorite_genres
has_many :users, through: :favorite_genres
[... other stuff]
end
class User < ApplicationRecord
has_many :group_memberships
has_many :groups, through: :group_memberships
has_many :favorite_genres
has_many :users, through: :favorite_genres
[... other stuff]
end
class Group < ApplicationRecord
has_many :group_memberships
has_many :users, through: :group_memberships
has_many :playlists
[... other stuff]
end
class Playlist < ApplicationRecord
belongs_to :group
end
class GroupMemberships < ApplicationRecord
belongs_to :user
belongs_to :group
[... other stuff]
end
class FavoriteGenres < ApplicationRecord
belongs_to :user
belongs_to :genre
[... other stuff]
end
因此,您将分组删除 user_id 列。连接发生在 :group_memberships(以前称为 users_groups 的表)中,它是一个 user_id、一个 group_id,然后您可以根据需要拥有其他元数据列(例如,管理员布尔/角色等)。这被称为“有很多通过”关系(http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association)
同样,用户最喜欢的流派是通过直通关系设置的。因此,您将拥有一个单独的数据库表和模型文件,用于连接。
add_foreign_key
我认为你在这个级别根本不需要你的电话,也不需要你的许多索引。您可能会做更多的急切加载或可能在整个连接表上添加索引,并且您会在架构中执行以下操作:
t.index ["user_id", "genre_id"], name: "index_favorite_genres_on_user_id_and_genre_id"
请记住,belongs_to
现在创建了一个验证,以便在 5.x 中出现。您可以通过在模型中添加该行来覆盖它optional: true
,例如belongs_to :foo, optional: true
综上所述,这是您的新架构:
create_table "genres", id: :serial, force: :cascade do |t|
t.string "tag"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "groups", id: :serial, force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "favorite_genres", id: false, force: :cascade do |t|
t.bigint "user_id", null: false
t.bigint "genre_id", null: false
end
create_table "groups_memberships", id: false, force: :cascade do |t|
t.bigint "user_id", null: false
t.bigint "group_id", null: false
end
create_table "playlists", id: :serial, force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name"
t.string "link"
t.text "description"
t.bigint "group_id"
t.index ["group_id"], name: "index_playlists_on_group_id"
end
create_table "users", id: :serial, force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "name"
t.string "token"
t.date "birthday"
t.string "link"
t.string "playlistId"
t.string "country"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
试一试(我没有在应用程序中构建它,因此代码中可能存在一些错误),您现在应该可以运行控制台了:
u = User.create([values])
u.genres (should return nil until you create some relationships)
等等
推荐阅读
- selenium - 单击重新计算步骤时黄瓜功能文件无法找到胶水代码并引发错误消息
- reactjs - 当 useRef 用作实例变量时,useCallback 和 useRef 钩子之间的区别
- embedded-linux - Yocto:没有可用的食谱(tegra)
- vuejs2 - VUEJS 显示嵌套对象
Vue 引导程序 - apache-kafka - Avro 消息未显示在所有分区中
- reactjs - 嵌套的 React 元素不出现
- qt - 需要在qml的listview中显示数千条数据,而不需要同时加载所有数据
- spring - 使用 @RequestBody 时设置默认的内容类型标头
- php - 已加载时未找到 PHP 类
- r - 根据先前的值命名列中的值,没有系统顺序