New working with Rails 4 and have a question on associations.
Let's say I have a Car
create_table "cars", force: true do |t|
t.string "name"
t.integer "owner_id"
t.integer "mechanic_id"
end
And I have some users which roles are "Owner" and "Mechanic"
create_table "users", force: true do |t|
t.string "name"
t.integer "role_id"
end
Now when I get a list of Cars, I want the names of the Owner and the Mechanic...
I know this sounds easy but i've been banging my head around the associations and not getting anywhere.
I want to display this:
name owner mechanic
Ferari Paul James
with this schema
User
id first role
1 Paul owner
2 James mechanic
Car
name owner_id mechanic_id
Ferrari 1 2
Any help would be much appreciated.
You have two associations to the same model. So in your model/car.rb write:
class Car < ActiveRecord::Base
belongs_to :owner, class_name: 'User'
belongs_to :mechanic, class_name: 'User'
end
and you can use car.owner.name and car.mechanic.name.
See rails guide on associations
Related
I'm developing an assigning system. I need to be able to access the Assignment of the referees to the Game model. The Assignment can have up to 4 referees who are all Users. I believe my associations are correct. My questions are:
Do I need to add an assignment_id to the Game table?
To test my system I will need to seed data eventually. How do I seed the data/setup the Assignment so that each attribute (center_referee, assistant_referee) is a User? I would like this form to be a drop-down inevitably if it makes a difference.
For seed Data, I'm looking for something along the lines of the following (ignore that I'm using name instead of first_name):
Assignment.create(center_referee: user.name, assistant_referee_1: user.name, assistant_referee_2: "user.name", fourth_official: "user.name", game_id: 1)
Do I need to setup up accepts_nested_attributes on the model associations to accomplish all this?
Models:
class User < ApplicationRecord
has_many :assignments
has_many :games, through: :assignments
end
class Game < ApplicationRecord
has_one :assignment
has_many :users, through: :assignments
end
class Assignment < ApplicationRecord
belongs_to :game
belongs_to :user
end
Schema:
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "first_name"
t.string "last_name"
t.string "role"
end
create_table "games", force: :cascade do |t|
t.string "home_team"
t.string "away_team"
end
create_table "assignments", force: :cascade do |t|
t.string "center_referee"
t.string "assistant_referee_1"
t.string "assistant_referee_2"
t.string "fourth_official"
t.integer "game_id"
end
I know this is somewhat of a loaded question but I've been scratching my head over this for quite sometime now.
I would like to add to what #Vijay said,
As you want to have multiple user_ids in single table, I would suggest you to name the association, for example: In assignment.rb as belongs_to :center_referee, class_name: 'User'
You can also keep the association optional by using optional: true.
Do I need to add an assignment_id to the Game table?
No, Here Assignment belongs_to Game so id should be present in the Assignment model(as an assignment cannot exist without a game but the game is independent of assignment).
If this was about accessing the assignment for a given game, you can try game.assignment.
Do I need to setup up accepts_nested_attributes on the model associations to accomplish all this?
accepts_nested_attributes is used if you want to accept attributes for the associated model(Assignment) while accepting attributes for the model(Game).
for more info checkout
To test my system I will need to seed data eventually. How do I seed the data/setup the Assignment so that each attribute (center_referee, assistant_referee) is a User? I would like this form to be a drop-down inevitably if it makes a difference.
I don't quiet get your this question, but checkout if this helps:
f.collection_select(:center_referee_id, User.all, :id, :name )
Hope this helped.
In your Assignment class, the belongs_to for game is correctly correlated to the database schema. But, the belongs_to :user is incorrect - there is no assignments.user_id column in the db.
I think you want 4 columns, but each of them referring back to the users table with a different name - is that correct? If so, then your model should have the belongs_to with the appropriate option for class.
Since you mention that these are optional, you will have to also specify the allow_null appropriately.
btw, your games table and the corresponding Game model are also not correlating correctly.
I think you need to start with only 2 tables, logically model them correctly to satisfy the one-to-one, one-to-many relationships. Once this is done, then "translate" that into the rails model DSL. currently, this all seems very hay-wire.
create_table "assignments", force: :cascade do |t|
t.string "center_referee"
t.string "assistant_referee_1"
t.string "assistant_referee_2"
t.string "fourth_official"
t.integer "game_id"
end
Looks like you currently have this. What you need to do is to have a change of the types to as below:
create_table "assignments", force: :cascade do |t|
t.integer "center_referee_id"
t.integer "assistant_referee_1_id"
t.integer "assistant_referee_2_id"
t.integer "fourth_official_id"
t.integer "game_id"
end
This is going to work because, as you are passing the belongs_to :center_referee, class_name: "User" to the model, ActiveRecord is intelligent enough to query the ids on the user table.
I have two models, Clinician and Patient. A clinician has_many: patients and a patient belongs_to :clinician. A join table, shared_patients is meant to store additional associations between patients and clinicians as a patient can be shared by many other clinicians besides the one it belongs_to. This is done using a has_and_belongs_to_many relationship.
See models:
class Clinician < ActiveRecord::Base
has_many :patients
has_and_belongs_to_many :shared_patients, join_table: 'shared_patients', class_name: 'Patient'
end
class Patient < ActiveRecord::Base
belongs_to :clinician
has_and_belongs_to_many :shared_clinicians, join_table: 'shared_patients', class_name: 'Clinician'
end
This is how my tables are set out in the db schema:
create_table "clinicians", force: true do |t|
t.string "first_name"
t.string "last_name"
t.integer "user_id"
end
create_table "patients", force: true do |t|
t.integer "clinician_id"
t.string "first_name"
t.string "last_name"
t.integer "user_id"
end
create_table "shared_patients", id: false, force: true do |t|
t.integer "clinician_id"
t.integer "patient_id"
end
Using these I would like to show the list of clinicians that a patient is shared with.
Right now I am getting an error:
PG::UndefinedTable: ERROR: relation "shared_patients" does not exist
LINE 1: INSERT INTO "shared_patients" ("clinician_id", "id", "patien...
If I try and create a relationship in console:
#shared = SharedPatient.new("id"=>1, "clinician_id"=>2526, "patient_id"=>1307)
=> #1, "clinician_id"=>2526, "patient_id"=>1307}>
#shared.save
Any advice on solving this error or on structuring the models to get the associations I want would be great. Thanks
When you have a has_and_belongs_to_many then you cannot have a class for the join table, ie. you can't have SharedPatient and you can't try using it as you've done.
I have a form that creates a new exercise showing which muscle groups are worked. Here's an example of what I want to enter into the DB:
New exercise: name => Pull-up, primary => back, secondary => [biceps, forearms, chest]
How should I set this up? I don't want to store primary and secondary as arrays in the muscle_groups_exercised table because I have future queries that will search for exercises based on a muscle_group that is primary to that exercise.
Here is the schema for this part of the app:
create_table "exercises", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "muscle_groups", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "muscle_groups_exercised", force: true do |t|
t.integer "muscle_group_id"
t.integer "exercise_id"
t.binary "primary"
t.binary "secondary"
end
add_index "muscle_groups_exercised", ["exercise_id"], name: "index_muscle_groups_exercised_on_exercise_id", using: :btree
add_index "muscle_groups_exercised", ["muscle_group_id"], name: "index_muscle_groups_exercised_on_muscle_group_id", using: :btree
Here are the models:
class Exercise < ActiveRecord::Base
has_many :muscle_groups, through: :muscle_groups_exercised
end
class MuscleGroup < ActiveRecord::Base
has_many :exercises, through: :muscle_groups_exercised
end
class MuscleGroupExercised < ActiveRecord::Base
belongs_to :exercise
belongs_to :muscle_group
end
I have an exercises_controller and muscle_groups_controller. I think this code should reside in the exercises_controller and muscle_groups_exercised model but not exactly sure how to do this.
What you are doing looks right to me. I imagine will want to apply muscle groups to an exercise, even if you are going to be searching within muscle groups for exercises later on. I would put the required code within exercises_controller.rb.
Check out Ryan Bates article and video on HAMBTM checkboxes for some help on how to do this. It isn't exactly what you are doing, you will need to apply an additional value for secondary or primary.
BTW, I wouldn't have two columns for secondary and primary, since there is only two values, just have the primary one and assume if it isn't true, then the muscle group is secondary.
Do you have a more specific question? Your schema seems appropriate for your requirements. You may not need both a t.binary primary and a t.binary secondary--you could probably get away with having just one of them to specify whether that entry is primary or secondary.
My app consists of exercises that users add to workouts. Users can create exercises or select existing ones.
*** UPDATE ****
I've added a model per Ben's solutions.
I'm receiving errors as below when attempting to add exercises to workouts. Is my syntax wrong? I've attempted soltions like this:
w=Workout.last
e=Exercise.last
w.exercises.build(:exercise => e) # NameError: uninitialized constant Workout::ExercisesWorkout
w.exercises_workouts.create(:exericse_id => 1) #NameError: uninitialized constant Workout::ExercisesWorkout
I'm confused by the new methods attached from the association as well as "_" or not, camelCase, Pluralize, symbol..etc.
Rails seems to be looking for the class ExercisesWorkout yet I define "exercises_workouts" and/or ExercisesWorkouts.
Thanks.
I'm having trouble adding exercises to workouts from the rails console. 2 potential issues that I see:
I don't know the proper syntax to do this (build, create, find)
Application setup properly (join table, models,..etc.)
Please let me know where my error is and if there is a better structure / association to use.
Thank you.
Models:
class Exercise < ActiveRecord::Base
has_many :workouts, :through => :exercises_workouts
has_many :exercises_workouts
end
class Workout < ActiveRecord::Base
has_many :exercises, :through => :exercises_workouts
has_many :exercises_workouts
end
class ExercisesWorkouts < ActiveRecord::Base
belongs_to :exercise
belongs_to :workout
end
schema.db:
ActiveRecord::Schema.define(version: 20141129181911) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "exercises", force: true do |t|
t.string "name"
t.string "description"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "exercises_workouts", id: false, force: true do |t|
t.integer "exercise_id", null: false
t.integer "workout_id", null: false
end
create_table "workouts", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
end
ERROR:
w=Workout.new #create new workout
w.name = 'test' #name workout
w.save #save workout
e1=Exercise.new #create new exercise
e1.name='press' #name exercise
e1.save #save exercise
#I'm not sure of the syntax here... I've tried alot between create, build using symbols and finds...., this is just one example..
w.exercises.create(e1) #NameError: uninitialized constant Workout::ExercisesWorkout
You also need a model for the join table:
class ExercisesWorkouts < ActiveRecord::Base
belongs_to :exercise
belongs_to :workout
end
Here is answer that covers join tables in more detail, if you're interested:
What would the joining table called in this case in Rails 3.1?
I have 3 models: Hacks, Votes, Users.
A user can create many hacks.
Each user should be able to vote on each hack ONCE (Rating of 1-5. Rating should be updateable in case of a missclick or whatever).
I thought about the following relations:
Hack.rb
belongs_to :user
User.rb
has_many :hacks
Votes.rb
belongs_to :user
belongs_to :hack
Is that correct or am I missing something?
I thought about getting all the votes like this later on:
Hack.first.votes
What kind of foreign-keys do I have to set up?
In my schema.rb I already successfully set my users<=>hack relation up, without any foreign keys.
ActiveRecord::Schema.define(version: 20141019161631) do
create_table "hacks", force: true do |t|
t.string "url"
t.string "name"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", force: true do |t|
t.string "email", null: false
t.string "crypted_password", null: false
t.string "salt", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.integer "role"
end
end
Thank you very much in advance!
I think this is what you want to have.
class User.rb
has_many :votes
has_many :hacks, through: :votes
end
class Vote.rb
belongs_to :user
belongs_to :hack
end
class Hack.rb
has_many :votes
end
With this, a hack has many votes through a user.
foreign keys:
votes table: user_id, hack_id
You should be able to do hack.votes
EDIT:
I edited the model to reflect a normal has many through relationship
user -> vote <- hack
a user has many votes
a user has many hacks through votes
a hack has many votes
foreign keys live in the votes table. You can use the following when creating the votes table to indicate the foreign key
t.references user
t.references hack