What kind of migration is this? - ruby-on-rails

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")

Related

Where is the index on this table

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.

Get the most common column value between all records

I'm trying to return the most common text value (country) from the database.
Whilst my first attempt is not producing any errors, I suspect it's not returning the most common value:
#mostpopularroast = Roast.group(:id).order("count(country) DESC").first
Interesting it gives me the exact same result should I use ASC instead.
I'm therefore now trying a solution from this similar question:
#mostpopularroast = Roast.group('country').order('count(*)').limit(1).pluck(:country).first
But this is giving me the error undefined method 'country' for "Country 1":String. Country 1 being the value in the db.
my model
class CreateRoasts < ActiveRecord::Migration[5.1]
def change
create_table :roasts do |t|
t.string :roaster
t.string :name
t.string :country
t.string :region
t.string :bestfor
t.string :beans
t.string :roast
t.string :slug
t.timestamps
end
end
end
You should apply descending ordering to get the most popular country:
Roast.group(:country).select(:country).order("count(*) desc").first.country
Your initial error is not related to this, it is just that you are using pluck, which returns Array object, then you are calling first method on it and getting String object, which is the object that is already the name of the most popular roast country, but then you are trying to call country on it, which results in the exception.

Rails 5.1 - should I create a database index on primary_key and foreign_key?

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.

How to modify the params that rails use to pull object from database

Sorry about the title, it's really hard to explain but I'll try my best to make it as clear as possible.
So, I have this app that I am building sort of 'Social Shift Trading Network'. So in this app I have a calendar(fullcalendar-rails) that I use, and I generate shifts using a model but I realise that the id generated automatically by the database tend to not be useful when there are a lot of deletion of object or changes because the index is not being reset. So, I thought that I would put a shift_id column in my Shift model and generate id with SecureRandom.urlsafe_base64(8).
But how do I set it as a primary key so that when I edit or call show on it for it to use shift_id as params?
I tried set :id => false, and set :primary => :shift_id but still no result. I believe because my route format is "/shifts/:id/edit(.:format)" formatted to pull :id that it does not work.
Thank for you for any help in advance.
class CreateShifts < ActiveRecord::Migration
def change
create_table :shifts, {:id => false, :primary_key => :shift_id} do |t|
t.string :position
t.datetime :date
t.datetime :start_time
t.datetime :finish_time
t.integer :original_owner
t.integer :current_owner
t.string :shift_id
t.string :shift_posted, :default => "Not Posted"
t.timestamps
end
end
If what I understood is correct, all you want is that the URL becomes something like this
/shifts/:some_random_hash/edit
To do that you don't need to mess your database like that, just generate that normal table, and add that :shift_id field, then tell the model which field to use for the url.
class Shift < ActiveRecord::Base
to_param
shift_id
end
end
This way when you use url_for or shift_path the model will use the :shift_id instead to generate that URL, but internally it would use the auto increment id

Ruby on Rails: prevent from selecting a column by default

I have entries table with a content field which might contain a lot of text. In most cases I don't need to access that field, so it seems to be a big waste of resources to every time load a huge amount of unused data from the database (select * from entries where id = 1).
How could I specify the default_scope, that all the fields apart from content would be loaded from database?
Assuming Rails 3 and a schema that looks like this:
create_table "entries", :force => true do |t|
t.string "title"
t.text "content"
t.datetime "created_at"
t.datetime "updated_at"
end
You can use the select method to limit the fields that are returned like this:
class Entry < ActiveRecord::Base
default_scope select([:id, :title])
end
In the rails console, you should see something like this:
puts Entry.where(:id => 1).to_sql # => SELECT id, title FROM "entries" WHERE "entries"."id" = 1
When you do want to select all of the fields, you can use the unscoped method like this:
puts Entry.unscoped.where(:id => 1).to_sql # => SELECT * FROM "entries" WHERE "entries"."id" = 1
As scoping, and default_scope mainly, gives many problems in use, my best way is to move big contents (binary, very large texts) to separate table.
create_table "entries", :force => true do |t|
t.string "title"
# ... more small sized attributes of entries
t.timestamps
end
create_table "entry_contents", :force => true do |t|
t.references :entries, foreign_key: true
t.text "content"
t.timestamps
end
class Entry ...
# reference
has_one :entry_content
# build entry_content for every new entry record
after_initialize do |entry|
entry.build_entry_content unless entry.entry_content.present?
end
end
This limits loading big data only when needed.
Entry.find(1).entry_content.content
To build on #phlipper answer's, if you want to just specify one or a few columns to get rid of:
class Entry < ActiveRecord::Base
default_scope { select(Entry.column_names.map!(&:to_sym) - [:metadata]) }
end
As you can see, as of Rails 5+ you have to pass a block to default_scope.
Also, you should consider not using default scope
Its not a default scope, but I am using the following solution for my case:
scope :no_content, -> { select(self.column_names.map(&:to_sym) - [:content]) }
Add no_content to the call is imo not a big deal, because you probably know which calls are a problem.

Resources