rails add record to has_many :through join table - ruby-on-rails

class EventTeam < ActiveRecord::Base
belongs_to :event
belongs_to :team
end
class Event < ActiveRecord::Base
has_many :event_teams
has_many :teams, through: :event_teams
end
class Team < ActiveRecord::Base
has_many :event_teams
has_many :events, through: :event_teams
end
I am trying to add the :event_id and :team_id to the EventTeam join table when creating a new Event and can't seem to figure out how, despite an exhaustive search of similar questions such as: how to add records to has_many :through association in rails (I've tried all of these suggestions)
It seems that the following should work, though a NoMethodError is delivered: "undefined method `events' for #ActiveRecord::Relation []"
EventsController
def new
#event = Event.new(:team_id => params[:team_id])
end
def create
#team = Team.where(:id => params[:team_id])
#event = #team.events.create(event_params)
if #event.save
flash[:success] = "Event created!"
redirect_to #event
else
render 'new'
end
end
I have a similar situation in the same app with Users, Teams, and Memberships (join table). The following code automatically adds the :team_id and :user_id to the Memberships table when a user creates a new Team.
TeamsController
def new
#team = Team.new(:user_id => params[:user_id])
end
def create
#team = current_user.teams.create(team_params)
if #team.save
flash[:success] = "Team created!"
redirect_to #team
else
render 'new'
end
end
Any suggestions on how to accomplish this?

undefined method `events' for #ActiveRecord::Relation []
where returns an AR relation not a single instance, so #team.events won't work. Use find instead
#team = Team.find(params[:team_id])
#event = #team.events.create(event_params)
Update
could not find Team with 'id'=
You are getting team_id inside event hash, so params[:team_id] won't work. You need to use params[:event][:team_id]
#team = Team.find(params[:event][:team_id])
#event = #team.events.create(event_params)

Just specify first value of the relation, since you are searching by unique index with value id, so that should be well:
#team = Team.where(id: params[:team_id]).first
#event = #team.events.create(event_params)
That is because .where, unlike find_by or find(1) returning a Relation, not a first value in it.
However, in modern version of rails I saw recommendation to use exactly where.first pair, not a find.

Related

"ActiveModel::ForbiddenAttributesError " on Rails App ( 5) with nested attributes

I have a simple_form_for that creates an invoice. Through this form, I want the user to be able to create a client that will be associated with that before-mentionned invoice. The current process being to firstly create the client, then associate it to the invoice by selecting it from a collection of created clients when the user create an invoice.
My models :
class Client < ApplicationRecord
has_many :invoices
end
class Invoice < ApplicationRecord
belongs_to :client
accepts_nested_attributes_for :client, reject_if: :all_blank, allow_destroy: true
end
Invoice controller:
def new
#invoice = Invoice.new
#invoice.build_client
end
def create
#invoice = Invoice.new(invoice_params)
#client = #invoice.build_client(params[:invoice][:client_attributes])
#client.user = current_user
#client.save
end
And I made sure to update my strong params in Invoice Controller with :
params.require(:invoice).permit(:param1, :param2,client_attributes:[:param1, :param2, :param3, etc..],..)
That being said, when creating an invoice, I ran into an "ActiveModel :: ForbiddenAttributesError", which is set to appears when strong params are not correctly defined. Which, in my case, does not seem to be the case.
I found out that adding "params.permit!" in my #Create in the Invoice Controller, allowed me to avoid that error. But that's a trick. It should not be necessary since that's the jobs of the strong params. Has anyone ever came across a similar case, please?
Ok, so I figured this thing out. All that was needed to do was to - obviously- save my client before, my invoice. Rather simple, isn't it!
Here is my final Invoice #New #Create
def new
#invoice = Invoice.new
#invoice.build_client
end
def create
#invoice = Invoice.new(invoice_params)
#invoice.client.user = current_user
#invoice.client.save
if #invoice.save
redirect_to #invoice
else
render :new
end
end

Rails with ActionMailer and complex activerecord (joins, where, and)

I would like to send an email when a project is registered (with a category and a eligible audience) to each user who has created an alert (with the same category and same eligible audience).
MODEL
class Project
belongs_to :category
belongs_to :fondation
has_many :project_eligibles
has_many :eligibles, through: :project_eligibles
end
class Category
has_many :projects
has_many :alerts
end
class ProjectEligible
belongs_to :project
belongs_to :eligible
end
class Alert
belongs_to :user
belongs_to :category
belongs_to :eligible
end
MAILER
class ProjectMailer < ApplicationMailer
def newproject(project)
#project = project
mail(
to: mails = User.joins(:alerts).where(alerts: {category_id: project.category_id}).collect(&:email).join(","),
subject: "New project for you !"
)
end
end
CONTROLLER
class ProjectsController < ApplicationController
def create
#project = Project.new(project_params)
if #project.save
ProjectMailer.newproject(#project).deliver_now
redirect_to projects_path
else
render :new
end
end
end
it works with the category but I can't do it with the eligible audience (many_to_many association) :
def newproject(project)
#project = project
mail(
to: mails = User.joins(:alerts).where(alerts: {eligible_id: project.project_eligibles.where(:eligible_ids)}).collect(&:email).join(","),
subject: "New project for you !"
)
end
end
And more difficult, I don't know how to do it with the 2 conditions ??
Does anyone have an idea to test?
A thousand thanks in advance for your help!
Try this in your ProjectMailer
project_elegibles = ProjectEligible.whare("project_id =?", #project.id).pluck(:eligible_id)
User.includes(:alerts).where("alerts.category_id =? and alerts.eligible_id IN (?)", #project.category_id, project_elegibles).pluck(:email)
Understand steps that i have used to deal with many-to-many relationship. I can't test this in my machine as I don't have the code. But this will helpful to fix your problem.
Assuming project = #project
mails = User.joins(:alerts).where("alerts.eligible_id IN (?) AND alerts.category_id = ?", project.project_eligibles.pluck(:eligible_id), project.category_id).pluck(:"users.email")
Possibly you should use left_outer_join
For rails 5.x.x
mails = User.left_outer_joins(:alerts).where("alerts.eligible_id IN (?) AND alerts.category_id = ?", project.project_eligibles.pluck(:eligible_id), project.category_id).pluck(:"users.email")

How to associate Models using one to one relationship

Given below are two models associated with one to many relationship and it works fine
class User < ActiveRecord::Base
has_many :events, dependent: :destroy
end
but when I associate them one to one
class User < ActiveRecord::Base
has_one :event, dependent: :destroy
end
It gives me the following error
undefined method `build' for nil:NilClass
Events Controller
class EventsController < ApplicationController
def new
#event = current_user.event.build
end
def create
#event = current_user.event.build(event_params)
if #event.save
redirect_to #event
else
render 'new'
end
end
private
def event_params
params.require(:event).permit(:date, :time, :venue)
end
end
The reason it is throwing the error is because there is no event for that user. While that is how to build through association for a has_many relationship, it doesn't work for has_one. See the documentation on has_one where they say that calling .build will not work.
Instead use #event = current_user.create_event
Adding a has_one relationship will give you the following methods:
association(force_reload = false)
association=(associate)
build_association(attributes = {})
create_association(attributes = {})
create_association!(attributes = {})
For has_one associations, Rails provides a special set of methods for building the association (documentation)
build_event
create_event
Both of these take a hash of attributes just like the build method of a has_many association does.
In your case, change current_user.event.build to current_user.build_event and current_user.event.build(event_params) to current_user.build_event(event_params)

Rails - can't tell create controller which item to create

I have several models:
User
Relationship
Project
Schedule
A user instance can be of type :student or :employer.
A schedule belongs_to a project and
a project has_one schedule
project belongs_to a student and a student has_many projects.
A relationship belongs_to Student and relationship also belongs_to Employer. Employer and Student both has_many relationships.
I am writing the controller to create a schedule and I can't figure out how to tell the controller which project the schedule belongs to. Here is what I have so far
def create
if current_user.type == 'Employer'
redirect_to employer_profile_path(current_user.profile_name)
else
#schedule = Schedule.find(params[:id])
if #schedule.save(schedule_params)
flash[:notice] = "Successfully created schedule."
redirect_to profile_path(current_user.profile_name) #change to project path later
else
render :action => 'new', :notice => 'Invalid Schedule'
end
end
end
private
def schedule_params
params.require(:schedule).permit(tasks_attributes: [:title, :content, :_destroy])
end
I am pretty sure I am defining #schedule incorrectly. The routes for schedule are:
resources :schedules
How do I tell the controller which Project the schedule belongs to?
Assuming you posted the create action for the SchedulesController there is no need for:
#schedule = Schedule.find(params[:id])
this will probably return nil, because there is no schedule (after all that is what you want to create here).
#schedule = Schedule.new(schedule_params)
if you want to assign a project to the schedule, you need to submit this information so you can access it in the params hash. Something like:
#schedule.project = Project.find(params[:project_id])

Changing Data from an other model

I am wondering what kind of query should I accept to allow my data to be updated. My models consists of client, interest, and a manager
Clients his has follow
id
name
email
password
Interest
id
description
manager
customer_id
interest_id
created_at
The goal of the manager his not to override old data in interest but just keep adding a new interest and refering to it.
The relationship his has follow
class Client < ActiveRecord::Base
has_many :music_interest_managers
has_many :music_interests, through => :music_interest_managers
end
class MusicInterest < ActiveRecord::Base
has_many :music_interest_managers
has_many :clients, through => :music_interest_managers
end
class MusicInterestManager < ActiveRecord::Base
belongs_to :music_interests
belongs_to :client
end
Now to update the data from the customer controller i am not sure how would i do this
This is what i am thinking about:
#client = Client.find(params[:id])
#manager = #client.manager.build(params[:manager])
#interest = #interest.manager.build(params[:interest])
Does this make sense? or i am dead wrong?
Update:
def update
#client = Client.find(params[:id])
#interest = #client.music_interests.build(params[:interest])
if #client.update_attributes(params[:client])
flash[:success] = "Profile updated"
#sign_in #client
redirect_to #client
else
render 'edit'
end
end
Or should i render a model view from interest to then apply the change?
#client = Client.find(params[:id])
#interest = #client.music_interests.build(params[:interest])
should work - try it out in the console!

Resources