2015-04-08 47 views
0

我有一个地方模型has_many类别通过category_tags。 我对标签进行了验证,以确保它具有地点和类别,并避免在一个地方有两次相同的类别。用has_many关系保存对象

型号:

class PlaceCategory < ActiveRecord::Base 

    has_many :place_category_tags, inverse_of: :place_category 
    has_many :places, through: :place_category_tags 

end 

class Place < ActiveRecord::Base 

    has_many :place_category_tags, inverse_of: :place 
    has_many :place_categories, through: :place_category_tags 

end 

class PlaceCategoryTag < ActiveRecord::Base 

    belongs_to :place 
    belongs_to :place_category 

    validates :place_id, presence: true 
    validates :place_category_id, presence: true, uniqueness: {scope: :place_id} 

end 

我也有一个指标我place_category_tags表,以确保在数据库级别的唯一性:

add_index "place_category_tags", ["place_id", "place_category_id"], name: "index_place_category_tags_on_place_id_and_place_category_id", unique: true, using: :btree 

我的问题是,当我保存的标签一个新的地方。验证在保存位置之前在标签上执行,因此它失败,因为place_id为空。

2.1.5 :001 > place = Place.new() 
=> #<Place id: nil, name: "erg", created_at: nil, updated_at: nil> 
2.1.5 :002 > place.place_category_tags.build(place_category_id: 3) 
=> #<PlaceCategoryTag id: nil, place_id: nil, place_category_id: 3, created_at: nil, updated_at: nil> 
2.1.5 :003 > place.save 
    (0.3ms) BEGIN 
    (0.3ms) BEGIN 
    PlaceCategoryTag Exists (0.5ms) SELECT 1 AS one FROM "place_category_tags" WHERE ("place_category_tags"."place_category_id" = 3 AND "place_category_tags"."place_id" IS NULL) LIMIT 1 
    PlaceCategoryTag Exists (0.5ms) SELECT 1 AS one FROM "place_category_tags" WHERE ("place_category_tags"."place_category_id" = 3 AND "place_category_tags"."place_id" IS NULL) LIMIT 1 
    (0.2ms) ROLLBACK 
    (0.2ms) ROLLBACK 
=> false 
2.1.5 :004 > place.errors 
=> #<ActiveModel::Errors:0x0000000384e8a8 @base=#<Place id: nil, created_at: nil, updated_at: nil>, @messages={:"place_category_tags.place_id"=>["can't be blank"]}> 

我搜索了类似的问题,但我发现的是,我不得不添加我的地方的的has_many place_category_tags,这是我做的inverse_of选项,但它并没有解决我的问题。

我也试图在代替place_id和place_category_id地方,place_category执行验证:

validates :place, presence: true 
validates :place_category, presence: true, uniqueness: {scope: :place} 

,似乎工作,但问题是,当我尝试创建唯一性验证不再执行两个相同的place_category_tags,和我在数据库级别的错误:

ActiveRecord::RecordNotUnique (PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "index_place_category_tags_on_place_id_and_place_category_id" 
DETAIL: Key (place_id, place_category_id)=(49, 3) already exists. 
: INSERT INTO "place_category_tags" ("created_at", "place_category_id", "place_id", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"): 
    app/controllers/backend/places_controller.rb:12:in `create' 

是否有人有什么错我的代码的想法?

此外,我发现验证在第二个例子中完全被跳过非常奇怪。如果有人有什么事情的想法,那简直太好了:)

编辑:我使用的Rails 4.1.8和Ruby 2.1.5p273

回答

0

如果您在控制台写,会发生什么:

place = Place.new() 
place.place_category_tags.build(place_category_id: 3) 
place.place_category_tags.first.place 

如果inverse_of工作,它应该返回你的新地方。

在第一个示例中,它看起来像您的place_id是se nil。 在第二个示例中,它似乎是重复的有效数字。

如果您在索引迁移中删除了unique: true标记,尝试使用它也许更容易。

我认为你应该对对象使用验证而不是ID。

0

您可以使用validates_associated辅助方法进行验证。

ActiveRecord在保存到数据库之前验证对象,因此您不必在数据库端再次验证它。

在你的情况下,它会检查一个地方是否有效,然后验证它的标签。但是在创建标签之前,您需要创建地点(要有ID),因此建议使用Place.create而不是Place.new,因为Sharvy Ahmed

0

使用create而不是new将解决您的问题。这将确保在建立关联对象之前,有一个id

place = Place.create 
place.place_category_tags.build(place_category_id: 3) 
place.save 
+0

是的,这工作,因为这个地方已经创建并有一个ID,但对我来说,我呼吁在新的动作新,然后在我的控制器的创建acion创建的地方。标签在表单中作为嵌套属性传递。 –