Rails – 保存现有记录 ID 而不是复制记录
Rails – saving existing record id instead of duplicating record
我现在正在学习Rails,我完全是个新手。希望能得到这里Rails专家的帮助。我试图通过 Whosebug 进行搜索,但未能找到解决方案。
所以有一个 Pet
模型有 Breed
模型作为参考,它参考了 Category
模型,所以 3 个模型:
class Pet < ApplicationRecord
belongs_to :breed
accepts_nested_attributes_for :breed
has_one :category, through: :breed
class Breed < ApplicationRecord
belongs_to :category
has_many :pets, dependent: :destroy
accepts_nested_attributes_for :category
class Category < ApplicationRecord
has_many :breeds, dependent: :destroy
has_many :pets, through: :breed
我已经使用 simple_form
设置了表格。创建新宠物时,用户可以 select
与 Breed
关联的现有 Category
选项。然后在表单中,将 breed.name
属性设置为用户可以输入自己的 text_field
。请在下面的嵌套属性部分:
...
<!-- Nested Breed & Category Attributes -->
<%= f.simple_fields_for :breed, Breed.new do |breed| %>
<%= breed.error_notification %>
<%= breed.error_notification message: breed.object.errors[:base].to_sentence if breed.object.errors[:base].present? %>
<div class="form-row">
<div class="form-group col-md-4">
<%= breed.association :category, collection: Category.order(:id),
label: "Animal Category", required: true %>
</div>
<div class="form-group col-md-8">
<%= breed.input :name, required: true,
label: "Breed", placeholder: "Domestic short hair",
hint: "Type 'unknown', if unsure", class: "form-control" %>
</div>
</div>
<% end %>
所以我的问题是,如何使用现有 breed_id
记录保存新宠物而不用不同的 breed_id
复制它?
所以目前,我已经有一个 breed_id : 1
的现有记录,它由 category_id: 1 (for Cat), and name: 'domestic short hair'
组成 – 如果我在表格中创建一个具有这些相同值的新宠物,它将复制记录,它应该保存 breed_id: 1
,但相反,新宠物保存在 breed_id: 15
...
当我查看 console
时:
irb(main):001:0> Breed.find_by_name("domestic short hair")
Breed Load (6.8ms) SELECT "breeds".* FROM "breeds" WHERE "breeds"."name" = LIMIT [["name", "domestic short hair"], ["LIMIT", 1]]
=> #<Breed id: 1, name: "domestic short hair", category_id: 1, created_at: "2021-08-14 13:12:41.266154000 +0000", updated_at: "2021-08-14 13:12:41.266154000 +0000">
irb(main):002:0> Breed.last
Breed Load (8.2ms) SELECT "breeds".* FROM "breeds" ORDER BY "breeds"."id" DESC LIMIT [["LIMIT", 1]]
=> #<Breed id: 15, name: "domestic short hair", category_id: 1, created_at: "2021-08-16 04:08:31.602249000 +0000", updated_at: "2021-08-16 04:08:31.602249000 +0000">
更新我的 controller / model / form helper
的最佳方法是什么(我不确定是哪一种),这样可以防止重复?我目前没有 breeds_controller
,只使用 pets_controller
,这是脚手架中的一个基本的:
pets_controller.rb
def show
end
# GET /pets/new
def new
@pet = Pet.new
@pet.build_breed
end
# GET /pets/1/edit
def edit
end
# POST /pets or /pets.json
def create
@pet = current_user.pets.new(pet_params)
respond_to do |format|
if @pet.save
format.html { redirect_to @pet, notice: "Pet was successfully added." }
format.json { render :show, status: :created, location: @pet }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @pet.errors, status: :unprocessable_entity }
end
end
end
...
private
def pet_params
params.require(:pet).permit(:owner_id, :name, :dob, :gender, :bio, :instagram,
breed_attributes: [:name, :category_id])
end
我试过在模型breed.db
中写一个before_save :existing_breed_record
,我认为这是错误的...
class Breed < ApplicationRecord
belongs_to :category
has_many :pets, dependent: :destroy
accepts_nested_attributes_for :category
before_save :existing_breed_record
private
def existing_breed_record
if self.find_by(category_id, name).exists?
return self.id
end
end
这是我的 schema.db
create_table "breeds", force: :cascade do |t|
t.string "name"
t.bigint "category_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["category_id"], name: "index_breeds_on_category_id"
end
create_table "categories", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "pets", force: :cascade do |t|
t.bigint "owner_id", null: false
t.bigint "breed_id", null: false
t.string "name"
t.date "dob"
t.integer "gender"
t.string "bio"
t.string "instagram"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["breed_id"], name: "index_pets_on_breed_id"
t.index ["owner_id"], name: "index_pets_on_owner_id"
end
我从指南中了解到,我认为有 find_or_create_by
方法是我需要的,但我不确定在哪里以及如何实施它...
对于数据库和Rails专家来说,为了学习,你建议我下次如何做更好?我是否应该将 breed
作为 Pet 模型的字符串 attribute/column 并直接将 Category
模型关联到 Pet?
如果您想使用嵌套形式,可以将 before_save
方法添加到 Pet
\pet.rb
before_save :check_existing_breed
def check_existing_breed
if self.breed&.name and breed = Breed.find_by(name: self.breed&.name)
self.breed = breed
end
end
在老师的帮助下,我们终于找到了解决办法。它有点乱,但它不再重复任何现有记录!
首先,感谢@Yunwei.W,按照建议,我在我的嵌套表单中添加了 @pet.breed
(之前是 :breed, Pet.new do ...
:
...
<!-- Nested Breed & Category Attributes -->
<%= f.simple_fields_for :breed, @pet.breed do |breed| %>
...
然后在 pets_controller.rb
:
def create
# Preventing duplicates when breed name mathes exsiting record
breed = Breed.find_by_name(params[:pet][:breed_attributes][:name]) || Breed.create(name: params[:pet][:breed_attributes][:name], category_id: params[:pet][:breed_attributes][:category_id])
@pet = current_user.pets.new(pet_params.except(params[:pet][:breed_attributes][:name]))
@pet.breed = breed
...
end
肯定有比这更好的解决方案,但我很高兴在使用此拦截器 3 天后它终于起作用了。感谢所有为帮助做出贡献的人!真的很感谢!
我现在正在学习Rails,我完全是个新手。希望能得到这里Rails专家的帮助。我试图通过 Whosebug 进行搜索,但未能找到解决方案。
所以有一个 Pet
模型有 Breed
模型作为参考,它参考了 Category
模型,所以 3 个模型:
class Pet < ApplicationRecord
belongs_to :breed
accepts_nested_attributes_for :breed
has_one :category, through: :breed
class Breed < ApplicationRecord
belongs_to :category
has_many :pets, dependent: :destroy
accepts_nested_attributes_for :category
class Category < ApplicationRecord
has_many :breeds, dependent: :destroy
has_many :pets, through: :breed
我已经使用 simple_form
设置了表格。创建新宠物时,用户可以 select
与 Breed
关联的现有 Category
选项。然后在表单中,将 breed.name
属性设置为用户可以输入自己的 text_field
。请在下面的嵌套属性部分:
...
<!-- Nested Breed & Category Attributes -->
<%= f.simple_fields_for :breed, Breed.new do |breed| %>
<%= breed.error_notification %>
<%= breed.error_notification message: breed.object.errors[:base].to_sentence if breed.object.errors[:base].present? %>
<div class="form-row">
<div class="form-group col-md-4">
<%= breed.association :category, collection: Category.order(:id),
label: "Animal Category", required: true %>
</div>
<div class="form-group col-md-8">
<%= breed.input :name, required: true,
label: "Breed", placeholder: "Domestic short hair",
hint: "Type 'unknown', if unsure", class: "form-control" %>
</div>
</div>
<% end %>
所以我的问题是,如何使用现有 breed_id
记录保存新宠物而不用不同的 breed_id
复制它?
所以目前,我已经有一个 breed_id : 1
的现有记录,它由 category_id: 1 (for Cat), and name: 'domestic short hair'
组成 – 如果我在表格中创建一个具有这些相同值的新宠物,它将复制记录,它应该保存 breed_id: 1
,但相反,新宠物保存在 breed_id: 15
...
当我查看 console
时:
irb(main):001:0> Breed.find_by_name("domestic short hair")
Breed Load (6.8ms) SELECT "breeds".* FROM "breeds" WHERE "breeds"."name" = LIMIT [["name", "domestic short hair"], ["LIMIT", 1]]
=> #<Breed id: 1, name: "domestic short hair", category_id: 1, created_at: "2021-08-14 13:12:41.266154000 +0000", updated_at: "2021-08-14 13:12:41.266154000 +0000">
irb(main):002:0> Breed.last
Breed Load (8.2ms) SELECT "breeds".* FROM "breeds" ORDER BY "breeds"."id" DESC LIMIT [["LIMIT", 1]]
=> #<Breed id: 15, name: "domestic short hair", category_id: 1, created_at: "2021-08-16 04:08:31.602249000 +0000", updated_at: "2021-08-16 04:08:31.602249000 +0000">
更新我的 controller / model / form helper
的最佳方法是什么(我不确定是哪一种),这样可以防止重复?我目前没有 breeds_controller
,只使用 pets_controller
,这是脚手架中的一个基本的:
pets_controller.rb
def show
end
# GET /pets/new
def new
@pet = Pet.new
@pet.build_breed
end
# GET /pets/1/edit
def edit
end
# POST /pets or /pets.json
def create
@pet = current_user.pets.new(pet_params)
respond_to do |format|
if @pet.save
format.html { redirect_to @pet, notice: "Pet was successfully added." }
format.json { render :show, status: :created, location: @pet }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @pet.errors, status: :unprocessable_entity }
end
end
end
...
private
def pet_params
params.require(:pet).permit(:owner_id, :name, :dob, :gender, :bio, :instagram,
breed_attributes: [:name, :category_id])
end
我试过在模型breed.db
中写一个before_save :existing_breed_record
,我认为这是错误的...
class Breed < ApplicationRecord
belongs_to :category
has_many :pets, dependent: :destroy
accepts_nested_attributes_for :category
before_save :existing_breed_record
private
def existing_breed_record
if self.find_by(category_id, name).exists?
return self.id
end
end
这是我的 schema.db
create_table "breeds", force: :cascade do |t|
t.string "name"
t.bigint "category_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["category_id"], name: "index_breeds_on_category_id"
end
create_table "categories", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "pets", force: :cascade do |t|
t.bigint "owner_id", null: false
t.bigint "breed_id", null: false
t.string "name"
t.date "dob"
t.integer "gender"
t.string "bio"
t.string "instagram"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["breed_id"], name: "index_pets_on_breed_id"
t.index ["owner_id"], name: "index_pets_on_owner_id"
end
我从指南中了解到,我认为有 find_or_create_by
方法是我需要的,但我不确定在哪里以及如何实施它...
对于数据库和Rails专家来说,为了学习,你建议我下次如何做更好?我是否应该将 breed
作为 Pet 模型的字符串 attribute/column 并直接将 Category
模型关联到 Pet?
如果您想使用嵌套形式,可以将 before_save
方法添加到 Pet
\pet.rb
before_save :check_existing_breed
def check_existing_breed
if self.breed&.name and breed = Breed.find_by(name: self.breed&.name)
self.breed = breed
end
end
在老师的帮助下,我们终于找到了解决办法。它有点乱,但它不再重复任何现有记录!
首先,感谢@Yunwei.W,按照建议,我在我的嵌套表单中添加了 @pet.breed
(之前是 :breed, Pet.new do ...
:
...
<!-- Nested Breed & Category Attributes -->
<%= f.simple_fields_for :breed, @pet.breed do |breed| %>
...
然后在 pets_controller.rb
:
def create
# Preventing duplicates when breed name mathes exsiting record
breed = Breed.find_by_name(params[:pet][:breed_attributes][:name]) || Breed.create(name: params[:pet][:breed_attributes][:name], category_id: params[:pet][:breed_attributes][:category_id])
@pet = current_user.pets.new(pet_params.except(params[:pet][:breed_attributes][:name]))
@pet.breed = breed
...
end
肯定有比这更好的解决方案,但我很高兴在使用此拦截器 3 天后它终于起作用了。感谢所有为帮助做出贡献的人!真的很感谢!