get ids as well for all models with seed_dump gem - mysql

Hi I am using seed_dump gem to create seeds.rb from existing data but I stuck one thing I want to get ids for all models as well how can I do this for example currently if I run
rake db:seed:dump
I just get code like this
Product.create(title: "title", description: "text")
but I want this
Product.create(id: 1, title: "title", description: "text")
how can i do that?

rake db:seed:dump EXCLUDE=[]
This overwrites the default exclude of [:id, :created_at, :updated_at] so that it includes the id

Create your own export. Assuming that your model's name is Country:
lib/tasks/export.rake
namespace :export do
desc "Exports data for using in a seeds.rb."
task :seeds_format => :environment do
Country.order(:id).all.each do |country|
puts "Country.create(#{country.serializable_hash.
delete_if {|key, value| ['created_at','updated_at'].
include?(key)}.to_s.gsub(/[{}]/,'')})"
end
end
end
You could run that with this command:
rake export:seeds_format > db/seeds.rb

Related

How to seed join table data from yaml file through seed.rb in Rails

I am trying to add to my seed.rb the ability to load data onto a join table for my ROR 3.2.5 application.
I have two models: Saft.rb and Keyword.rb, which I would like to join.
class Saft < ActiveRecord::Base
has_and_belongs_to_many :keyword, :join_table => "safts_keywords"
end
class Keyword < ActiveRecord::Base
attr_accessible :word
has_and_belongs_to_many :saft, :join_table => "safts_keywords"
end
I seed datasets for both from a yaml file, such as from: keywords_list.yml
---
- word: "12"
- word: "34"
The corresponding part of my Seed.rb:
keywords_data = YAML.load_file(Rails.root.join('db/seeds/keywords_list.yml'))
keywords_data.each do |keyword|
h = Keyword.find_or_initialize_by_word(keyword['word'])
h.save
end
Now I would like to seed the initial dataset for the join table from a yaml file too. (safts_keywords.yml)
---
- saft_id: 1
keyword_id: 2
When I try to load the data through:
# Load the Join Table
safts_keywords_data = YAML.load_file(Rails.root.join('db/seeds/safts_keywords_list.yml'))
safts_keywords_data.each do |saftkeyword|
h = SaftKeyword.find_or_initialize_by_saft_id(saftkeyword['saft_id'],
:keyword_id => saftkeyword['keyword_id'])
h.save
end
I get:
...** Invoke db:structure:load_if_sql (first_time)
** Invoke db:create
** Execute db:structure:load_if_sql
** Invoke db:seed (first_time)
** Execute db:seed
** Invoke db:abort_if_pending_migrations (first_time)
** Invoke environment
** Execute db:abort_if_pending_migrations
rake aborted!
uninitialized constant SaftKeyword
/Users/Stephan/Development/REPRO/APP/db/seeds.rb:99
/Users/Stephan/Development/REPRO/APP/db/seeds.rb:97:in `each'
/Users/Stephan/Development/REPRO/APP/db/seeds.rb:97
...
How can I get this to work?
Edited:
I created the join table through a migration. It is named "safts_keywords", but Saft_Keyword does not work either in the seed.rb.
HABTM join tables do not usually have a model associated with them as they are simply join tables and nothing else.
That means these relationships would be established from one side or the other. Given my assumption that a Saft is more likely to have multiple Keywords I would go about this as follows.
I have no idea what a Saft is but if you know their ids I would recommend the following:
1:
- keyword1
- keyword2
- keyword3
2:
- keyword2
- keyword4
Then your seed file as:
safts_keywords_data = YAML.load(Rails.root.join('db/seeds/safts_keywords_list.yml'))
safts_keywords_data.each do |saft_id,keywords|
saft = Saft.find(saft_id)
saft.keywords << keywords.map do |key|
Keyword.find_or_create_by(word: key)
end
end
If there is a clearer way to locate a unique Saft I would recommend that just from a readability standpoint but this should give you a clearer picture.

Why does activerecord not populate an auto-incrementing column in the item returned from a create?

Why does rails not populate an auto-incrementing column in the item returned from a create? Is there a better way to do this?
In rails, when you do a = Foo.create then a.id is populated
But if you have a field that was created via
def up
execute "ALTER TABLE my_table ADD COLUMN my_auto_incrementing_column INTEGER AUTO_INCREMENT not null UNIQUE KEY;"
end
Then that field does not appear when you use create. You have to use a reload also.
a = Foo.create
a.id # not nil
a.my_auto_incrementing_column # nil
a.reload
a.my_auto_incrementing_column # is now populated
Version information:
$ ruby -v
ruby 1.9.3p484 (2013-11-22 revision 43786) [x86_64-darwin14.5.0]
$ bundle exec rails -v
Rails 3.2.12
$ mysql --version
mysql Ver 14.14 Distrib 5.6.26, for osx10.10 (x86_64) using EditLine wrapper
Some background:
This code is being applied to a large existing in-production rails codebase that requires that all id fields be UUIDs. The auto_increment column is not a primary key, because it was added after we had discovered that a new external integration partner could not handle using our existing long unique identifiers (UUIDs).
We are working hard to update our version of ruby but we don't want to wait for that as a solution to this problem. Also, after reading changelogs in activerecord, I still don't have proof that any future version of ruby/rails will contain a bugfix for this problem.
The code which I want to improve:
class Foo < ActiveRecord::Base
has_one :object_containing_auto_incrementing_column
def my_method
if self.object_containing_auto_incrementing_column.nil?
self.object_containing_auto_incrementing_column = ObjectContainingAutoIncrementingColumn.create(owner: self)
self.object_containing_auto_incrementing_column.reload
end
self.object_containing_auto_incrementing_column.my_auto_incrementing_column
end
end
After looking at the source code it does not appear that ActiveRecord tries to populate auto-incrementing columns. It only assigns the value that is returned by the INSERT statement to the #id attribute and nothing else.
# ActiveRecord:: Persistence::ClassMethods
def create
# ...
self.id ||= new_id if self.class.primary_key
# ...
end
If you want to populate my_auto_incrementing_column without hitting the DB twice, I think there is no way around patching ActiveRecord itself.
Have a look at how the insert method is implemented:
# Returns the last auto-generated ID from the affected table.
#
# +id_value+ will be returned unless the value is nil, in
# which case the database will attempt to calculate the last inserted
# id and return that value.
#
# If the next id was calculated in advance (as in Oracle), it should be
# passed in as +id_value+.
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
value = exec_insert(sql, name, binds)
id_value || last_inserted_id(value)
end
There might not be any trivial way to change the current API to populate your field in question.
Apparently it is still not possible to get in create() a field (not id) generated in autoincrement at database level by SEQUENCE.
I worked around the problem
in my case: PostgreSQL AUTOINCREMENT by SEQUENCE
with an after_create callback
Migration
class CreateFoo < ActiveRecord::Migration[7.0]
def change
create_table :foo do |t|
t.integer :autoinc_field
end
execute "CREATE SEQUENCE table_name_seq OWNED BY table_name.autoinc_field INCREMENT BY 1 START WITH 100000"
execute "ALTER TABLE table_name ALTER COLUMN autoinc_field SET DEFAULT nextval('table_name_seq');"
end
end
Model
class Foo < ApplicationRecord
after_create :reload
end
Result
>> Foo.create!.autoinc_field
=> 100000

Rails4, MySql, mysql2 encoding issue

I have a project where Rails 4, MySql 5.1, gem mysql2 are used.
This stack worked fine until today. I've added two fields in a table (added corresponding migration) and if I save data to the table, the data in this field will look ugly, like -%D0%9D%D0%BE%D0%B4%D0%B0%D1%80%D0%B8
Here is Rails log:
This is the data from a request. All looks fine, 'name' and 'comment' are in Russian.
Parameters: {"{\"user_id\":\"1\",\"name\":\"АБВ\",\"email\":\"mike#outofcloud.ru\",\"answer\":\"5\",\"comment\":\"цувцув\"}"=>nil}
But in the SQL statement the 'name' parameter changed:
SQL (23.3ms) INSERT INTO "feedbacks" ("answer", "code", "comment", "created_at", "email", "name", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING "id" [["answer", 5], ["code", 1], ["comment", "цувцув"], ["created_at", "2015-05-13 08:54:22.047321"], ["email", "example#example.ru"], ["name", **"%D0%90%D0%91%D0%92"**], ["updated_at", "2015-05-13 08:54:22.047321"], ["user_id", "1"]]
'comment' field was added before my migrations, it works as expected. It look great in the DB and in the app.
'name' field have been added recently and its doesn't work as expected.
Can someone help with the issue?
The string that you're showing is the result of using URI.encode on "АБВ" - see:
[4] pry(main)> URI.encode "АБВ"
=> "%D0%90%D0%91%D0%92"
Rails doesn't do this by itself. You must have some code somewhere else (or, hopefully not, a gem) that's performing that transformation.
If you don't know where that is then please add the code from your controllers showing any before_action methods, or other transformations. Also the code from your model if you're doing anything before_save or any custom attribute setters, eg. def name=

Can rspec change and use an innodb fulltext index in the same test?

I have an oddly specific problem. Let's say I have this table in a Rails project:
create_table "documents", force: true do |t|
t.text "tags"
end
add_index "documents", ["tags"], name: "index_documents_on_tags", type: :fulltext
I have an integration test that creates a few Document instances with varying tag combinations, which the method I'm trying to test should return by way of a fulltext search. Unfortunately, it turns out that InnoDB doesn't rebuild its fulltext indices until the current transaction ends, meaning that my search comes up empty.
If I build the test data in fixtures (e.g. in advance, outside of the transaction that rspec uses for each test) it all works fine, but is there any way for me to tweak the data and run a search against it within the same test?
Tricky but fixable. Bear with me.
Step 1
Add this wonderful helper by #mattias (https://stackoverflow.com/a/7703220/537648)
def without_transactional_fixtures(&block)
self.use_transactional_fixtures = false
before(:all) do
DatabaseCleaner.strategy = :truncation
end
yield
after(:all) do
DatabaseCleaner.strategy = :transaction
end
end
Step 2
Add this before block to your rspec examples
Sample usage:
describe "doing my thing" do
before do
# This will rebuild the indexes. You need it before each example
ActiveRecord::Base.connection.execute("ANALYZE TABLE `searchables`")
end
without_transactional_fixtures do
it "does something without transaction fixtures" do
...
end
end
end
Bonus Step
If you are getting this error:
ActiveRecord::StatementInvalid: Mysql2::Error: SAVEPOINT active_record_1 does not exist: ROLLBACK TO SAVEPOINT active_record_1
Be careful when using FactoryBot/FactoryGirl. Use let! instead of let if you need to create objects to the searchable table.
Example:
describe '.search' do
without_transactional_fixtures do
let! (:campaign0) { create(:campaign, io_number: 'C0-1234-4321', status: 'completed') }
let! (:campaign1) { create(:campaign, io_number: "C1-4321-4321") }
before do
ActiveRecord::Base.connection.execute("ANALYZE TABLE `searchables`")
end
...
Thank you #awaage (https://stackoverflow.com/a/13732210/537648)
I had the same question and didn't find a very good solution. One thing you can do is use a tool like DatabaseCleaner and change your strategy for those tests from "transaction" to "truncation".
I had the same issue, and resolved it by manually create the fulltext index in the test.
Example: for your case
create_table "documents", force: true do |t|
t.text "tags"
end
add_index "documents", ["tags"], name: "index_documents_on_tags", type: :fulltext
in your test:
before do
#index_key = "index_documents_on_tags_#{Time.current.to_i}"
ActiveRecord::Base.connection.execute("CREATE FULLTEXT INDEX #{#index_key} ON documents(tags)")
end
after do
ActiveRecord::Base.connection.execute("ALTER TABLE documents DROP INDEX #{#index_key}")
end

Ruby On Rails: Testing deletes tables

I'm creating an application in RoR and I'm implementing unit testing in all my models.
When I run every test on his own (by running ruby test/unit/some_test.rb) all tests are successful.
But when I run all tests together (by running rake test:units) some tables from both databases (development e test) are deleted.
I'm using raw SQL (mysql) do create tables because I need composite primary keys and physical constraints so I figured it would be the best. Maybe this be the cause?
All my tests are in this form:
require File.dirname(FILE) + '/../test_helper'
require File.dirname(FILE) + '/../../app/models/order'
class OrderTestCase < Test::Unit::TestCase
def setup
#order = Order.new(
:user_id => 1,
:total => 10.23,
:date => Date.today,
:status => 'processing',
:date_concluded => Date.today,
:user_address_user_id => 3,
:user_address_address_id => 5,
:creation_date => Date.today,
:update_date => Date.today
)
end
################ Happy Path
def test_happy_path
assert #order.valid?, #order.errors.full_messages
end
...
The errors I get when running the tests are something like this:
3) Error:
test_empty_is_primary(AddressTestCase):
ActiveRecord::StatementInvalid: Mysql::Error: Table 'shopshop_enterprise_test.addresses' doesn't exist: SHOW FIELDS FROM addresses
/test/unit/address_test.rb:9:in new'
/test/unit/address_test.rb:9:insetup'
Any guesses?
Thanks!
PS: When using postgres as the database engine, everything works fine with rake test:units! (of course, with the correct changes so the sql statements can work with postgres)