Guys I came across an online practice question, and there's something that I don't properly understand
Below is a migration file
class ContactsMigration < ActiveRecord::Migration
def change
create_table :contacts do |t|
t.string :name
t.integer :telephone_number
t.text :address, null: false
t.timestamps
end
end
end
One of the questions is asking if there are any indexes on this table, and the correct answer is yes. Can't figure out where is the index here though. The logical is the timestamp, but the guides don't show that those columns are automatically indexed. Where am I going wrong? Any help or a link to a guide would be extremely appreciated
Active Record Basics
Primary keys - By default, Active Record will use an integer column
named id as the table's primary key (bigint for PostgreSQL and MySQL,
integer for SQLite). When using Active Record Migrations to create
your tables, this column will be automatically created.
Related
Here is a migration to create a table:
class CreateTemplates < ActiveRecord::Migration[5.1]
def change
create_table :templates, id: :uuid do |t|
t.references :account, type: :uuid, foreign_key: true
t.string :name
t.text :info
t.string :title
t.timestamps
end
end
end
Since account_id is a foreign_key (and identifies the customer) it will appear in almost all (99%) of queries on this table - there is not much point in retrieving a template that belongs to another customer.
So should I drop the index the above migration created for the account_id foreign_key and create this one instead?
add_index :templates, [:id, :account_id], unique: false
Or should I keep the original and also add this?
EDIT
To clarify the 99% use case - I think I was mistaken there. When creating a template, the account_id is always inserted so that the index method of the tempaltes_controller will always return all templates using the account_id, so that a user only sees a list of templates belonging to their account. For edits, updates, deletes, those actions only need the template_id. So my 99% guess is wrong! Most queries won't actually need a composite key it seems to me.
If most of your queries are going to filter on a combination of [:id, :account_id](which is unlikely) then creating a composite index will improve the performance of your queries.
However, it sounds like that most of your queries will only require :account_id, If that is the case then you do not need to add a composite index.
I am trying to make a polymorphic model in ActiveRecord. It should have a one-to-one relationship with another object of any type. I am following the Understanding Polymorphic Associations in Rails blog post from LaunchSchool.
My migration:
# Expresses an explanation for an action or event
class CreateReasons < ActiveRecord::Migration
def change
create_table :reasons do |t|
t.string :description
t.integer :event_id
t.string :event_type
t.timestamps
end
end
end
Note that I am not calling add_foreign_key.
My model class:
# Expresses an explanation for an action or event
class Reason < ActiveRecord::Base
belongs_to :event, polymorphic: true
end
The same error occurs if I remove this line.
When I run rake db:migrate (with or without bundle exec), I get an error, I think because of a foreign key constraint:
D, [2016-04-05T22:40:41.389615 #45464] DEBUG -- : (9.7ms) CREATE TABLE "reasons" ("id" serial primary key, "description" character varying(255), "event_id" integer, "event_type" character varying(255), "created_at" timestamp, "updated_at" timestamp, CONSTRAINT fk_reasons_event_id FOREIGN KEY ("event_id") REFERENCES "events" ("id"))
E, [2016-04-05T22:40:41.390720 #45464] ERROR -- : PG::Error: ERROR: relation "events" does not exist
: CREATE TABLE "reasons" ("id" serial primary key, "description" character varying(255), "event_id" integer, "event_type" character varying(255), "created_at" timestamp, "updated_at" timestamp, CONSTRAINT fk_reasons_event_id FOREIGN KEY ("event_id") REFERENCES "events" ("id"))
D, [2016-04-05T22:40:41.391245 #45464] DEBUG -- : (0.2ms) ROLLBACK
I don't know where this constraint came from, and I don't think I want it because this relationship should be polymorphic (the event relationship can be any type). Why is it there? What did I do wrong?
Your migration seems ok and I can hardly imagine this kind of behaviour provided by ActiveRecord itself. More than that, foreign key automatically generated by Rails would probably be named index_reasons_on_event_id, but not fk_reasons_event_id.
In my opinion, there is something going behind the scenes here, like patching ActiveRecord::ConnectionsAdapters, as was described here.
I would suggest to check initializer files for some nasty monkeypatching and probably your Gemfile for something alike automatic_foreign_key or schema_plus, which can infer foreign keys from column names and relations automatically.
Rails guide documents a different way of writing migration. SEE
class CreateReasons < ActiveRecord::Migration
def change
create_table :reasons do |t|
t.string :description
t.references :event, polymorphic: true, index: true
t.timestamps
end
end
end
I have used the way mentioned in your question for earlier version of rails.
So I accidentaly made a model in Ruby on Rails on which I wrote an attribute twice, with the same name and type. If I check my migration it looks like this:
class CreateTable < ActiveRecord::Migration
def change
create_table :table do |t|
t.text :Total
t.text :Total
t.timestamps
end
end
end
The name is not really Table, but you get the idea.
My question is how will this be reflected on the db, since running the migrations caused no problem. I don't think it's posible to have to columns with the same id, so I'm guessing it was only created once.
It will create the column just once. Your table is valid. But i advise you to rollback the migration, delete the migration file and create a new migration with valid column names(Given no one else has pulled this change of yours).
I am trying to setup a model based on a pre-built model that has the following code in the migration:
def change
create_table :friendships do |t|
t.string :user_id, :friend_user_id
t.string :status
end
end
I get the t.string :status part where a column is being created which will use a string. I don't understand the t.string :user_id, :friend_user_id part where there are two attributes on the same line.
In the first line, two columns are being created named as user_id and friend_user_id having string data type. In the second line there is another column being created named status having string data type. So in migrations you can write all column names in one line which have the same data type. i.e. The migration can be written like this.
def change
create_table :friendships do |t|
t.string :user_id, :friend_user_id, :status
end
end
This is a join table to show relationship. Actually user_id and friend_user_id all refer to id in users table.
Say my id is 10 and yours is 11. I regard you as a "good" friend. So in this table there is a record: 10, 11, "good". But you think me as a normal friend, so one more record: 11, 10, "normal"
This is simply a declaration of two columns with similar attributes in the same line. It is similar to
t.string :user_id
t.string :friend_user_id
You can find the documentation for that in here (look for "Short-hand examples")
So I've got two models, State and Acquisition. State has_many Acquisitions. I felt like an autoincrementing integer primary key for 51 records was rather silly. So I altered the model for the State to be the PK (State being the two letter abbreviation; I'm not storing the actual state name anywhere:
class State < ActiveRecord::Base
self.primary_key = "state"
has_many :acquisition_histories
end
The problem is when I created my Acquisition model, it created the foreign key column state_id as an integer. More specifically, the script/generated migration did:
class CreateAcquisitions < ActiveRecord::Migration
def self.up
create_table :acquisitions do |t|
t.date :date
t.string :category
t.text :notes
t.references :state
t.timestamps
end
end
end
I'm assuming that t.references data type sets it to int. The problem is my create method on my Acquisition class is trying to put a state abbreviation into the state_id field on the table acquisitions (and yes, it's called state_id on the database, even though it says :state in the migration script). The method doesn't fail, but it does put a 0 in the state_id field and the records go into the ether.
Though, I agree that this might be more trouble than it's worth considering the extra effort of working against the defaults elsewhere, just in case you actually want to do what you've asked:
Create states migration:
class CreateStatesTable < ActiveRecord::Migration
def change
create_table :states, id: false do |t|
t.string :state, limit: 2
t.string :name
t.index :state, unique: true
end
end
end
states model:
class State < ActiveRecord::Base
self.primary_key = :state
end
Note that before Rails 3.2, this was set_primary_key = :state instead of self.primary_key= see: http://guides.rubyonrails.org/3_2_release_notes.html#active-record-deprecations
if you find yourself here... leave as quickly as you can and go to:
Using Rails, how can I set my primary key to not be an integer-typed column?
In Rails 5.1 you can specify the type of the primary key at creation:
create_table :states, id: :string do |t|
# ...
end
From the documentation:
A Symbol can be used to specify the type of the generated primary key column.
I'm working on a project that uses UUIDs as primary keys, and honestly, I don't recommend it unless you're certain you absolutely need it. There are a ton of Rails plugins out there that will not work unmodified with a database that uses strings as primary keys.
Note that mkirk's answer creates a faux primary key. This explains why ActiveRecord needs to be told what the primary key is. Inspecting the table reveals
Table "public.acquisitions"
Column | Type | Modifiers
--------+----------------------+-----------
state | character varying(2) |
name | character varying |
Indexes:
"index_acquisitions_on_state" UNIQUE, btree (state)
In practice this works as expected so nothing wrong there, but it could be nicer.
We can keep the id column and change its type to string*. The migration looks like
class CreateAcquisitionsTable < ActiveRecord::Migration
def change
create_table :acquisitions do |t|
t.string :name
end
change_column :acquisitions, :id, :string, limit: 2
end
end
Inspecting the table reveals that you have an actual primary key with all the goodies such as the unique key constraint (no unique index needed), not null constraint, and auto-incrementing key.
Table "public.acquisitions"
Column | Type | Modifiers
--------+----------------------+---------------------------------------------------
id | character varying(2) | not null default nextval('acquisitions_id_seq'::regclass)
name | character varying |
Indexes:
"acquisitions_pkey" PRIMARY KEY, btree (id)
And you won't need to explicitly tell ActiveRecord what the primary is.
You'll want to consider setting a default id if none is provided.
class MyModel < ActiveRecord::Base
before_create do
self.id = SecureRandom.uuid unless self.id
end
end
* Disclaimer: you should not change the default primary key unless you have good reason to
You want to follow the Rails conventions. The extra primary key is not an issue in any way. Just use it.
I had a bit of experience with string used as primary keys and it's a pain in the ***. Remember that by default if you want to pass an object with the default :controller/:action/:id pattern, the :id will be a string and this will probably lead to routing problems if some ids get weirdly formatted ;)
class CreateAcquisitions < ActiveRecord::Migration
def self.up
create_table :acquisitions, :id => false do |t|
t.date :date
t.string :category
t.text :notes
t.references :state
t.timestamps
end
end
end
Rails works best when you don't fight against the defaults. What harm does it do to have an integer primary key on your state table?
Unless you're stuck with a legacy schema that you have no control over, I'd advise you to stick to the Rails defaults—convention over configuration, right?—and concentrate on the important parts of your app, such as the UI and the business logic.