2014-07-26 2 views
1

각 사용자가 하나 이상의 전자 메일을 많이 가질 수있는 시스템을 모델링하려고합니다.두 모델을 동시에 생성하십시오.

create_table :users do |t| 
end 

create_table :user_emails do |t| 
    t.integer :user_id, null: false 
    t.string :email, null: false 
end 

add_index :user_emails, :email, :unique => true 
add_foreign_key :user_emails, :users, dependent: :delete 

다음과 같은 레일 모델 :

class User < ActiveRecord::Base 
    has_many :emails, class_name: 'UserEmail', dependent: :destroy 

    def self.find_by_email(email) 
    UserEmail.find_by(email: email).try(:user) 
    end 

    validate do 
    if emails.count < 1 
     errors.add(:emails, "is empty") 
    end 
    end 
end 

class UserEmail < ActiveRecord::Base 
    belongs_to :user 

    validates_presence_of :user_id, :email 
    validates_uniqueness_of :email 
end 

지금은 그 중 하나를 만들 수 아니다

에 따라 좋은 정규화 규칙은 내가 두 마이그레이션 (간결함을 제거 일부 필드를) 생성 . 적어도 UserEmail이 필요하므로 사용자를 만들 수 없습니다. 동시에 UserEmail이 필요하기 때문에 UserEmail을 미리 만들 수 없습니다.

나는 내가 생각할 수있는 @user.emails.build@user.emails << e의 조합을 시도했다고 생각합니다.

어떻게하면 데이터 일관성을 잃지 않고이 간단한 문제를 해결할 수 있습니까? (하나는 저장되고 다른 하나는 저장되지 않습니다)?

P.s .: 유효성 검사를 완화하고 트랜잭션을 사용하면 문제를 일관되게 해결할 수 있습니다. 그러나 나는 레일에서 거래를 한 번도 해본 적이 없기 때문에 어떤 도움도 정말로 감사 할 만하다.

감사

+0

@ user_email.build_user을 시도 해본 적이 있습니까 – Nishu

+0

비어있는 user_id에서 유효성 검사 오류가 여전히보고됩니다. –

답변

2

validates_presence_of : user_id 유효한 사용자가 아닐 수있는 유효한 정수 만 확인하십시오.

그러나 validates_presence_of :uservalidates_presence_of :emails을 사용하면 사용자와 이메일의 연결이 유효하고 비어 있지 않은지 확인합니다.

또한 사용자 및 관련 전자 메일을 만들 때 응용 프로그램 수준에서 다음 코드를 수행 할 수 있습니다.

user = User.new(name: 'user name') 
user.emails.build(email_address: 'email address') #build(email: '') for your case. 

그런 다음 아래

user.save! 

으로 데이터베이스에 내가 테스트 코드입니다 저장할 수 있습니다.

class User < ActiveRecord::Base 
    has_many :emails, class_name: 'UserEmail', dependent: :destroy 
    validates_presence_of :emails, :message => 'User should have at least one email address.' 
end 

class UserEmail < ActiveRecord::Base 
    belongs_to :user 
    validates_presence_of :user 
    validates_presence_of :email_address 
end 


[40] pry(main)> u = User.create!(name: 'doh') 
    (0.1ms) begin transaction 
    (0.0ms) rollback transaction 
ActiveRecord::RecordInvalid: Validation failed: Emails User should have at least one email address. 

[1] pry(main)> u = User.new(name: 'nice') 
=> #<User id: nil, name: "nice", created_at: nil, updated_at: nil> 
[2] pry(main)> u.emails.build(email_address: '[email protected]') 
=> #<UserEmail id: nil, user_id: nil, email_address: "[email protected]", created_at: nil, updated_at: nil> 
[3] pry(main)> u.save! 
    (0.1ms) begin transaction 
    SQL (0.5ms) INSERT INTO "users" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", "2014-07-26 10:14:52.184245"], ["name", "nice"], ["updated_at", "2014-07-26 10:14:52.184245"]] 
    SQL (0.2ms) INSERT INTO "user_emails" ("created_at", "email_address", "updated_at", "user_id") VALUES (?, ?, ?, ?) [["created_at", "2014-07-26 10:14:52.189757"], ["email_address", "[email protected]"], ["updated_at", "2014-07-26 10:14:52.189757"], ["user_id", 5]] 
    (0.7ms) commit transaction 
=> true 
[4] pry(main)> u.emails.count 
    (0.2ms) SELECT COUNT(*) FROM "user_emails" WHERE "user_emails"."user_id" = ? [["user_id", 5]] 
=> 1 
[5] pry(main)> u.id 
=> 5 
[6] pry(main)> u.name 
=> "nice" 
[7] pry(main)> u.emails 
=> [#<UserEmail id: 4, user_id: 5, email_address: "[email protected]", created_at: "2014-07-26 10:14:52", updated_at: "2014-07-26 10:14:52">] 
[8] pry(main)> u 
=> #<User id: 5, name: "nice", created_at: "2014-07-26 10:14:52", updated_at: "2014-07-26 10:14:52"> 


[19] pry(main)> UserEmail.create!(email_address: '[email protected]') 
    (0.1ms) begin transaction 
    (0.1ms) rollback transaction 
ActiveRecord::RecordInvalid: Validation failed: User can't be blank 

희망이 있습니다.

0

난 그냥 저장 층에 NOT NULL 제약 조건을 추가하는 것을 깨달았다는 충분하다. user_id 존재 확인을 제거하고 동일한 일관성 동작을 사용할 수 있습니다. 예외는 이제 작동합니다.

그러나 이것은 레일 포로 (Trail POW)에서 꽤 해킹 된 것처럼 보입니다. 더 나은 해결책은 여전히 ​​정말로 고맙습니다 ...

관련 문제