creating a record with has_one and belongs_to - activerecord

I have a User and a Team model. A user can only have one team and each team belongs to a user. I'm struggling to get the association to work both ways.
Team model:
class Team < ActiveRecord::Base
belongs_to :user
end
User model:
class User < ActiveRecord::Base
has_one :team
end
I've added a team_id column to the users table and user_id to a teams table.
I want to create a team for the user so in the console I did the following
#user = User.find_by(id: 4)
#team = #user.build_team
Which created a team with the correct user_id but the team_id for the user is still nil. What am I doing wrong?

I was going to delete this question as it's probably pretty stupid, but in case any one else gets as confused as me I found the answer here: http://ruby-on-rails.wonderhowto.com/how-to/use-one-one-association-when-using-ruby-rails-3-407824/
What I now understand is that you have a has_one relationship and the foreign key appears in the table of the Model that it 'owns'. I.e. a user has_one team so the teams table needs a user_id. The users table does not need a team_id.
You can create a new team by using
#team = #user.build_team
As I did or:
#user.team = (name: "blah", rating: "blah blah")
and then if you want to get the teams user then
#user.team
will return that object.

Related

Activerecord/Datamapper - Have one child belong to many parents

How would you set up an activerecord/datamapper association for the following scenario:
A user creates a "bookshelf" which has many books(a book object just has an isbn that is used to query an api, and has_many review objects associated with it). Let's say Jack creates a "bookshelf" with a book object. Then, lets say that Jill creates a "bookshelf" with the same book object(it has the same id and the same reviews). The book object has the following code as of now:
class Book < ActiveRecord::Base
has_many :reviews
end
Then, when you view the page for a book (you click the link to it from the "bookshelf" created by Jack) you should see the same book object when you clicked the link to it from Jill's "bookshelf" (e.g. both "bookshelves" have a link to /books/23 because they have the same book object).
I have not been able to figure this out with the has_many association because that requires me to make a new book each time a user adds a book to their "bookshelf." I have trouble understanding the has_and_belongs_to_many relationship, is that what should be used here? I was not able to find any similar questions on SO, so any help is greatly appreciated.
I am using Rails 4 with Ruby 2.1.
Here is a drawing of what I would like to accomplish:
Drawing
Yes, you would have to define many-to-many relationship between a Bookshelf and a Book. There are two ways to achieve this in Rails:
Option 1) Use has_and_belongs_to_many
See guide
According to official documentation has_and_belongs_to_many association:
Specifies a many-to-many relationship with another class. This associates two classes via an intermediate join table. Unless the join table is explicitly specified as an option, it is guessed using the lexical order of the class names. So a join between Developer and Project will give the default join table name of “developers_projects” because “D” precedes “P” alphabetically.
So, your classes should look like this:
class Bookshelf < ActiveRecord::Base
has_and_belongs_to_many :books
end
class Book < ActiveRecord::Base
has_and_belongs_to_many :bookshelves
has_many :reviews
end
Add a join table generation to your migrations:
class CreateBooksBookshelvesJoinTable < ActiveRecord::Migration
def change
create_table :books_bookshelves, id: false do |t|
t.belongs_to :book, index: true
t.belongs_to :bookshelf, index: true
end
end
end
This will create a books_bookshelves table in your database. The table will have no primary key. There would be two foreign keys to your models Book and Bookshelf.
So, if you call self.books in the context of an user's bookshelf, you will get a list of books in the bookshelf. Vice versa, calling self.bookshelves in the context of a book will return a set of bookshelves the book belongs to.
The problem with this approach is that every time you add a new book to the bookshelf a new record is created in the database. If you are okay with that, there is no easier option than using has_and_belongs_to_many association. Otherwise, I recommend you to go with the Option #2.
Option 2) Use has_many :through
Another option is to use has_many, :through association (see guide). You would have to define one more model to do that, but it might come handy in some use cases (see below for an example).
Your classes should look like this:
class Bookshelf < ActiveRecord::Base
has_many :books, through: :books_bookshelves
has_many :books_bookshelves
end
class Book < ActiveRecord::Base
has_many :bookshelves, through: :books_bookshelves
has_many :books_bookshelves
has_many :reviews
end
class BooksBookshelf < ActiveRecord::Base
belongs_to :book
belongs_to :bookshelf
end
Probably the best thing about using has_many :through association is that it allows you to add custom columns to the join table (e.g. add column count to keep track how many books of the same type are there in the bookshelf).
The migration would look pretty much the same as the one we used in Option 1, except for the fact we are adding an unique constraint on the foreign keys (please note that adding the constraint is optional):
class CreateBooksBookshelvesJoinTable < ActiveRecord::Migration
def change
create_table :books_bookshelves, id: false do |t|
t.belongs_to :book, index: true
t.belongs_to :bookshelf, index: true
# add your custom columns here
end
add_index :books_bookshelves, [:book_id, :bookshelf_id], unique: true # to make sure you won't create duplicate records
end
end
By going with this approach, adding a new would be a bit more complicated as you would have to make sure you are not inserting duplicate records in the join table. (However, you may remove the unique constraint from the migration, to achieve exactly the same kind of behavior as you would get with has_and_belongs_to_many.)

Access attribute in Active Record 'through' association object

I have two classes, User and Product in a 'many-to-many through' association, using the class Prole (for product role).
class User < ActiveRecord::Base
has_many :proles
has_many :products, through: :proles
end
class Product < ActiveRecord::Base
has_many :proles
has_many :users, through: :proles
end
class Prole < ActiveRecord::Base
# has an attribute called 'role'
end
prole has an attribute called role which I'd like to use to qualify the user-product association.
The association works fine, but I can't figure out how to access the role attribute after creating the association. For example, if I do:
user.products << product
how can I access the attribute in the prole object just created?
I guess I could iterate through the prole objects and find the correct one, but I'm hoping there's a cleaner way.
Is this possible? Any hints?
TIA.
I was hoping for something a little more direct, but here's a
POSSIBLE ANSWER:
prole = Prole.find_by user_id: user.id, product_id: product.id
or even better
prole = user.proles.where("product_id = #{product.id}")
After some testing, it looks like the easiest way to grab specifically the Prole object that was just created is by querying by the two foreign keys directly against the Prole model, as suggested in your possible answer.
Prole.find_by(user_id: user.id, product_id: product.id)
If you want it as an association on the user object, you could use the includes approach to do eager loading, but it will still load every prole for the user in question
# specifying proles: {product_id: product.id} in the where clause here
# only limits users retrieved, not proles
user = User.includes(:proles).where(id: user.id)
# eager-loaded prole array
user.proles.find { |prole| prole.product_id == product.id }
See this answer for more info on that. But it looks like your possible answer is the cleanest way.

1 user model with student,tutor role + different profile for student, tutor?

How to correctly setup a User model with 2 roles, and have 2 separate profile models for each of the roles? Im confused on how to implement. Currently im using this but it fails:
models/user.rb
# id :integer ( only important columns noted to save space)
# profile_id :integer
# profile_type :string(255)
belongs_to :profile, :polymorphic => true
models/profile_student.rb:
# user_id :integer
has_one :user, as: :profile, dependent: :destroy
models/profile_tutor.rb:
# user_id :integer
has_one :user, as: :profile, dependent: :destroy
How to correctly get the profile for a user??
for example using devise.
#user = current_user.profile
I would try having two types of users: student and tutor. In order to do this, in your user table, have a column called type and put in a validation that ensures it is either student or tutor:
validates :type, :inclusion => {:in => ['student', 'tutor']}
Then create a Student model and a Tutor model. In rails, 'type' is a special kind of attribute in which rails will know that it is referring to other models. Then, in order to make profiles, you have two options. You can either say that both a student and a tutor has_one :profile, or you can separate the types of profiles.
For example, you can do:
class Student < User
has_one :profile
end
class Tutor < User
has_one :profile
end
If both profiles have similar types of information, that may work for you. However, if a tutor and student have considerably different profiles, try something like this:
class Student < User
has_one :student_profile
end
class Tutor < User
has_one :tutor_profile
end
and then create a separate model for each type of profile.
By using this 'type' column, you can make it so that students and tutors inherit all the methods and properties of users, but can also have their own distinct properties and methods.

Rails 3 scope with multiple models

Here are my models:
Patient:
has_many :patient_records
PatientRecord:
belongs_to :patient
has_many :progress_reports
ProgressReport:
has_one :patient_record
The query I am trying to produce is to get all patients where progress_reports are older than or equal to 7 days from now (using a date_of_report column in progress_reports) while including or joining the patient record table... I have been working on this for so long that I have ran into a brick wall.
I would try:
scope :recent_patients, lambda
{ |since_when| join(:progress_reports)
.where("progress_reports.created_at >= ?", since_when)}
in your Patient model
reports = ProgressReport.where(:created_at > 7.days.ago).all
if you want to get each patient that belongs to each record, do:
reports.each do |r|
puts r.patient.name
end

multiple belongs_to for a model

I have this model "Comment" which is given by a model "User" for a given "city" and "department".
While creating the schema for table "comments", I put in columns city_id, department_id and user_id which should act as foreign keys to respective ids in tables cities, departments and users.
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :city
belongs_to :department
end
Cities and Departments are independent tables which are populated with reference data (which would be used to populate in the forms.
When I try to access comment.city.name, I get a "undefined method `name' for nil:NilClass".
Table cities is defined with columns -"id", "name" and "symbol".
What is the root cause of this error?
What else do I need to do ? I have tried even by putting has_many :feedbacks in class City and class Department (even though it should not happen because they are independent of comments). I am missing something basic here, it seems.
Thanks,
Ashish
I think you need a has_many to go with every belongs_to. So each of your classes that comments belong_to (User, City, Department) should have
has_many :comments

Resources