Rails:自我参照联想让我头晕目眩
Rails: Self-referential associations have my head spinning
我目前正在开展一个在 Rails 上使用 Ruby 的小型学校项目,但我在让自我参照关联正常工作时遇到了一些问题。
上下文
我的网络应用程序的预期功能是供用户 post houses/apartments 供其他用户搜索和租用。由于我遇到了特定关联的问题,我正在使用一个完全精简的版本,它只有两个模型,用户和租约。
我正在努力实现的目标
理想情况下,当一个人第一次在网站上注册时,会创建一个用户对象来保存他们的信息,例如电子邮件和密码。然后,用户可以 post 列表或搜索列表。
一旦创建了 post 并且另一个用户决定租用 posted 房子,就会创建一个 Lease 对象,其中包含 posting 用户的 ID 作为以及租用用户的ID,别名分别为“landlord_id”和“tenant_id”。
现在应根据是否有任何租赁对象的 ID 为房东或租户,将用户识别为用户、房东或租户(或房东和租户)。此标识将用于确定用户是否可以访问网站的其他区域。
userFoo.leases
这应该为我提供与用户 ID 相关联的所有租赁对象的列表,无论它是房东还是租户。
userFoo.tenants
这应该给我一个所有用户对象的列表,这些对象的 ID 与 userFoo 的 ID 相关联,作为租户通过租约,如果我要求房东则相反。
代码
用户Class
class User < ApplicationRecord
has_many :tenants, class_name: "Lease", foreign_key: "landlord_id"
has_many :landlords, class_name: "Lease", foreign_key: "tenant_id"
end
租赁Class
class Lease < ApplicationRecord
belongs_to :landlord, class_name: "User"
belongs_to :tenant, class_name: "User"
end
用户Table迁移
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.string :password_digest
t.timestamps
end
end
end
租赁Table迁移
class CreateLeases < ActiveRecord::Migration[6.0]
def change
create_table :leases do |t|
t.references :landlord, null: false, foreign_key: {to_table: :users}
t.references :tenant, null: false, foreign_key: {to_table: :users}
t.timestamps
end
end
end
数据库架构
ActiveRecord::Schema.define(version: 2020_10_18_005954) do
create_table "leases", force: :cascade do |t|
t.integer "landlord_id", null: false
t.integer "tenant_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["landlord_id"], name: "index_leases_on_landlord_id"
t.index ["tenant_id"], name: "index_leases_on_tenant_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.string "password_digest"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
add_foreign_key "leases", "users", column: "landlord_id"
add_foreign_key "leases", "users", column: "tenant_id"
end
怎么了?
userFoo.leases
通常情况下,用户会 have_many 通过将他们的 ID 与租约关联为“user_id”来租约。但是,由于我使用的是“tenant_id”和“landlord_id”,此命令失败,因为它无法在 Leases table.[= 中找到“user_id” 21=]
userFoo.tenants
此命令为我提供了所有租约对象的列表,其中 userFoo 的 ID 关联为“landlord_id”,而不是与 userFoo 的 ID 关联的所有用户对象作为租户。要按原样检索租户,我必须使用命令:
userFoo.tenants.first.tenant
结论
我很难理解这些更深入、更复杂的关联,我花了一些时间试图在 has_many 上找到涵盖所有论点的详细参考,但我真正能找到的东西很少引用 guides.rubyonrails.com 上“员工”和“经理”示例的博客 post。我认为一个问题是我不确定我是否在我的 table 模式中正确地反映了我的模型关联。
如果有人能为我指出正确的方向,我非常乐意自学。我也对其他解决方案持开放态度,但前提是我无法从此设置中获得我想要的功能,因为我的导师特别要求我以这种方式尝试
在此先感谢您的帮助!非常感谢。
根据您的要求,您可以这样尝试:
# app/models/user.rb
class User < ApplicationRecord
has_many :owned_properties, class_name: "Property", foreign_key: "landlord_id"
has_many :rented_properties, class_name: "Property", foreign_key: "tenant_id"
end
这里我声明了两个具有相同table但外键不同的关联。
# app/models/property.rb
class Property < ApplicationRecord
belongs_to :landlord, class_name: "User"
belongs_to :tenant, class_name: "User"
end
这里我拿了一个table通过使用这个用户可以post一个属性其中房东是房子的所有者,稍后你可以添加正在租房的租户一个 属性.
# db/migrations/20201018054951_create_users.rb
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :name, null: false
t.string :email, null: false, index: true
t.string :password_digest, null: false
t.timestamps
end
end
end
以上是您的用户table迁移。
# db/migrations/20201018055351_create_properties.rb
class CreateProperties < ActiveRecord::Migration[6.0]
def change
create_table :properties do |t|
t.references :landlord, foreign_key: {to_table: :users}, null: false
t.references :tenant, foreign_key: {to_table: :users}
t.timestamps
end
end
end
以上是你的属性table迁移。
# db/schema.rb
ActiveRecord::Schema.define(version: 2020_10_18_055351) do
create_table "properties", force: :cascade do |t|
t.bigint "landlord_id", null: false
t.bigint "tenant_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["landlord_id"], name: "index_properties_on_landlord_id"
t.index ["tenant_id"], name: "index_properties_on_tenant_id"
end
create_table "users", force: :cascade do |t|
t.string "name", null: false
t.string "email", null: false
t.string "password_digest", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["email"], name: "index_users_on_email"
end
add_foreign_key "properties", "users", column: "landlord_id"
add_foreign_key "properties", "users", column: "tenant_id"
end
如果要获取用户的所有拥有的属性,请使用 user.owned_properties
。
如果要获取用户的所有租用属性,请使用user.rented_properties
。
^^ 这两种情况你都会得到 Property
class.
的对象
如果你想获得属性的房东,请使用property.landlord
。
如果您想获得 属性 的租户,请使用 property.tenant
。
^^ 这两种情况你都会得到 User
class.
的对象
如果需要,您可以将其他属性添加到属性 table.
,例如:name
、price
等
我想,这会对你有所帮助。谢谢 :) 快乐编码 :)
我目前正在开展一个在 Rails 上使用 Ruby 的小型学校项目,但我在让自我参照关联正常工作时遇到了一些问题。
上下文
我的网络应用程序的预期功能是供用户 post houses/apartments 供其他用户搜索和租用。由于我遇到了特定关联的问题,我正在使用一个完全精简的版本,它只有两个模型,用户和租约。
我正在努力实现的目标
理想情况下,当一个人第一次在网站上注册时,会创建一个用户对象来保存他们的信息,例如电子邮件和密码。然后,用户可以 post 列表或搜索列表。
一旦创建了 post 并且另一个用户决定租用 posted 房子,就会创建一个 Lease 对象,其中包含 posting 用户的 ID 作为以及租用用户的ID,别名分别为“landlord_id”和“tenant_id”。
现在应根据是否有任何租赁对象的 ID 为房东或租户,将用户识别为用户、房东或租户(或房东和租户)。此标识将用于确定用户是否可以访问网站的其他区域。
userFoo.leases
这应该为我提供与用户 ID 相关联的所有租赁对象的列表,无论它是房东还是租户。
userFoo.tenants
这应该给我一个所有用户对象的列表,这些对象的 ID 与 userFoo 的 ID 相关联,作为租户通过租约,如果我要求房东则相反。
代码
用户Class
class User < ApplicationRecord
has_many :tenants, class_name: "Lease", foreign_key: "landlord_id"
has_many :landlords, class_name: "Lease", foreign_key: "tenant_id"
end
租赁Class
class Lease < ApplicationRecord
belongs_to :landlord, class_name: "User"
belongs_to :tenant, class_name: "User"
end
用户Table迁移
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.string :password_digest
t.timestamps
end
end
end
租赁Table迁移
class CreateLeases < ActiveRecord::Migration[6.0]
def change
create_table :leases do |t|
t.references :landlord, null: false, foreign_key: {to_table: :users}
t.references :tenant, null: false, foreign_key: {to_table: :users}
t.timestamps
end
end
end
数据库架构
ActiveRecord::Schema.define(version: 2020_10_18_005954) do
create_table "leases", force: :cascade do |t|
t.integer "landlord_id", null: false
t.integer "tenant_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["landlord_id"], name: "index_leases_on_landlord_id"
t.index ["tenant_id"], name: "index_leases_on_tenant_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.string "password_digest"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
add_foreign_key "leases", "users", column: "landlord_id"
add_foreign_key "leases", "users", column: "tenant_id"
end
怎么了?
userFoo.leases
通常情况下,用户会 have_many 通过将他们的 ID 与租约关联为“user_id”来租约。但是,由于我使用的是“tenant_id”和“landlord_id”,此命令失败,因为它无法在 Leases table.[= 中找到“user_id” 21=]
userFoo.tenants
此命令为我提供了所有租约对象的列表,其中 userFoo 的 ID 关联为“landlord_id”,而不是与 userFoo 的 ID 关联的所有用户对象作为租户。要按原样检索租户,我必须使用命令:
userFoo.tenants.first.tenant
结论 我很难理解这些更深入、更复杂的关联,我花了一些时间试图在 has_many 上找到涵盖所有论点的详细参考,但我真正能找到的东西很少引用 guides.rubyonrails.com 上“员工”和“经理”示例的博客 post。我认为一个问题是我不确定我是否在我的 table 模式中正确地反映了我的模型关联。
如果有人能为我指出正确的方向,我非常乐意自学。我也对其他解决方案持开放态度,但前提是我无法从此设置中获得我想要的功能,因为我的导师特别要求我以这种方式尝试
在此先感谢您的帮助!非常感谢。
根据您的要求,您可以这样尝试:
# app/models/user.rb
class User < ApplicationRecord
has_many :owned_properties, class_name: "Property", foreign_key: "landlord_id"
has_many :rented_properties, class_name: "Property", foreign_key: "tenant_id"
end
这里我声明了两个具有相同table但外键不同的关联。
# app/models/property.rb
class Property < ApplicationRecord
belongs_to :landlord, class_name: "User"
belongs_to :tenant, class_name: "User"
end
这里我拿了一个table通过使用这个用户可以post一个属性其中房东是房子的所有者,稍后你可以添加正在租房的租户一个 属性.
# db/migrations/20201018054951_create_users.rb
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :name, null: false
t.string :email, null: false, index: true
t.string :password_digest, null: false
t.timestamps
end
end
end
以上是您的用户table迁移。
# db/migrations/20201018055351_create_properties.rb
class CreateProperties < ActiveRecord::Migration[6.0]
def change
create_table :properties do |t|
t.references :landlord, foreign_key: {to_table: :users}, null: false
t.references :tenant, foreign_key: {to_table: :users}
t.timestamps
end
end
end
以上是你的属性table迁移。
# db/schema.rb
ActiveRecord::Schema.define(version: 2020_10_18_055351) do
create_table "properties", force: :cascade do |t|
t.bigint "landlord_id", null: false
t.bigint "tenant_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["landlord_id"], name: "index_properties_on_landlord_id"
t.index ["tenant_id"], name: "index_properties_on_tenant_id"
end
create_table "users", force: :cascade do |t|
t.string "name", null: false
t.string "email", null: false
t.string "password_digest", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["email"], name: "index_users_on_email"
end
add_foreign_key "properties", "users", column: "landlord_id"
add_foreign_key "properties", "users", column: "tenant_id"
end
如果要获取用户的所有拥有的属性,请使用 user.owned_properties
。
如果要获取用户的所有租用属性,请使用user.rented_properties
。
^^ 这两种情况你都会得到 Property
class.
如果你想获得属性的房东,请使用property.landlord
。
如果您想获得 属性 的租户,请使用 property.tenant
。
^^ 这两种情况你都会得到 User
class.
如果需要,您可以将其他属性添加到属性 table.
,例如:name
、price
等
我想,这会对你有所帮助。谢谢 :) 快乐编码 :)