Permitting params with custom keys - ruby-on-rails

I have a model names TeamInvite that I am trying to create a team_invite_params method for.
When I created the migration for the table, because I wanted to keep track of the sender and recipient, I opted for the custom field names
sender_id
and
recipient_id
to not mix up which user was which.
Anyway, when I try and permit the parameters for a team_invite, I am having trouble telling the permit method that the
Edit:
"team_invite"=>{"user"=>"2"}
is in fact what should go under recipient_id. All the methods I have tried either don't work or end up passing no value at all.
Controller
def create
#team_invite = TeamInvite.new(team_invite_params)
end
Model
class TeamInvite < ApplicationRecord
belongs_to :recipient, class_name: "User"
belongs_to :sender, class_name: "User"
end
Params:
{"utf8"=>"✓", "authenticity_token"=>"0QLA9YiTu9nAf6QJLX5rg0DWa7CAMjqGOlUBICbcL8Ucs2DsCDgGBnhiDXPa/ot8DK0xwTR1D7MASu4/9tVj0w==", "team_invite"=>{"recipient_id"=>"2"}, "commit"=>"Save Team invite"}
View (if it matters):
<%= form_for :team_invite, url: team_invites_path do |f| %>
<%= f.select :recipient_id, User.all.collect { |p| [ p.name, p.id ] }, include_blank: false %>
<%= f.submit %>
<% end %>
Migration:
class CreateTeamInvite < ActiveRecord::Migration[5.0]
def change
create_table :team_invites do |t|
t.references :team, foreign_key: true
t.integer :recipient_id
t.integer :sender_id
t.timestamps
end
end
end

You need:
params.permit(:team_invites => [ :user ]) #or whatever other nested team_invite params you want to pass in.
Docs for strong_params here: https://github.com/rails/strong_parameters

Related

Rails 4 - How can I map 2 columns of a Model to another column of another model

I want to map 2 columns of the same model (dev_status and test_planning_status) to another model's column (Status.name) and in the UserStory form I want to have a dropdown with values from Status table
I have tried something like this but unable to figure out
Status model is like this
class Status < ActiveRecord::Base
has_many :dev_status, :class_name => 'UserStory', :foreign_key => 'dev_status_id'
has_many :test_planning_status, :class_name => 'UserStory', :foreign_key => 'test_planning_status_id'
end
Currently I have this in models/UserStory
class UserStory < ActiveRecord::Base
validates :us_number, presence: true
validates :team_id, presence: true
validates :dev_status, presence:true
validates :test_status, presence:true
belongs_to :team
CreateUserStories migration is
class CreateUserStories < ActiveRecord::Migration
def change
create_table :user_stories do |t|
t.string :us_number
t.references :team
t.string :dev_status
t.string :test_planning_status
t.integer :tc_count
t.timestamps null: false
end
add_foreign_key :user_stories, :pod
end
My UserStoryController params is
def user_story_params
params.require(:user_story).permit(:us_number, :team_id, :dev_status, :test_planning_status)
end
UserStory _form is
<%= f.label :dev_status,'Dev Status' %>
<%= f.select :status, Status.all.map {|u|[u.status, u.id]},
{include_blank: true} %>
<%= f.label :test_planning_status, 'Test Planning Status' %>
<%= f.select :status, Status.all.map {|u|[u.status, u.id]},
{include_blank: true} %>
The goal should be to call UserStory.dev_status.name to get the dev_status name, and UserStory.test_planning_status.name to get the test_planning_status name.
Your migration should be creating columns dev_status_id (not dev_status) and test_planning_status_id (not test_planning_status).
Use t.references or t.belongs_to in your future migrations.
Above columns should be integers, not strings.
You need to specify belongs_to on the UserStory object for your status fields.
belongs_to :dev_status, class_name: 'Status'
belongs_to :test_planning_status, class_name: 'Status'
Change
validates :test_status, presence:true
to
validates :test_planning_status, presence:true
The two f.select :status in your form need to be changed to f.select :test_planning_status and f.select :dev_status
That should get you pointed in the right direction. Hope it helps!
This sounds like a standard has_many relationship::
class Status < ActiveRecord::Base
# columns id | name | value | other | information | created_at | updated_at
has_many :user_stories
end
class UserStory < ActiveRecord::Base
# columns id | title | value | dev_status_id | test_planner_status | created_at | updated_at
belongs_to :dev_status, class_name: :status
belongs_to :test_planning_status, class_name: :status
end
This would give you the ability to access the following:
#app/controllers/statuses_controller.rb
class UserStoriesController < ActionController::Base
def show
#story = UserStory.find params[:id]
##story.dev_status = gives you dev's details, with status value from Status table
end
end
If you wanted to avoid the law of demeter (IE only have one point to access your data), you'll want to use the delegate method:
#app/models/user_story.rb
Class UserStory < ActiveRecord::Base
delegate :name to: :dev_status, prefix: true
# this will allow you to call #user.dev_status_name
end
If you then wanted to have statuses changed, you'll be able to use the collection_select helper to get it working with the Status objects:
#app/views/user_stories/edit.html.erb
...
<%= f.collection_select :dev_status_id, Status.all, :id, :name, prompt: true %>
<%= f.collection_select :test_planner_status, Status.all, :id, :name, prompt: true %>
--
ActiveRecord
You must remember that models are built, they are just classes. Rails uses an ORM (ActiveRecord) to pull data to populate these classes.
Many people become confused about how models fit into the Rails ecosystem. A model is made of "attributes", which you have to populate, either manually or through the Rails ORM API. IE your User model could have the following:
#app/models/user.rb
class User < ActiveRecord::Base
def will_you_marry_me?
"no"
end
end
#app/views/application.html.erb
Will the user marry?
<%= #user.will_you_marry_me? %>
When you talk about "mapping" columns, what you're really asking is how to call a different table's data to attributes in your model class. For example, if you have User class, how to populate #user.birthday with data from profiles table etc.
The answer to that is to use the relational structure of ActiveRecord. Relational databases simply work with foreign_keys to load data from other tables. For example, you could have profiles table with user_id to get information about a specific user (see the image above).
ActiveRecord makes the process of loading "other table" data very simple. By using the relationships in the API, you can populate data with the likes of has_many etc.

has_and_belongs_to_many relationship in ruby on rails

I am trying to make a website for a tennis coach using ruby on rails. I am completely new to this and I am struggling with some of the terminology. The website has a login system where users can sign in and then sign up for different events that the tennis coach has created. So basically the users can go to many events and the events can have many users attending. I have made a has_and_belongs_to_many relationship between my users table and events table and the code is below:
Here is my migration:
class CreateEventsUsers < ActiveRecord::Migration
def change
create_table :events_users, :id => false do |t|
t.integer :event_id
t.integer :user_id
end
add_index :events_users, :event_id
add_index :events_users, :user_id
add_index :events_users, [:event_id, :user_id]
end
def self.down
drop_table :events_users
end
end
Here are my models:
class User < ActiveRecord::Base
has_and_belongs_to_many :events
end
class Event < ActiveRecord::Base
has_and_belongs_to_many :users
end
Here is my event form:
<li>
<%= link_to event.title, event %>
| <%= link_to "delete", event, method: :delete %>
| *insert sign up here*
</li>
Basically my question is, how would I make a form and a controller that would let a signed in user, sign up for one of the events in the database? I have been stuck on this for days. Any help would be greatly appreciated.
I would suggest using a has_many through instead of using has_and_belongs_to for scalability issues and is a suggested practice. Coming to your requirement, I did a similar thing but with users following other users and tried to adopt some of them for your requirement.
class Event < ActiveRecord::Base
has_many : Relationship
has_many : users, :through => :Relationships
end
class User < ActiveRecord::Base
has_many :Relationship
has_many :events, :through => :Relationships
//define other methods//
def follow!(event_id)
relationships.create!(event_id: event.id)
end
end
class Relationship < ActiveRecord::Base
attr_accessible :event_id
belongs_to :events
belongs_to :users
end
You need to create the relationship migration like this
class CreateRelationships < ActiveRecord::Migration
def change
create_table :relationships do |t|
t.integer :user_id
t.integer :event_id
t.timestamps
end
add_index :relationships, :user_id
add_index :relationships, :event_id
add_index :relationships, [:user_id, :event_id], unique: true
end
end
You can try adding a button on the events page view like this.
<%= form_for(current_user.relationships.build(evnt_id: #user.id)) do |f| %>
<div><%= f.hidden_field :event_id %></div>
<%= f.submit "Follow", class: "btn btn-large btn-primary" %>
<% end %>
Relationship controller can be something like this.
class RelationshipsController < ApplicationController
def create
#user = User.find(params[:relationship][:event_id])
current_user.follow!(#user)
respond_to do |format|
format.html { redirect_to #user }
end
end
end
This tutorial was very helpful for me in developing the application for users to follow other users. Hope it will help you too. Good luck !!
http://ruby.railstutorial.org/chapters/following-users#top
If you haven't discovered it already, you are in for a real treat if you checkout railscasts from Ryan Bates. Here is a tutorial on using checkboxes in a form with a HABTM association--actually he suggests, and you should consider it, moving to has_many, through relationship instead of HABTM.
Regardless, it should at least give you an idea of how you can make it work in your app. http://railscasts.com/episodes/17-habtm-checkboxes-revised. That episode may require a membership, however, the $9 bux/month is totally reasonable for what you're getting.

associations without the user_id column

I created a FriendRequest.rb model in my Rails app with the following table columns.
create_table "friend_requests", force: true do |t|
t.integer "requesting_user_id"
t.integer "requested_friend_id"
t.datetime "created_at"
t.datetime "updated_at"
end
With relationships defined as you see below, I added this code to a /views/users/show.html.erb page show the friend requests that have been made for each user. However, I'm getting this error
PG::UndefinedColumn: ERROR: column friend_requests.user_id does not exist
because, I obviously didn't create a user_id column. Is there a way that I can make this code work by adding more information to the relationships? or should I scrap my work and do it differently?
<% for user in #user.friend_requests %>
<li><%= h user.name %></li>
<%= link_to "Add Friend", friend_requests_path(:friend_id => user), :method => :post %>
<% end %>
User.rb
has_many :friend_requests
FriendRequest.rb
belongs_to :user
Just change your has_many association for:
has_many :friend_requests, foreign_key: 'requesting_user_id'
By default, Rails will look for [model_name]_id in the other table, this is why it is looking for user_id, but by adding the foerign_key: option, you can override this default behaviour and tell Rails what is the name of the foreign_key you want to use.
You can use this configuration:
class User < ActiveRecord::Base
has_many :friend_requests
has_many :requesters, through: friend_requests
end
class FriendRequest < ActiveRecord::Base
belongs_to :requester, foreign_key: 'requesting_user_id'
belongs_to :requested, foreign_key: 'requested_friend_id'
validates :requester_id, presence: true
validates :requested_id, presence: true
end
Take a look at:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
Look especially for the options :primary_key and :foreign_key

HABTM Form not working in rails. Not submiting into Join tables

I am trying to assign a team to a player. A player can have many teams but upon initial set up they only have one. I am trying to set up a choice so the user can pick a team.
When I submit the form the data for the other fields, first_name and last_name submit fine however no data gets saved in the join table. I am using a select box however check boxes will do the job too if someone can think of that.
Models
I have 2 models. Players and Teams set up with a HABTM relationship.
class Player < ActiveRecord::Base
has_and_belongs_to_many :teams
class Team< ActiveRecord::Base
has_and_belongs_to_many :players
Controller
def new
#player = Player.new
end
def create
#player = Player.new(params[:player])
if #player.save
flash[:notice] = "Player Created"
redirect_to(:action =>'list')
else
render('new')
end
end
Join table
I can insert data into the the join table from the rails console. I can then get this data and show it. So the relationships work.
create_table "players_teams", :id => false, :force => true do |t|
t.integer "player_id"
t.integer "team_id"
end
add_index "players_teams", ["player_id", "team_id"], :name => "index_players_teams_on_player_id_and_team_id"
View
In my view I have this
<%= f.collection_select(:team_id, Team.all, :id, :name, :prompt => true) %>
This loads a view with the teams Populated. Once submitted I get
Parameters: {"utf8"=>"✓", "authenticity_token"=>"kvOmx3G5H1mqLMnEn6HS3a79+WQnIzfsUA3Dt0XHo1w=", "player"=>{"first_name"=>"Test", "last_name"=>"Data", "email"=>"email#email.com"}, "teams"=>{"team_id"=>"1"}, "commit"=>"create player"}
I am not sure where to go from here.
Where on the form are you putting the collection? Maybe it is a typo but is should be
<%= form_for(#player) do |f| %>
...
<%= f.collection_select(:team_ids, Team.all, :id, :name, :prompt => true) %>
...
<% end %>
and you should get
Parameters: {
"utf8"=>"✓", "authenticity_token"=>"kvOmx3G5H1mqLMnEn6HS3a79+WQnIzfsUA3Dt0XHo1w=",
"player"=>{
"first_name"=>"Test",
"last_name"=>"Data",
"email"=>"email#email.com",
"team_ids"=>["1"]
},
"commit"=>"create player"
}
which means that the team_ids attribute gets set by the form.
Your parameter is incorrect. Since a player has_and_belongs_to_many :teams, the proper parameter is team_ids. However, that should be an array, so you need something like this:
<%= f.collection_select("team_ids[]", Team.all, :id, :name, :prompt => true) %>
I believe that should do it.

ROR Self referential has_many through with accept_nested_attributes_for

I wondered if someone could take a quick look at this. I'm making a simple conversion application which converts between units of measurement. I need to be able to self reference the table using a join table which stores the relationship between each, along with the conversion between each. This then would be referenced between either side of the relationship. For example 1cm = 10mm and 10mm = 1cm.
So thus far I have this:
#migrations
create_table :measures do |t|
t.string :name
end
create_table :measure_measures do |t|
t.integer :measure_id
t.integer :related_measure_id
t.integer :conversion
end
class Measure < ActiveRecord::Base
has_many :related_measures,
:foreign_key => 'measure_id',
:class_name => 'MeasureMeasure',
:dependent => :destroy
has_many :measures, :through => :related_measures
accepts_nested_attributes_for :related_measures,
:reject_if => proc { |attrs| attrs['related_measure_id'].blank? ||
attrs['quantity'].blank? },
:allow_destroy => true
end
#controller
#measure = Measure.find params[:id
#form
<% form_for #measure do |f| %>
<% fields_for :related_measures do |f_r_m| %>
<%= f_r_m.text_field :related_measure_id -%>
<%= f_r_m.text_field :quantity -%>
<% end %>
<% end %>
For the most part this works ok. Except I cannot access the name of the related measure, only the owner.
I need to get it somehow like this:
f_r_m.object.related_measure.name
but clearly despite my best efforts i cannot set it up and receive the error.
undefined method `owner_measure' for #<MeasureMeasure:0x1053139a8>
Help would be very much appreciated. :)
At first glance the problem comes from the has many through definition. By failing to define the foreign key, Rails, assumes measure_id in the join table. Which would just link back to the measure you're trying to find it from.
has_many :measures, :through => :related_measures, :foreign_key => :related_measure_id
We can't diagnose this error without seeing the innards of the join model.
f_r_m.object.related_measure refers to join table.
But I suspect it's because you haven't defined the relationship properly in the join model. Which should look something like this:
class MeasureMeasures < ActiveRecord::Base
belongs_to :measure
belongs_to :related_measure, :classname => "Measure"
end

Resources