I'm on Rails 4.2 and Postgres 9.5
I was using the following migration and it is not adding the id column automatically:
class AddCharacteristics < ActiveRecord::Migration
def change
create_table :characteristics do |t|
t.string :name
t.timestamps
end
end
end
The resulting table as defined in db/schema.rb does not have an id column:
create_table "characteristics", force: :cascade do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
The id column not being in the schema.rb is a non-issue as pointed out. However I cannot access the characteristic object from the console:
2.3.0 :003 > Characteristic.last
Characteristic Load (0.3ms) SELECT "characteristics".* FROM "characteristics" ORDER BY "characteristics"."id" DESC LIMIT 1
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: relation "characteristics" does not exist
LINE 1: SELECT "characteristics".* FROM "characteristics" ORDER BY...
^
: SELECT "characteristics".* FROM "characteristics" ORDER BY "characteristics"."id" DESC LIMIT 1
In PSQL console this query works fine:
# SELECT "characteristics".* FROM "characteristics" ORDER BY "characteristics"."id" DESC LIMIT 1;
id | name | created_at | updated_at
----+------+----------------------------+----------------------------
1 | test | 2016-01-29 12:58:24.225279 | 2016-01-29 12:58:24.225279
Don't worry, id columns are not shown in schema.rb but they exist in the DB.
For example: https://github.com/everydayrails/rails-4-1-rspec-3-0/blob/master/db/schema.rb
The edited question about my console not working was because my console was in test, but my database query was in development
If you are using RSpec and want to keep the development and test environments in sync, you might want to add ActiveRecord::Migration.maintain_test_schema! in rails_helper.rb.
References:
https://relishapp.com/rspec/rspec-rails/docs/upgrade#pending-migration-checks
https://github.com/everydayrails/rails-4-1-rspec-3-0/blob/master/spec/rails_helper.rb#L16
Related
Using Ruby 2.7.0 and Ruby on Rails 6.0.3.4
Okay, suppose I have two models En and Em and I want to set an N:M relationship between them. The Rails docs are a bit scarce, but they advise the use of the create_join_table migration method for establishing HABTM relationships. Soon enough, running rails g migration En Em gives me a migration file with
The setup:
def change
create_join_table :ens, :ems do |t|
t.index [:en_id, :em_id]
t.index [:em_id, :en_id]
end
end
Great. I also have to signal this N:M relationship in my En and Em models, and I do so:
class En < ApplicationRecord
has_and_belongs_to_many :ems
end
class Em < ApplicationRecord
has_and_belongs_to_many :ens
end
Looking good so far, my simple test works well too:
m = Em.create
n = En.new
n.ems << m
n.save
En.find(n.id).ems # equals [em], unsurprisingly
The Problem
The moment I want to do anything more fancy with ActiveRecord, say finding some En through Em through En, I run into issues:
Em.where(ens: En.where(id: En.first)) #fails
En.where(ems: Em.where(id: Em.first)) #fails
Error:
ActiveRecord::UnknownPrimaryKey: Unknown primary key for table ems_ens in model En::HABTM_Ems.
Because create_join_table does not give me a primary key, so I have to add one myself. No problem, one more migration should do the trick, right?
def change
add_column :ems_ens, :id, :primary_key
end
Error:
DRb::DRbRemoteError: PG::UndefinedColumn: ERROR: column "en_id" does not exist
LINE 1: ...CT "ems".* FROM "ems" WHERE "ems"."id" IN (SELECT en_id FROM...
If it helps, I did quickly write a repo to replicate this behavior exactly in this post. Surely I'm missing something obvious here, right? I'm having similar issues with my work code, but oddly enough I'm having issues one level deeper (something like En.where(ems: Em.where(ens: ...)), hierarchies in relational databases are no fun).
Additional information
Since one commenter requested, the auto-generated schema.rb and described tables.
ActiveRecord::Schema.define(version: 2020_11_09_210057) do
enable_extension "plpgsql"
create_table "ems", force: :cascade do |t|
t.string "mane"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "ems_ens", force: :cascade do |t|
t.bigint "en_id", null: false
t.bigint "em_id", null: false
t.index ["em_id", "en_id"], name: "index_ems_ens_on_em_id_and_en_id"
t.index ["en_id", "em_id"], name: "index_ems_ens_on_en_id_and_em_id"
end
create_table "ens", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
end
Table "public.ems_ens"
Column | Type | Collation | Nullable | Default
--------+--------+-----------+----------+-------------------------------------
en_id | bigint | | not null |
em_id | bigint | | not null |
id | bigint | | not null | nextval('ems_ens_id_seq'::regclass)
Indexes:
"ems_ens_pkey" PRIMARY KEY, btree (id)
"index_ems_ens_on_em_id_and_en_id" btree (em_id, en_id)
"index_ems_ens_on_en_id_and_em_id" btree (en_id, em_id)
Table "public.ems"
Column | Type | Collation | Nullable | Default
------------+--------------------------------+-----------+----------+---------------------------------
id | bigint | | not null | nextval('ems_id_seq'::regclass)
mane | character varying | | |
created_at | timestamp(6) without time zone | | not null |
updated_at | timestamp(6) without time zone | | not null |
Indexes:
"ems_pkey" PRIMARY KEY, btree (id)
Table "public.ens"
Column | Type | Collation | Nullable | Default
------------+--------------------------------+-----------+----------+---------------------------------
id | bigint | | not null | nextval('ens_id_seq'::regclass)
name | character varying | | |
created_at | timestamp(6) without time zone | | not null |
updated_at | timestamp(6) without time zone | | not null |
Indexes:
"ens_pkey" PRIMARY KEY, btree (id)
The problem here is that you're not joining onto the ens relation, and while the where method is smart enough to know that it needs to look for the en_id column, it's not smart enough to add that join onto your query. As a result, the join table isn't in the FROM clause, so the WHERE clause ends up invalid. To fix it, you can just add joins(:ens).
Incorrect (note how the FROM clause is not joining onto your join table):
irb(main):026:0> Em.where(ens: En.where(id: En.first))
En Load (0.9ms) SELECT "ens".* FROM "ens" ORDER BY "ens"."id" ASC LIMIT $1 [["LIMIT", 1]]
Em Load (0.8ms) SELECT "ems".* FROM "ems" WHERE "ems"."id" IN (SELECT en_id FROM "ens" WHERE "ens"."id" = $1) LIMIT $2 [["id", 1], ["LIMIT", 11]]
Traceback (most recent call last):
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column "en_id" does not exist)
LINE 1: ...CT "ems".* FROM "ems" WHERE "ems"."id" IN (SELECT en_id FROM...
Corrected (note how the WHERE stays the same -- only the INNER JOIN part is new):
irb(main):027:0> Em.joins(:ens).where(ens: En.where(id: En.first))
En Load (0.3ms) SELECT "ens".* FROM "ens" ORDER BY "ens"."id" ASC LIMIT $1 [["LIMIT", 1]]
Em Load (1.0ms) SELECT "ems".* FROM "ems" INNER JOIN "ems_ens" ON "ems_ens"."em_id" = "ems"."id" INNER JOIN "ens" ON "ens"."id" = "ems_ens"."en_id" WHERE "ems"."id" IN (SELECT en_id FROM "ens" WHERE "ens"."id" = $1) LIMIT $2 [["id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Em id: 1, mane: nil, created_at: "2020-11-10 03:40:07", updated_at: "2020-11-10 03:40:07">]>
I'm having issues with my app.
I am trying to run:
heroku run rake db:migrate
but I get this error:
Running rake db:migrate on pierwsza1... up, run.7908
ActiveRecord::SchemaMigration Load (22.9ms) SELECT "schema_migrations".* FROM "schema_migrations"
Migrating to AddUserIdToPins (20160515200705)
(1.9ms) BEGIN
== 20160515200705 AddUserIdToPins: migrating ==================================
-- add_column(:pins, :user_id, :integrer)
(3.6ms) ALTER TABLE "pins" ADD "user_id" integrer
(8.6ms) ROLLBACK
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:
PG::UndefinedObject: ERROR: type "integrer" does not exist
LINE 1: ALTER TABLE "pins" ADD "user_id" integrer
These are the contents of the file I generated with the rails generate migration add_user_id_to_pins user_id:integer:index:
class AddUserIdToPins < ActiveRecord::Migration
def change
add_column :pins, :user_id, :integer
add_index :pins, :user_id
end
end
In your migration file you have defined user_id as an integrer instead of an integer
You've just need to update your migration file with a valid type
Check this line of your logs :
PG::UndefinedObject: ERROR: type "integrer" does not exist
LINE 1: ALTER TABLE "pins" ADD "user_id" integrer
It clearly states that you have mistyped "integer" with "integrer". Please correct that in your file.
I'm having a problem where I've successfully registered a resource in ActiveAdmin, but I can't create or update any records. I think it's due to a namespacing issue. Can I override it using an option while registering the resource?
I'm building a Rails Engine that registers AA resources from within the engine. I followed the instructions here.
My engine contains lib/admin/myengine/myresources.rb
if defined?(ActiveAdmin)
ActiveAdmin.register Myengine::Myresource do
end
end
In the test/dummy app, the relevant schema looks like:
create_table "myengine_myresources", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
When I run the dummy app server, I successfully navigate to http://localhost:3000/admin/myengine_myresources and click 'New Myengine Myresource'
I type in a name and click 'Create Myresource', but it treats the request as if I've submitted blank attribute values.
The server log shows:
Started POST "/admin/myengine_myresources" for ::1 at 2015-12-02 11:13:52 -0800
Processing by Admin::MyengineMyresourcesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"stuff", "myresource"=>{"name"=>"Arbitrary Name"}, "commit"=>"Create Myresource"}
(0.1ms) begin transaction
(0.1ms) rollback transaction
Rendered /Users/me/.rvm/gems/ruby-2.2.3/gems/activeadmin-1.0.0.pre2/app/views/active_admin/resource/new.html.arb (190.7ms)
Completed 200 OK in 231ms (Views: 199.4ms | ActiveRecord: 0.2ms)
My working theory is that the params need to be inside :myengine_myresource rather than just :myresource.
Any ideas on how to get that working?
Here's one workaround:
if defined?(ActiveAdmin)
ActiveAdmin.register Myengine::Myeresource do
controller do
def permitted_params
params[:myengine_myresource] = params.delete :myresource
params.permit(myengine_myresource: [:my, :list, :of, :accepted, :params])
end
end
end
end
I am saving a time into database and I get a very different value back from the database.
2.1.0 :047 > Schedule.create(last_reminded_at: Time.now)
(0.9ms) BEGIN
SQL (1.1ms) INSERT INTO "schedules" ("last_reminded_at")
VALUES ($1) RETURNING "id" [["last_reminded_at", "2014-12-13 22:14:16.022267"]]
(8.3ms) COMMIT
=> #<Schedule id: 8, ... last_reminded_at: "2014-12-13 23:14:16">
Schedule is created with correct time, but when I get it back from the db, the time is always 1st Jan 2000.
2.1.0 :048 > Schedule.last
Schedule Load (0.9ms) SELECT "schedules".* FROM "schedules" ORDER BY id DESC LIMIT 1
=> #<Schedule id: 8, ... last_reminded_at: "2000-01-01 22:14:16">
Looking at the query, I suspect the insert statement is somehow incorrect?
Maybe I need to configure ActiveRecord timezone somehow?
This is the only time related config I have, in config/application.rb:
Time.zone = 'UTC'
The schema is:
create_table "schedules", force: true do |t|
....
t.time "last_reminded_at"
end
I am running Rails 4.1.8, Postgresql 9.2.6 and Ruby 2.1.2p95.
Your schema has set last_reminded_at to a time. You want a datetime as you care about the date too.
I'm a Rails beginner and to learn it I'm building a simple time tracking app. I want to populate an administrator's dashboard with a ton of information from many tables with nested information.
What would be the best practice for querying the database to request all of the data for one company to view a dashboard of all clients, projects, tasks, adjustments and minutes?
Here's how the data is structured:
Company
has_many clients
Client
belongs_to company
has_many projects
Project
belongs_to client
has_many tasks
Task
belongs_to project
has_many minutes
Minute
belongs_to task
This data structure might be really bad. I don't know.
An example view of the data:
Activision
-- Website Redesign
--- Development
---- 100 Minutes
I'm starting with this but I'm pretty but it could be totally backwards (Users belong to Companies):
#clients = Client.find_all_by_company_id(current_user.company_id)
#clients.each do |client|
project = Project.find_all_by_client_id(client.id)
puts project.name
project.each do |project|
task = Task.find_all_by_project_id(project.id)
puts task.name
end
end
I guess the question can also be asked: Is there a good book or resource that fully describes Rails ActiveRecord best practices?
Use the includes method to eagerly load the associations.
Example from the guides
Category.includes(:posts => [{:comments => :guest}, :tags]).find(1)
Based on what you said, that should be:
require 'active_record'
require 'logger'
# ===== Config =====
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
ActiveRecord::Base.logger = Logger.new $stdout
ActiveSupport::LogSubscriber.colorize_logging = false
# ===== Schema =====
ActiveRecord::Schema.define do
self.verbose = false
create_table :clients do |t|
t.string :name
t.integer :company_id
end
create_table :companies do |t|
t.string :name
end
create_table :projects do |t|
t.string :name
t.integer :client_id
end
create_table :tasks do |t|
t.string :name
t.integer :project_id
end
create_table :minutes do |t|
t.integer :quantity
t.integer :task_id
end
end
# ===== Classes =====
class Company < ActiveRecord::Base
has_many :clients
end
class Client < ActiveRecord::Base
belongs_to :company
has_many :projects
end
class Project < ActiveRecord::Base
belongs_to :client
has_many :tasks
end
class Task < ActiveRecord::Base
belongs_to :project
has_many :minutes
end
class Minute < ActiveRecord::Base
belongs_to :task
end
# ===== Data =====
Company.create! name: 'Activision' do |company|
company.clients.build name: 'Robert Kotick' do |client|
client.projects.build name: 'Website Redesign' do |project|
project.tasks.build name: 'Development' do |task|
task.minutes.build quantity: 100
end
end
end
end
# ===== Querying and displaying =====
company = Company.find_by_name 'Activision'
clients = Client.includes(projects: {tasks: :minutes}).where(company_id: company.id)
print "\n----- The query makes four requests, regardless of how much data you have. -----\n\n"
clients.inspect # do this to force loading since AR queries are lazy
print "\n----- some representation of the data (notice no queries while iterating through) -----\n\n"
clients.each do |client|
puts client.name
client.projects.each do |project|
puts "-- #{project.name}"
project.tasks.each do |task|
puts "--- #{task.name}"
task.minutes.each do |minute|
puts "---- #{minute.quantity}"
end
end
end
end
# ===== Output =====
# >> D, [2012-09-12T00:01:42.755414 #72855] DEBUG -- : (0.7ms) select sqlite_version(*)
# >> D, [2012-09-12T00:01:42.755890 #72855] DEBUG -- : (0.2ms) CREATE TABLE "clients" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "company_id" integer)
# >> D, [2012-09-12T00:01:42.756327 #72855] DEBUG -- : (0.1ms) CREATE TABLE "companies" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255))
# >> D, [2012-09-12T00:01:42.756728 #72855] DEBUG -- : (0.1ms) CREATE TABLE "projects" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "client_id" integer)
# >> D, [2012-09-12T00:01:42.757122 #72855] DEBUG -- : (0.1ms) CREATE TABLE "tasks" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "project_id" integer)
# >> D, [2012-09-12T00:01:42.757531 #72855] DEBUG -- : (0.1ms) CREATE TABLE "minutes" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "quantity" integer, "task_id" integer)
# >> D, [2012-09-12T00:01:42.906877 #72855] DEBUG -- : (0.0ms) begin transaction
# >> D, [2012-09-12T00:01:42.909242 #72855] DEBUG -- : SQL (0.5ms) INSERT INTO "companies" ("name") VALUES (?) [["name", "Activision"]]
# >> D, [2012-09-12T00:01:42.934937 #72855] DEBUG -- : SQL (24.7ms) INSERT INTO "clients" ("company_id", "name") VALUES (?, ?) [["company_id", 1], ["name", "Robert Kotick"]]
# >> D, [2012-09-12T00:01:42.936110 #72855] DEBUG -- : SQL (0.1ms) INSERT INTO "projects" ("client_id", "name") VALUES (?, ?) [["client_id", 1], ["name", "Website Redesign"]]
# >> D, [2012-09-12T00:01:42.937001 #72855] DEBUG -- : SQL (0.1ms) INSERT INTO "tasks" ("name", "project_id") VALUES (?, ?) [["name", "Development"], ["project_id", 1]]
# >> D, [2012-09-12T00:01:42.937767 #72855] DEBUG -- : SQL (0.1ms) INSERT INTO "minutes" ("quantity", "task_id") VALUES (?, ?) [["quantity", 100], ["task_id", 1]]
# >> D, [2012-09-12T00:01:42.938005 #72855] DEBUG -- : (0.0ms) commit transaction
# >> D, [2012-09-12T00:01:42.939882 #72855] DEBUG -- : Company Load (0.1ms) SELECT "companies".* FROM "companies" WHERE "companies"."name" = 'Activision' LIMIT 1
# >>
# >> ----- The query makes four requests, regardless of how much data you have. -----
# >>
# >> D, [2012-09-12T00:01:42.940458 #72855] DEBUG -- : Client Load (0.1ms) SELECT "clients".* FROM "clients" WHERE "clients"."company_id" = 1
# >> D, [2012-09-12T00:01:42.943272 #72855] DEBUG -- : Project Load (0.1ms) SELECT "projects".* FROM "projects" WHERE "projects"."client_id" IN (1)
# >> D, [2012-09-12T00:01:42.943919 #72855] DEBUG -- : Task Load (0.1ms) SELECT "tasks".* FROM "tasks" WHERE "tasks"."project_id" IN (1)
# >> D, [2012-09-12T00:01:42.944520 #72855] DEBUG -- : Minute Load (0.1ms) SELECT "minutes".* FROM "minutes" WHERE "minutes"."task_id" IN (1)
# >>
# >> ----- some representation of the data (notice no queries while iterating through) -----
# >>
# >> Robert Kotick
# >> -- Website Redesign
# >> --- Development
# >> ---- 100
This is a horrible Law of Demeter violation, if any of these things change at any point, whether in their structure or naming, we will have to come fix this code. I'm not really sure how to deal with that without introducing lots of abstractions.
Regarding a book, there have been many, but I honestly don't think the Rails world has figured out yet what constitute best ActiveRecord practices (in fact, there's a large portion of the community that thinks almost all ActiveRecord practices are just terrible -- I'm mostly in that camp).
But if you want things like the above, which says to use #includes to eager load associations, then the guides are a great place to find out information like that. I also really enjoyed this blog and videos.
This produces the same you have
#clients = current_user.company.clients
#clients.each do |client|
projects = client.projects
# puts project.name # makes no sense here
projects.each do |project|
project.tasks.each do |task|
puts task.name
end
end
end
try something like
Client.includes(
:company =>{:projects=>:tasks})
all of the above should be connected (via has_one, has_many, belongs_to)
Hope this helps!