Rails insert into db with has_many through - ruby-on-rails

How can i add data in my database with the following model?
class User < ActiveRecord:Base
has_many :user_groups
has_many :groups, through: :user_groups
class Group < ActiveRecord:Base
has_many :user_groups
has_many :users, through: :user_groups
class UserGroup < ActiveRecord:Base
belongs_to :user
belongs_to :group
Now i'm using this code to save the new record
#group_controller
def create
#group = current_user.groups.build(params...)
if #group.save
#my redirect etc...
end
Well, this will create an row in my group table with the corrects parameters etc but my join table is still empty... why? Can anybody explain these? ;)

because you are building the group and saving it, but not the relationship.
if #group.save
current_user.groups << #group
will fix it.

Related

Using rails counter_cache on a non-standard has_many :through

so the standard way to use a has_many :through association would be to use the Physician-Appointment-Patient model from the Active Record Association Guide, right? It's basically a more verbose HABTM, with two models having a has_many :through and the connector model having two belongs_to. Now, in my current project I have a model structure like this:
class Cart < ActiveRecord::Base
has_many :line_items, through: line_item_groups
has_many :line_item_groups
end
class LineItemGroup < ActiveRecord::Base
belongs_to :cart
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :line_item_group
has_one :cart, through: line_item_group
end
Works fine. But now I want a line_item_count on Cart and can't figure out where I should add the counter_cache attribute.
Any help is appreciated :-)
First add line_item_count field in carts table, then in LineItem model add
class LineItem < ActiveRecord::Base
before_create :increment_counter
before_destroy :decrement_counter
def increment_counter
Cart.increment_counter(:line_item_count, cart.id)
end
def decrement_counter
Cart.decrement_counter(:line_item_count, cart.id)
end
end
I didn't tried it, but I think it will solve your problem.

Rails: ActiveRecord has_many association not working

I'm a bit new to Rails Active Record associations. I've tried to set up a relationship, but I get ActiveRecord error when I try to retrieve data. Did I associate my models incorrectly?
User has many Uploads, which has many UserGraphs:
class User < ActiveRecord::Base
has_many :uploads, through: :user_graphs
end
class Upload < ActiveRecord::Base
has_many :users, through: :user_graphs
end
class UserGraph < ActiveRecord::Base
belongs_to :user
belongs_to :upload
end
I want to get all of a user's uploads, and all of a user's graphs. The 2nd line doesn't work in rails console and gives an error
#user = User.find(1)
#uploads = #user.uploads
The error:
ActiveRecord::HasManyThroughAssociationNotFoundError: Could not find the association :user_graphs in model User
Extra Credit:
If Users have Uploads that have UserGraphs... shouldn't it be has_many :uploads and has_many :user_graphs, through :uploads?
Add
has_many :user_graphs
to the User and Upload classes.
The :through option defines a second association on top of this one.
You didn't tell Rails that you have a user_graphs association on User, only an uploads association. So when Rails goes to follow the user_graphs association on uploads, it can't find it.
So, you need add the user_graphs association. Your models should look like this:
class User < ActiveRecord::Base
has_many :user_graphs # <<< Add this!
has_many :uploads, through: :user_graphs
end
class Upload < ActiveRecord::Base
has_many :user_graphs # <<< Add this!
has_many :users, through: :user_graphs
end
class UserGraph < ActiveRecord::Base
belongs_to :user
belongs_to :upload
end

How to get related records across a join table in Rails?

In my Rails application I have people which can have many projects and vice versa:
# app/models/person.rb
class Person < ActiveRecord::Base
has_many :people_projects
has_many :projects, :through => :people_projects
end
# app/models/people_project.rb
class PeopleProject < ActiveRecord::Base
belongs_to :person
belongs_to :project
end
# app/models/project.rb
class Project < ActiveRecord::Base
has_many :people_projects
has_many :people, :through => :people_projects
def self.search(person_id)
if person_id
where("person_id = ?", person_id) # not working because no person_id column in projects table
else
scoped
end
end
end
How can I filter the projects by person_id in the index view of my ProjectsController, e.g. by using a URL like this: http://localhost:3000/projects?person_id=164
I can't get my head around this. Please help! Thanks...
Your association definition is not complete for Person and Project models. You also need has_many :people_projects defined.
# app/models/person.rb
class Person < ActiveRecord::Base
has_many :people_projects # <-- This line
has_many :projects, :through => :people_projects
end
# app/models/project.rb
class Project < ActiveRecord::Base
has_many :people_projects # <-- This line
has_many :people, :through => :people_projects
end
# app/models/people_project.rb
# This is defined correctly
class PeopleProject < ActiveRecord::Base
belongs_to :person
belongs_to :project
end
Please reference The has_many :through Association for further details.
With this definition, you will be able to get all the projects of the current user using current_user.projects, just like you've already done in your ProjectsController#index.
Update:
You could use either joins or includes in your search method and apply the where condition. Something like follows:
# app/models/project.rb
class Project < ActiveRecord::Base
has_many :people_projects
has_many :people, :through => :people_projects
def self.search(person_id)
if person_id
includes([:people_projects, :people]).where("people.id = ?", person_id)
else
scoped
end
end
end
You will not have a person_id in the projects table because its a has_many<>has_many relationship.
Simply #person.projects will perform a join btw person_projects & projects tables and returns the appropriate projects.
*I assume,current_user returns a Person object.*
Also, complete your Model definitions. Each of them should list their relation to PeopleProjects
class Person < ActiveRecord::Base
has_many :people_projects
has_many :projects, :through => :people_projects
end
class Project < ActiveRecord::Base
has_many :people_projects
has_many :people, :through => :people_projects
end

Adding and removing from a has_many :through relation

From the Rails associations guide, they demonstrate a many-to-many relationship using has_many :through like so:
class Physician < ActiveRecord::Base
has_many :appointments
has_many :patients, :through => :appointments
end
class Appointment < ActiveRecord::Base
belongs_to :physician
belongs_to :patient
end
class Patient < ActiveRecord::Base
has_many :appointments
has_many :physicians, :through => :appointments
end
How would I create and remove appointments?
If I've got a #physician, do I write something like the following for creating an appointment?
#patient = #physician.patients.new params[:patient]
#physician.patients << #patient
#patient.save # Is this line needed?
What about code for deleting or destroying? Also, if a Patient no longer existed in the Appointment table will it get destroyed?
In your code of creating an appointment, the second line is not needed, and using #build method instead of #new:
#patient = #physician.patients.build params[:patient]
#patient.save # yes, it worked
For destroying an appointment record you could simply find it out and destroy:
#appo = #physician.appointments.find(1)
#appo.destroy
If you want to destroy the appointment records along with destroying a patient, you need to add the :dependency setting to has_many:
class Patient < ActiveRecord::Base
has_many :appointments
has_many :physicians, :through => :appointments, :dependency => :destroy
end

has_many :through default values

I have a need to design a system to track users memberships to groups with varying roles (currently three).
class Group < ActiveRecord::Base
has_many :memberships
has_many :users, :through => :memberships
end
class Role < ActiveRecord::Base
has_many :memberships
has_many :users, :through => :memberships
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :role
belongs_to :group
end
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, :through => :memberships
end
Ideally what I want is to simply set
#group.users << #user
and have the membership have the correct role. I can use :conditions to select data that has been manually inserted as such :
:conditions => ["memberships.role_id= ? ", Grouprole.find_by_name('user')]
But when creating the membership to the group the role_id is not being set.
Is there a way to do this as at present I have a somewhat repetitive piece of code for each user role in my Group model.
UPDATED
It should be noted what id ideally like to achieved is something similar to
#group.admins << #user
#group.moderators << #user
This would create the membership to the group and set the membership role (role_id ) appropriately.
You can always add triggers in your Membership model to handle assignments like this as they are created. For instance:
class Membership < ActiveRecord::Base
before_save :assign_default_role
protected
def assign_default_role
self.role = Role.find_by_name('user')
end
end
This is just an adaptation of your example.

Resources