How to truncate a join table in rails? - ruby-on-rails

To truncate an ActiveRecord table, I can do
Category.destroy_all
or
Post.destroy_all
How does one go about truncating a categories_post table?

For a true TRUNCATE, you can use execute to run the raw SQL.
ActiveRecord::Base.connection.execute("TRUNCATE TABLE #{table_name}")
Your examples using models weren't doing true TRUNCATE queries.
destroy_all does not TRUNCATE a table. It "destroys the records matching conditions by instantiating each record and calling its destroy method" (link).
delete_all is closer - it ignores callbacks - but still not a TRUNCATE.
Using the execute method deletes the rows from the database without creating any model instances.
Also, an actual TRUNCATE query, at least in MySQL, will reset the auto-increment on the primary key, so that the next record you insert will have id of 1.

I guess your join table is called categories_posts. CategoryPost.destroy_all should work, if not, maybe you need to specify the table name in the model (CategoryPost)
set_table_name "categories_posts"
Update, there isn't a CategoryPost model, so it should be created:
class CategoryPost < ActiveRecord::Base
set_table_name "categories_posts"
end

Related

Can you create a database backed rails model without a table?

I have a complicated SQL query that aggregates columns using a group_by.
IndividualOrder.select(SUM(...) as amount, other_fields).group_by("organization.id")
The problem is: I get an ActiveRecord::Relation from IndividualOrder, which isn't really what the result is conceptually anymore. I'm not entirely sure how to cast it to a new method. If I use arel to handle it, I still usually would have to go IndividualOrder.arel_table, and it would still cast to whatever I select.
I just want to take the fields [:amount, :organization, :other] and be able to interact with them as I would a database backed model that had those values as a table.
So, it's not quite a tableless model (which usually aren't database backed), and it's not an actual model because it's a generated query.
Is this the use case for scenic? I'm stuck with having to navigate around 2 variables that exist within the query that I'm doing via ActiveRecord.
Hi there hope this answer helps you, this should be a comment not a answer( not enough karma for doing that yet)
what you can do is to create a view in your database,
rails g migration my_new_view
ActiveRecord does not give us any methods to create views so you will have to do "manually" or you can use specific gems for doing those.
class MyNewView < ActiveRecord::Migration[5.2]
def change
reversible do |dir|
dir.up do
execute <<-SQL
CREATE OR REPLACE VIEW public.my_new_view AS
# your sql query here SELECT ....
SQL
end
dir.down do
execute <<-SQL
DROP VIEW IF EXISTS public.my_new_view;
SQL
end
end
end
end
now you can refer in your model.
Beware view tables will not show in schema by default, for seing them you have to put in your application.rb:
config.active_record.schema_format = :sql
cheers.

Why ActiveRecord::Store.store_acessor doesn't reflect on the resulting SQL when querying

Consider this class:
class UsersMigration < ActiveRecord::Migration[5.2]
def change
create_table(:users) do |t|
t.hstore :settings
end
end
end
class User < ActiveRecord::Base
store_accessor :settings, :privileges, :colors
end
When doing this Query:
User.where(privileges: 'Admin')
It results in this error:
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column users.privileges does not exist)
and is right because the column is actually settings
In order to make the query we have to do this:
User.where("settings->'privileges' = 'Admin'")
If the model already has explicit serialization to an hstore column why the first query doesn't result in the following SQL:
SELECT "user".* FROM "users" WHERE (settings->'privileges' = 'Admin')
ActiveRecord is not built to actually care about the contents of hstore, json or array columns. AR is built around the relational model with tables and columns. Columns on the table correspond to attributes in the model.
Hstore, json and array types are relatively new database features and not definitely not polyglot. The implementation varies per database. In ActiveRecord the support for these columns is actually provided by the database driver and not the framework itself.
store_accessor just creates a getter/setter and sets up dirty tracking for the attribute in the model. It does not "explicitly tell" AR that thats something in a hstore column - remember AR gives zero fawks about the contents of a hstore column.
In fact I would say that if your are using it this way its a very telling sign that you have fallen victim to the JSON/hstore anti-pattern.

Delete all records from all tables in database using seeds.rb

Right now my approach is to list every table one at a time and call .delete_all on it. Which is repetitive:
Example:
#app/db/seeds.rb
Blog.delete_all
Person.delete_all
Post.delete_all
User.delete_all
Author.delete_all
Book.delete_all
# ... on and on for all the tables
And then of course run rake db:seed which would clear out all the records for those above tables.
Is there a command that does exactly what I want:
deletes all the records from all the tables without deleting the tables themselves?
Or, is there a way to iterate through all my tables and .delete_all on each table?
Is there a command that does exactly what I want: deletes all the
records from all the tables?
bundle exec rake db:reset
This is functionally equivalent to rake db:drop db:setup.
Don't want delete the tables?
#app/db/seeds.rb
[Blog, Person, Post, User, Author, Book].each do |table|
ActiveRecord::Base.connection.execute("TRUNCATE #{table.table_name}")
end
SQL-TRUNCATE
As the OP has asked to delete all the record in all the tables, and not all the tables. So you can get all the tables by: ActiveRecord::Base.connection.tables, it will give you all you tables in the database.
puts ActiveRecord::Base.connection.tables
ActiveRecord::Base.connection.tables.each do |table|
next if table.match(/\Aschema_migrations\Z/)
klass = table.singularize.camelize.constantize
klass.delete_all
end
Edit:
If you do want id to start again from 1, when you create a new instance after emptying the table, you have to destroy the table, and re-create it.
ActiveRecord::Migration.drop_table(:users)
ActiveRecord::Migration.create_table(:users)
And now if you create a new instance of model User, it will start generating ids right from 1. Please note that you need to send the name of the table in drop_table, and create_table functions, while the code that I've written above gives you the name of the class, in this case User. You can get the table name if you have the name of the model:
User.table_name # it will give you "users"
# in above code, you can do:
ActiveRecord::Migration.drop_table(klass.table_name)
# string and symbol: both type of arguments work here!
If you really want to write ruby code to do this, try using ObjectSpace to get every instance of a Class, then select classes which inherit from ActiveRecord::Base
models = ObjectSpace.each_object(Class).select { |klass| klass < ActiveRecord::Base }
models.each{|m|m.delete_all}
However, the answers suggesting different rake tasks, instead of including this in seeds.rb are probably a better solution to your problem.
If you force rails to load all your models you can then iterate over them
Rails.application.eager_load!
# This only gets direct subclasses, since we are just deleting
# No reason to get their subclasses
models = ActiveRecord::Base.subclasses
models.each(&:delete_all)
I added those lines to my seeds.rb file. With this code, you don't have to bother with referencing manually your models and/or with foreign key constraints (thanks to disable_referential_integrity).
Rails.application.eager_load!
ActiveRecord::Base.connection.disable_referential_integrity do
ApplicationRecord.descendants.each do |model|
model.delete_all
end
end
NB : ApplicationRecord.descendants returns only true application models unlike ActiveRecord::Base.descendants (no more ApplicationRecord, schema_migrations and ar_internal_metadata).

Rails Migration Column Added But Not Populated

I'm trying to write a very simple Rails migration that adds a new column to the table and populates it with a combination of data from other columns. However, the column gets added but the column value is nil for every record. What am I doing wrong?
class AddNameToPermissions < ActiveRecord::Migration
def change
add_column :auth_permissions, :name, :string
Auth::Permission.reset_column_information
Auth::Permission.all.each do |permission|
target_name = permission.target_symbol || permission.target_class
permission.name = permission.action << ", " << target_name
permission.save
end
end
end
Calling permission.save will execute all the callbacks including validators and maybe a validator is preventing the record to be saved. To skip validators you can use update_column instead of save
permission.update_column(:name, permission.action << ", " << target_name)
Also I would recommend you a couple of tips:
Use Auth::Permission.find_each instead of Auth::Permission.all.each (this loads all the records at once in memory)
Try to update the permission in a rake task and use only the migration to modify the table (it is the recommended good practice way)
Unless you are using the default option in a migration, it is usually advisable to do that in a custom rake task. Content Migrations are a bit of an anti pattern. The main Logic you want there is anything that has to deal with the schema of your database. Run your migration with only the add_column line, and then write a rake task to transfer the values from one table to the other.

Correct way to create Rails-4-expected table and column names for HABTM with migration?

I need a HABTM join table for item_ones and item_twos, but I can't seem to get the migration results to match Rails's expectations. If I do this:
create_join_table :item_ones, :item_twos do |t|
...
end
(...which is what rails g migration CreateJoinTableOneTwo item_one item_two generates...)
..then it creates a table named item_ones_item_twos, but Rails will be looking for item_ones_twos (because in 4.0, it factors out common prefixes). If I do this:
create_join_table :item_ones, :twos do |t|
...
end
Then it creates the right table, but the second foreign key is named two_id instead of item_two_id.
So is there any way to get this right other than e.g. manually specifying a :table_name?
Normally, Rails would expect a join table name of <first model>s_<second model>s (eg. assemblies_parts) for any HABTM join tables. However, because ItemOne and ItemTwo have the same prefix (Item), Rails shortens the required table name to item_ones_twos.
You will need to specify the table_name option, as detailed here: http://edgeguides.rubyonrails.org/migrations.html#creating-a-join-table

Resources