【发布时间】:2021-10-18 04:51:28
【问题描述】:
我现在正在学习 Rails,我是个新手。我希望能够在这里得到 Rails 专家的帮助。我尝试通过 StackOverflow 进行搜索,但未能找到解决方案。
所以有一个 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" = $1 LIMIT $2 [["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 $1 [["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,这是Scaffolding 的一个基本的:
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 模型,并直接将 Category 模型与 Pet 关联?
【问题讨论】:
-
您是否已经尝试使用
first_or_initialize方法?听起来很合适。 -
感谢您的回复@SebastianPlasschaert 我还没有也不确定该方法,我该如何实现它?
-
意见:
accepts_nested_attributes_for并不是所有用例的最佳解决方案,如果您正在执行多个级别或需要大量逻辑,则更好的选择最有可能为品种创建单独的控制器和类别,并使用 AJAX 动态创建嵌套记录。这使您可以应用单独的访问控制,因为您可能不信任所有用户来创建品种或类别,并且它可以让您获得更好的用户体验。例如,您可以在用户输入时使用自动完成功能来查找品种,这样会更有帮助,并且可以避免重复数据。 -
然而这是一个相当高级的话题,你可能想回避这个功能,直到你有更多的 Rails 经验。
-
非常感谢 Max 的建议,这正是我想要的,但是对于我正在做的这个项目,我还没有做到这一点。我实际上是在考虑放弃
breed model,只使用category作为一般动物类型,暂时不添加太多细节......
标签: ruby-on-rails ruby postgresql simple-form ruby-on-rails-6