数据库规范化因first_or_create或错误分配而失败

时间:2012-10-01 09:01:18

标签: ruby-on-rails activerecord

这个问题显然是微不足道的,但我是Rails的新手,我无法弄清楚我哪里弄错了。我正在从旧电子表格填充相关模型。以下是rake导入任务代码段。不知何故,当位置(华盛顿)相同并且已经存在于Smith的帖子的数据库中时,为第二条记录post.locations << location做出的新分配将删除与Jones的帖子的位置关联。我的想法是将同一地点与两个人的帖子联系起来。我错过了什么?

import.rake

data = [
  { name: 'Jones',
    post: 'President',
    city: 'Washington'
  },
  { name: 'Smith',
    post: 'Vice-President',
    city: 'Washington'
  },
  { name: 'Peters',
    post: 'Janitor',
    city: 'New York'
  }
]

data.each do |row|
  name = row[:name]; post = row[:post]; city = row[:city]
  person = Person.where(name: name).first_or_create
  post = Post.where(post: post).first_or_create
  location = Location.where(city: city).first_or_create
  post.people << person
  post.locations << location
  location.save; person.save; post.save
end

上面的导入结果为

person1 = Person.find_by_name("Jones");
person1.posts.first.locations.first == nil
person2 = Person.find_by_name("Smith");
person2.posts.first.locations.first.city == "Washington"
person3 = Person.find_by_name("Peters");
person3.posts.first.locations.first.city == "New York"

location.rb

class Location < ActiveRecord::Base
  belongs_to :post
  attr_accessible :city
end

person.rb

class Person < ActiveRecord::Base
  attr_accessible :name
  has_many :occupations
  has_many :posts, through: :occupations
end

post.rb

class Post < ActiveRecord::Base
  attr_accessible :post
  has_many :occupations
  has_many :people, through: :occupations
  has_many :locations
end

occupation.rb

class Occupation < ActiveRecord::Base
  belongs_to :person
  belongs_to :post
  attr_accessible :person_id, :post_id, :since, :till
end

1 个答案:

答案 0 :(得分:1)

好的,如果我正确理解你的模型,那么我认为这就是问题...

Location(很多)和Post(一)之间存在一对多关联。当你说post.locations << location时,Rails正在做的是找到location并更新其post_id,因此会覆盖之前的所有内容。您可能想要的是多对多,因此帖子可以有多个位置,反之亦然。

有两种方法可以设置:has_and_belongs_to_manyhas_many :through。后者通常更好,因为它更灵活,但现在我只会展示更简单的方法:

class Post
  has_and_belongs_to_many :locations
end

class Location
  has_and_belongs_to_many :posts
end

create_table :posts_locations, :id => false do |t|
  t.integer :post_id
  t.integer :location_id
end

当您执行post.locations << locationlocation.posts << post之类的操作时,联接表会自动更新,并将帖子与位置相关联。