0

My seeds file populated the countries table with a list of countries. But now it needs to be changed to hard-code the id (instead of rails generating the id column for me).

I added the id column and values as per below:

 zmb: {id: 103,code: 'ZMB', name: Country.human_attribute_name(:zambia, default: 'Error!'), display_order: nil, create_user: user, update_user: user, eff_date: Time.now, exp_date: default_exp_date},
    skn: {id: 104,code: 'SKN', name: Country.human_attribute_name(:st_kitts_and_nevis, default: 'Error!'), display_order: nil, create_user: user, update_user: user, eff_date: Time.now, exp_date: default_exp_date}

countries.each { |key, value| countries_for_later[key] = Country.find_or_initialize_by(id: value[:id]); countries_for_later[key].assign_attributes(value); countries_for_later[key].save!; }

Above it just a snippet. I have added an id: for every country.

But when I run db:seed I get the following error:

ActiveRecord::RecordInvalid: Validation failed: Code has already been taken

I am new to rails so I'm not sure what is causing this - is it because the ID column already exists in the database?

user3437721
  • 2,227
  • 4
  • 31
  • 61
  • Do you have validation on any of your models for code? It would seem you have validates uniqueness of code on one of your models. Probably country ... if you want to get to the errors for objects you can just use `puts country.errors.inspect` – Ryan-Neal Mes Apr 20 '15 at 18:11
  • validates_uniqueness_of :code is in place but I can't find any duplicated – user3437721 Apr 21 '15 at 08:33
  • I would check your seed file again. Note that when you run your seed file you might have data in your db already which would cause the validation to fire. – Ryan-Neal Mes Apr 21 '15 at 09:42
  • Yeah the db is fully populated but I should be able to run db:seed as many time as I want without error? Should it not just ignore anything thats already there? Does the db have to be empty to run a seed? – user3437721 Apr 21 '15 at 09:46
  • No. If you want to do that you would need to use `find_or_create_by`. For example - `Country.find_or_create_by(code: 'ABC', name: 'Country ABC')`. That will make your seed file rerunnable. Right now it sounds like you are just using new and save or create. This will definitely cause the validation to fire. – Ryan-Neal Mes Apr 21 '15 at 09:47
  • I am using Country.find_or_initialize_by - is that ok? – user3437721 Apr 21 '15 at 09:48
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/75789/discussion-between-ryan-neal-mes-and-user3437721). – Ryan-Neal Mes Apr 21 '15 at 10:00

7 Answers7

0

The error is about Code: Code has already been taken. You've a validation which says Code should be uniq. You can delete all Countries and load seeds again.

dx7
  • 804
  • 5
  • 11
0

Run this in the rails console:

Country.delete_all

Then re-run the seed:

rake db:seed
RichardAE
  • 2,945
  • 1
  • 17
  • 20
0

Yes, it is due to duplicate entry. In that case run ModelName.delete_all in your rails console and then run rake db:seed again being in the current project directory. Hope this works.

sansarp
  • 1,446
  • 11
  • 18
0

Add the line for

countries_for_later[key].id = value[:id]

the problem is that you can't set :id => value[:id] to Country.new because id is a special attribute, and is automatically protected from mass-assignment

so it will be:

countries.each { |key, value| 
  countries_for_later[key] = Country.find_or_initialize_by(id: value[:id])
  countries_for_later[key].assign_attributes(value)
  countries_for_later[key].id = value[:id] if countries_for_later[key].new_record?
  countries_for_later[key].save(false)
}
mohamed-ibrahim
  • 10,837
  • 4
  • 39
  • 51
0
ActiveRecord::RecordInvalid: Validation failed: Code has already been taken

is the default error message for the uniqueness validator for :code.

Running rake db:reset will definitely clear and reseed your database. Not sure about the hardcoded ids though.

Check this : Overriding id on create in ActiveRecord

you will have to disable protection with

save(false)

or

Country.create(attributes_for_country, without_protection: true)

I haven't tested this though, be careful with your validators.

Community
  • 1
  • 1
floum
  • 1,149
  • 6
  • 17
0

The ids data that you are using in your seeds file: does that have any meaning outside of Rails? Eg

zmb: {id: 103,code: 'ZMB',

is this some external data for Zambia, where 103 is it's ID in some internationally recognised table of country codes? (in my countries database, Zambia's "numcode" value is 894). If it is, then you should rename it to something else, and let Rails decide what the id field should be.

Generally, mucking about with the value of ID in rails is going to be a pain in the ass for you. I'd recommend not doing it. If you need to do tests on data, then use some other unique field (like 'code') to test whether associations etc have been set up, or whatever you want to do, and let Rails worry about what value to use for ID.

Max Williams
  • 32,435
  • 31
  • 130
  • 197
  • No its just the default id and has no specific meaning. I just want to keep the ids the same – user3437721 Apr 20 '15 at 16:25
  • I'd really recommend not doing that. You will end up spending a lot of time mucking about, since you are going to be fighting rails all the way. It's a risky testing strategy anyway: you could test that a record has a given ID but it could be the wrong "proper" data with a different ID to what you expect. Always better to test the proper data (in this case i'd recommend using the `code` field as the basis for any tests which check which record was returned or whatever). – Max Williams Apr 20 '15 at 16:29
  • The id from the country table is used in another table. So I don't want the country ids to ever change as that would impact the other table. So thought hardcoding them was best, in case someone deleted one from the seeds file.. – user3437721 Apr 20 '15 at 16:34
0

What I think is happening is you have existing data in your database ... let's say

[{id:1 , code: 'ABC'},
{id:2 , code: 'DEF'}]

Now you run your seed file which has {id: 3, 'DEF'} for example.

Because you are using find_or_initialize_by with id you are running into errors. Since you can potentially insert duplicates.

I recon you should just clear your data, but you can try doing find_or_initialize_by using code instead of id. That way you wont ever have a problem of trying to create a duplicate country code.

Country.find_or_initialize_by(code: value[:code])

I think you might run into problems with your ids, but you will have to test that. It's generally bad practice to do what you are doing. Whether they ids change or now should be irrelevant. Your seed file should reference the objects that are being created not ids.

Also make sure you aren't using any default_scopes ... this would affect how find_or_initialize_by works.

Ryan-Neal Mes
  • 6,003
  • 7
  • 52
  • 77