One to one Association in Rails not working - ruby-on-rails

I'm kind of new in Rails and couldn't make my Models relationship to work, and I don't know the reason. I've tried in many ways and followed many different tutorials, but couldn't figure out the right way. Do you know what's wrong with my code? How can I test it?
I have two models, with has_one association User and Profile. Profile belongs to User. So every time I add a user, I would also add a Profile.
This is the migration file for User model
class CreateUsers < ActiveRecord::Migration[5.1]
def change
create_table :users do |t|
t.string :email
t.string :password_digest
t.timestamps
end
end
end
This is migration file for Profile model
class CreateProfiles < ActiveRecord::Migration[5.1]
def change
drop_table :profiles
create_table :profiles do |t|
t.string :name
t.string :lastname
t.string :phone
t.string :address
t.string :city
t.string :state
t.string :country
t.string :gender, :limit => 10
t.string :zipcode
t.references :users, foreign_key: true
t.timestamps
end
end
end
model Profile
class Profile < ApplicationRecord
belongs_to :user
end
model User
class User < ApplicationRecord
has_secure_password
has_one :profile
end
Thank you very much!

I think the problem might be with the migration.
Profile belongs to User, so in the create_profiles migration you should have:
t.references :user, foreign_key: true
(user instead of users).
With your code, the Profile model will have users_id field when it should have user_id.

Related

Rails 5 error when creating model instance with foreign keys

I have two models in use at this point: Airports and Flights.
Flights belong to both an origin airport and destination airport. When I try to create a Flight model instance, I get the following error:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such table: main.destination_airports
I have set up my code to match the example here: https://www.theodinproject.com/courses/ruby-on-rails/lessons/active-record-associations
I've researched the problem (here and elsewhere), but can't turn up anything that seems to match my situation. I can't figure out what's going wrong. Has to be something simple. Something with the migrations, maybe? Thanks in advance for the help.
Relevant code:
Airport model:
class Airport < ApplicationRecord
has_many :departing_flights, foreign_key: "origin_airport_id",
class_name: "Flight"
has_many :arriving_flights, foreign_key: "destination_airport_id",
class_name: "Flight"
validates :abbreviation, presence: true, length: { is: 3 }
end
Flight model:
class Flight < ApplicationRecord
belongs_to :origin_airport, class_name: "Airport"
belongs_to :destination_airport, class_name: "Airport"
has_many :bookings
has_many :passengers, through: :bookings
end
Migrations:
class CreateAirports < ActiveRecord::Migration[5.1]
def change
create_table :airports do |t|
t.string :abbreviation
t.string :full_name
t.string :city
t.string :state
t.string :zip
t.timestamps
end
end
end
class CreateFlights < ActiveRecord::Migration[5.1]
def change
create_table :flights do |t|
t.references :origin_airport, foreign_key: true
t.references :destination_airport, foreign_key: true
t.datetime :depart_time
t.datetime :arrive_time
t.integer :capacity
t.string :airline
t.string :flight_number
t.timestamps
end
end
end
Check the filenames of the migrations, perhaps they are not being run in the correct order?
Also, you may just need to run the migration in the right environment (RAILS_ENV=development)
Update: I found the (an?) answer - changing the migration for the flights table to replace t.references with t.integer and the column name with "x_id" corrects the problem. So, does references only work if using the model name for the column id?

Two different relations between same two models

I have two models: users and emails.
I have separated this tables because we want the user to be able to have many emails in the same user account, and also easily check uniqueness of email values among accounts.
primary_email_id is a FK of a unique email, but also a user has many emails. How do I say that in the rails model?
I was trying
class User < ActiveRecord::Base
# relations
has_many :emails
has_one :primary_email
end
…
class Email < ActiveRecord::Base
# relations
belongs_to :user
end
Is that correct? How does rails know when I say primary_email I'm making reference to the emails table?
By the way, both migrations are:
create_table :users do |t|
t.string :username
t.string :first_name
t.string :last_name
t.binary :password
# t.integer :primary_email
t.timestamps null: false
end
create_table :emails do |t|
# t.integer :user
t.string :email
t.boolean :verified
t.timestamps null: false
end
add_reference :users, :primary_email, references: :emails, index: true, foreign_key: true
add_reference :emails, :user, index: true, foreign_key: true

Rails 4 has_many :through relationship: destroy parent model instance when child model instance count reaches 0

In our Rails 4 app, there are four models:
class User < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :calendars, through: :administrations
end
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
end
class Calendar < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :users, through: :administrations
has_many :posts, dependent: :destroy
end
class Post < ActiveRecord::Base
belongs_to :calendar
end
Here are the corresponding migrations:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email
t.integer :total_calendar_count
t.integer :owned_calendar_count
t.timestamps null: false
end
end
end
class CreateAdministrations < ActiveRecord::Migration
def change
create_table :administrations do |t|
t.references :user, index: true, foreign_key: true
t.references :calendar, index: true, foreign_key: true
t.string :role
t.timestamps null: false
end
end
end
class CreateCalendars < ActiveRecord::Migration
def change
create_table :calendars do |t|
t.string :name
t.timestamps null: false
end
end
end
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.references :calendar, index: true, foreign_key: true
t.date :date
t.time :time
t.string :focus
t.string :format
t.string :blog_title
t.text :long_copy
t.text :short_copy
t.string :link
t.string :hashtag
t.string :media
t.float :promotion
t.string :target
t.integer :approval
t.text :comment
t.timestamps null: false
end
end
end
Each time a user quits a calendar, meaning we destroy the corresponding administration, we want to make sure that the following happens:
We check if there are other administrations for the same calendar.
If there are, we do nothing (the user is simply redirected to his dashboard).
But if there aren't any more (because the user was the last one to quit the calendar for example), then we want to destroy the calendar as well.
This should automatically destroy all the posts belonging to this calendar, thanks to the has_many :posts, dependent: :destroy line in our Calendar model, but we guess it would not hurt to check that too.
We are thinking of achieving this through a private clear_calendar method in the Calendar model, that we would use as an after_destroy callback in the Administrations controller:
private
def clear_calendar
#calendar = Calendar.find(params[:id])
unless #calendar.administration.exists?
#calendar.destroy
end
end
Does that make sense?
This is a very sensible approach, and the whole world is happy that you didn't place this logic in a controller action.
One thing to note: by placing this logic in your Calendar model, you're necessarily binding Calendar and Administration together. Perhaps you find that to be ok at this point in time, but a truly object-oriented program, instead of asking another model if it exists, would instead tell a model what it wants, instead of how it wants it done (e.g. delete self if this association doesn't exist).
I would recommend placing this logic in a PORO--perhaps a service--that removes unnecessary coupling from your database-backed objects. Perhaps that'd look something like so:
class Calendar < ActiveRecord::Base
...
private
def clear_calendar
ParentDestructionService.new(self)
end
end
class ParentDestructionService
def initialize(parent)
#parent = parent
end
.....logic goes here.....
end
This way, not only do you truly separate a how process from a class that shouldn't care about Administration, but you now have the capabilities to either stick this in a Sidekiq process, or simply thread it off. Either way, you're way more flexible. Now in the future, you'll be able to send ANY parent through that service, and things will work as intended.

Ruby on rails does not create foreign keys on database

Is it normal that rails doesn't create the foreign keys on database? Or I'm doing something wrong?
I have these models:
class City < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
belongs_to :city
end
and their respective migrations:
class CreateCities < ActiveRecord::Migration
def change
create_table :cities do |t|
t.string :name
t.timestamps
end
end
end
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
t.references :city, index: true
t.timestamps
end
end
end
That's correct. Rails doesn't automatically add foreign keys, you have to specify it yourself in the migration like you have.
t.references :city is effectively the same as t.integer :city_id.
Are you saying that even though are specifying a foreign key, that you aren't seeing the results?

ActiveRecord relationships and migrations for new web app

I'm a Rails beginner trying to build my first web app.
In my web app Physiotherapists can add Patients and create ExercisePlans from a list of Exercises. I sketched out the model relationships as follows underneath. Is this the correct way to do it?
I am mostly concerned about the following:
Is a join table the correct way to match exercises to exercise_plans?
Do I need a join table to match physiotherapists to exercise_plans?
RELATIONSHIPS
Physiotherapist
has_many :patients
has_many :exercise_plans
Patient
has_many :exercise_plans
belongs_to :physiotherapist
Exercise
has_many :exercise_plans
ExercisePlan
belongs_to :patient
belongs_to :physiotherapist
has_and_belongs_to_many :exercises
MIGRATIONS
class CreatePhysiotherapists < ActiveRecord::Migration
def change
create_table :physiotherapists do |t|
t.string :first_name
t.string :last_name
t.string :company_name
t.string :email
t.string :password
t.timestamps
end
class CreatePatients < ActiveRecord::Migration
def change
create_table :patients do |t|
t.string :first_name
t.string :last_name
t.string :email
t.integer :physiotherapist_id #the physiotherapist to which the patient belongs
t.timestamps
end
class CreateExercises < ActiveRecord::Migration
def change
create_table :exercises do |t|
t.string :title
t.string :category
t.string :bodypart
t.text :instructions
t.timestamps
end
class CreateExercisePlans < ActiveRecord::Migration
def change
create_table :exercise_plans do |t|
t.string :name
t.integer :exercise_id #an array of exercises that are in the plan
t.integer :physiotherapist_id #the physiotherapist who created the plan
t.integer :patient_id #the user for whom the exercise plan is made
t.timestamps
end
#join table for the has_and_belongs_to_many relationship with exercises
create_table :exercise_plans_exercises do |t|
t.integer :exercise_id
t.integer :exercise_plan_id
end
end
1) Yes. However, I'd use has_many :through instead of has_and_belongs_to_many. It is a much more flexible approach and allows for a customization of exercise for each specific exercise plan. For example, you might want to store a number of repetitions or duration in Activity model. http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
ExercisePlan
has_many :activities
has_many :exercises, through: :activities
Exercise
has_many :activities
has_many :exercise_plans, through: :activities
Activity
belongs_to :exercise
belongs_to :exercise_plan
2) No, there's no need for an additional join table.
Notes:
You don't really need to have physiotherapist_id in exercise_plans, because you already have it in patients. You can exercise_plan.patient.physiotherapist.

Resources