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])
Related
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
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.
I am trying to make an app in Rails 4.
I just asked this related question and got a clear answer. It seems I can't understand how to take that logic and apply it elsewhere.
Rails How to show attributes from a parent object
I have a user model, profile model a projects model and a universities model.
Associations are:
Profile belongs to university
Profile belongs to user
University has many profiles
University has many projects
Projects HABTM user
Projects belong to universities
In my projects controller, I define #creator as follows:
def create
logger.debug "xxx create project"
#authorise #project
#project = Project.new(project_params)
#project.creator_id = current_user.id
#project.users << current_user
respond_to do |format|
if #project.save
format.html { redirect_to #project }
format.json { render action: 'show', status: :created, location: #project }
else
format.html { render action: 'new' }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
I try to define creator_profile like this:
def show
#authorise #project
#project = Project.find(params[:id])
#creator = User.find(#project.creator_id)
#creator_profile = #creator.profile
end
In my uni table, I have attributes called logo and name. I use avatar uploader in which i have logo defined (that's why I have two .logo below).
In my projects, show, I want to display the university that the project creator belongs to.
I've tried this:
<%= image_tag(#creator_profile.university.logo.logo) %>
<div class="generaltext"><%= #creator_profile.university.name %> </div>
I get this result: undefined method `logo' for nil:NilClass
Based on the link to my problem above
<%= image_tag(creator_profile.university.logo.logo) %>
<div class="generaltext"><%= creator_profile.university.name %> </div>
I get this result:
undefined local variable or method `creator_profile' for #<#<Class:0x007f998f17ad88>:0x007f998d1ce318>
I'm not sure I understood the very detailed explanations given in the answer to my previous question. If the first version is right, then I don't understand the explanation at all. If the second version is right, then why does this error message come up?
Im wondering if the problem arises out of there not being an association between university and user? I was hoping, based on the user who created the project, to find the uni that the creator belongs to.
That's why i tried:
<%= image_tag(creator_profile.project.university.logo.logo) %>
<div class="generaltext"><%= creator_profile.project.university.name %> </div>
I get this error:
undefined method `project' for #<Profile:0x007f998ada41b8>
I think that you need to understand some basic concepts of Ruby and Ruby and Rails to solve this question yourself.
In ruby, vars with # are instance variables and are available all over the class. That means that they will be available in your view if you declare them in your controller.
EG #creator_profile = #profile.user
On the other hand, vars without # are only available inside the same block.
An example:
#controller
#users = User.all ##users, instance variable
#view
<% #users.each do |user| %>
<h3><%= user.name %></h3> #user, local variable. This will work
<% end %>
<h3><%= user.name %></h3> #this won't work because it is outside the block
Google about ruby vars and scopes.
Also, I think that you are relying too much on 'rails magic' (or you are skipping some code lines), if you don't declare an instance var, it won't exist. Naming conventions don't work that way.
At last but not at least, having a look at your relations, I think that they need some refactor. Also the use of singular and plural is not correct. I know that it's not real code but it denotes that they don't reflect real relationships between entities.
Don't try to make 'octopus' models, where everybody belongs to everybody, and think about the relationships itself, not only trying to associate models. EG:
Profile
belongs_to :creator, class_name: 'User'
This way you can write:
#controller
#profile_creator = Profile.find(params[:id]).creator
#view
#profile_creator.university
You will understand better what you are doing.
Hope it helps.
It seems I can't understand how to take that logic and apply it elsewhere.
I don't think you appreciate how ActiveRecord associations work in Rails. I'll explain further down the page.
Your associations will be the likely cause of the problem.
Setting up complicated associations is always tricky - it's best to keep the data as separate as possible.
Here's how I'd construct the models / associations:
#app/models/university_student.rb
class UniversityStudent < ActiveRecord::Base
belongs_to :university
belongs_to :student, class_name: "User" #-> student_id
end
#app/models/user.rb
class User < ActiveRecord::Base
has_many :placements, class_name: "UniversityStudent", foreign_key: :student_id #-> user.placements
has_many :universities, through: :placements #-> user.universities
has_and_belongs_to_many :projects #-> user.projects
has_one :profile #-> user.profile (avatar etc)
has_many :created_projects, class_name: "Project", foreign_key: :creator_id
end
#app/models/profile.rb
class Profile < ActiveRecord::Base
belongs_to :user #-> store avatar here. This can be used across entire app
end
#app/models/university.rb
class University < ActiveRecord::Base
has_many :projects
has_many :students, class_name: "UniversityStudent" #-> university.students
end
#app/models/project.rb
class Project < ActiveRecord::Base
belongs_to :university
belongs_to :creator, class_name: "User" #-> creator_id
has_and_belongs_to_many :users
delegate :profile, to: :creator, prefix: true #-> #project.creator_profile
end
This allows you to do the following:
def create
#project = curent_user.created_projects.new project_params
#project.users << current_user
Because the associations actually associate your data, you'll be able to do the following:
def show
#project = Project.find params[:id]
##creator_profile = #project.creator.profile
#creator_profile = #project.creator_profile #-> if you use the delegate method outlined in the models
end
--
In my projects, show, I want to display the university that the project creator belongs to.
#app/controllers/projects_controller.rb
class ProjectsController < ApplicationController
def show
##project = Project.find params[:id]
#project = current_user.created_projects.find params[:id]
end
end
#app/views/projects/show.html.erb
<%= #project.creator.universities.first %>
My code above allows for multiple universities. Thinking about it, it should be limited to one, but I'll leave it as is for now, maybe change it later.
In my uni table, I have attributes called logo and name. I use avatar uploader in which i have logo defined (that's why I have two .logo below).
Don't use two logo method, it's an antipattern (explained below)
The fix for this is two-fold:
Firstly, make sure you're calling #creator_profile.university with the following:
<%= #creator_profile.university %>
If this works, it means you have a problem with .logo.logo (detailed below), if it doesn't, it means you've not defined #creator_profile or the university association correctly.
Secondly, you need to ensure you have the correct controller/view setup.
The problem for many people - especially beginners - is they simply don't understand the way Rails works with controllers & views. You need to appreciate that each time you render a view, the only data it has access to is that which you define in the corresponding controller action...
#app/controllers/projects_controller.rb
class ProjectsController < ApplicationController
def show
#project = Project.find params[:id]
#creator_profile = #project.creator_profile
end
end
#app/views/projects/show.html.erb
<%= content_tag :div, #creator_profile.universities.first.name, class: "generaltext" %>
Trivia
#project.creator_id = current_user.id
This should not have to be defined.
You should be able to change the foreign_key in the association, so that Rails will automagically define the creator_id for you:
#app/models/project.rb
class Project < ActiveRecord::Base
belongs_to :creator, class: "User" #-> foreign_key should be :creator_id
end
#app/controllers/projects_controller.rb
class ProjectsController < ApplicationController
def create
#project = current_user.created_projects.new project_params #-> populates foreign key automatically.
--
.logo.logo
This is an antipattern.
Calling the same method twice is simply bad practice - why are you doing it?
You either want to delegate any recursive data you're trying to access (such as the example with .creator_profile above), or you'll want to restructure that functionality.
You want the following:
If you have to delegate to an assets model, you could get away with the following:
<%= #creator_profile.university.images.logo %>
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!
class Party < ActiveRecord::Base
belongs_to :hostess, class_name: 'Person', foreign_key: 'hostess_id'
validates_presence_of :hostess
end
class Person < ActiveRecord::Base
has_many :parties, foreign_key: :hostess_id
end
When creating a new Party, the view lets the user select an existing Hostess, or enter a new one. (This is done with jQuery autocomplete to look up existing records.) If an existing record is chosen, params[:party][:hostess_id] will have the correct value. Otherwise, params[:party][:hostess_id] is 0 and params[:party][:hostess] has the data to create a new Hostess (e.g., params[:party][:hostess][:first_name], etc.)
In the Parties controller:
def create
if params[:party][:hostess_id] == 0
# create new hostess record
if #hostess = Person.create!(params[:party][:hostess])
params[:party][:hostess_id] = #hostess.id
end
end
#party = Party.new(params[:party])
if #party.save
redirect_to #party, :notice => "Successfully created party."
else
#hostess = #party.build_hostess(params[:party][:hostess])
render :action => 'new'
end
end
This is working fine when I pass in an existing Hostess, but it's not working when trying to create the new Hostess (fails to create the new Hostess/Person and thus fails on creating the new Party). Any suggestions?
Given the models you provided, you can have this setup in a cleaner way using a few rails tools like inverse_of, accepts_nested_attributes_for, attr_accessor, and callbacks.
# Model
class Party < ActiveRecord::Base
belongs_to :hostess, class_name: 'Person', foreign_key: 'hostess_id', inverse_of: :parties
validates_presence_of :hostess
# Use f.fields_for :hostess in your form
accepts_nested_attributes_for :hostess
attr_accessor :hostess_id
before_validation :set_selected_hostess
private
def set_selected_hostess
if hostess_id && hostess_id != '0'
self.hostess = Hostess.find(hostess_id)
end
end
end
# Controller
def create
#party = Party.new(params[:party])
if #party.save
redirect_to #party, :notice => "Successfully created party."
else
render :action => 'new'
end
end
We're doing quite a few things here.
First of all, we're using inverse_of in the belongs_to association, which allows you to validate presence of the parent model.
Second, we're using accepts_nested_attributes_for which allows you to pass params[:party][:hostess] into the party model and let it build the hostess for you.
Third, we're setting up an attr_accessor for :hostess_id, which cleans up controller logic quite a bit, allowing the model to decide what to do whether it receives hostess object or the hostess_id value.
Fourth, we're making sure to override hostess with an existing hostess in case we got a proper hostess_id value. We do this by assigning hostess in the before_validation callback.
I didn't actually check if this code works, but hopefully it reveals enough information to solve your problem and exposes more helpful tools lurking in rails.