Devise + Polymorphic association questions - ruby-on-rails

I'm fairly new to Rails, and having an issue understanding where I'm missing something.
I'm using Rails 4, Devise and I have a main User model that holds just password, email, and user_name that are common fields to my additional models: Artist, Fan.
I have the relationships set up as so:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :account, polymorphic: true
validates_uniqueness_of :user_name
end
class Artist < ActiveRecord::Base
has_one :user, as: :account, dependent: :destroy
has_many :tracks, dependent: :destroy
accepts_nested_attributes_for :user
end
class Fan < ActiveRecord::Base
has_one :user, as: :account, dependent: :destroy
accepts_nested_attributes_for :user
end
And there is also one model that belongs_to Artist:
class Track < ActiveRecord::Base
belongs_to :artist
end
I have managed to get the polymorphic connections working, creating an Artist and it's user on creation. Now I am trying to create the Artist/Track connection.Currently I'm a getting it to
create the new Song, but it's not making the association on create. I'm getting a Track with no associated Artist_id = nil
My Song Controller looks something like this:
class TracksController < ApplicationController
before_action :authenticate_user!, :except => [:index, :show]
before_action :set_track, only: [:show, :edit, :update, :destroy]
def new
#track = Track.new
#track.build_artist
end
def create
#track = Track.new(track_params)
if #track.save
flash[:notice] = "Track was successfully created."
redirect_to #artist
else
redirect_to new_artist_track_path
end
end
Also... after successful create redirect_to #artist gives me an ActionControllerError of
"Cannot redirect to nil!
Any help would be appreciated
Edit: solved the redirect_to issue with changing redirect_to #artist to
redirect_to artist_path(current_user)
Still can't figure out how to save the association with Artist in Track on create
Edit #2: Here is my track_params
def track_params
params.require(:track).permit(:track_title, :description, :track_type)
end
Edit #3:
Here's the form:
<%= bootstrap_form_for #track, :html => { :multipart => true } do |f| %>
<div class="field">
<%= f.label :track_title %><br>
<%= f.text_field :track_title %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_field :description %>
</div>
<div class="field">
<%= f.label :track_type %><br>
<%= f.text_field :track_type %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And the following is the log after create:
Started POST "/tracks" for 127.0.0.1 at 2014-07-10 00:03:08 -0600
Processing by TracksController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"TzzOJrRmCzAcqdJcm+X5vSfKoHiL36T/yqUsdrpP+Ew=",
"track"=>{"track_title"=>"Track", "description"=>"Test track ", "track_type"=>"Demo"}, "commit"=>"Create Track"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1 (0.0ms) begin transaction SQL (261.5ms) INSERT INTO "tracks" ("created_at", "description", "track_title", "track_type", "updated_at") VALUES (?, ?, ?, ?, ?) [["created_at", "2014-07-10 06:03:08.377238"], ["description", "Test track "], ["track_title", "Track"], ["track_type", "Demo"], ["updated_at", "2014-07-10 06:03:08.377238"]] (45.2ms) commit transaction
Redirected to http://localhost:3000/artists/1
Completed 302 Found in 312ms (ActiveRecord: 306.9ms)

The #artist member variable is only available on each request, so the instance that you created in the new request is not available in the create request, well technically its nil. And you don't redirect to the record, but to a route.
Assuming you have the parameters set up correctly, and the association is being made in create, you could redirect to artist_path(#track.artist) but would need to see your routes.rb file to be sure

You can set the artist to the current user in the controller after the create
def create
#track = Track.new(track_params)
if #track.save
self.update_attributes(artist: current_user)
flash[:notice] = "Track was successfully created."
redirect_to artist_path(#track.artist)
else
redirect_to new_artist_track_path
end
end

Related

Create parent and child entry from same nested forms in Rails

EDIT 3: Just to clarify, the goal and problem is to create 2 new records from the same form of which one is the parent and one is the child. The child needs the parent ID, but the parent is created from the same form that the child is.
EDIT 2: I think I'm getting closer. See log file at end, the deal is successfully saved and it looks like the client entry is starting to commit but then not saving. Code is updated below for changes.
I followed the Railscast #196 for nested forms and I am successfully able to edit, add and delete from nested forms as long as the record is already created. Now I am trying to use nested forms to create new records. I think I'm 99% of the way there, but I'm missing something I can't see anymore. I just need to figure out how to pass the id of the parent to the child.
In addition to the Railscast I used this answer to set inverse_of relationships and call the save order (using create instead of save though). I'm pretty sure the problem is in the form or the controller (but models are listed below too)
Nested Form (I tried to simplify to make it easier to read)
EDIT 2: remove hidden field
<%= form_for(#deal) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="deal-<%= #deal.id %>" >
<div class="form-group">
<%= f.label :headline %>
<%= f.text_field :headline, required: true, placeholder: "Headline" %>
</div>
<div class="form-group" id="clients">
<%= f.fields_for :clients do |client_form| %>
<div class="field">
<%= client_form.label :client %><br />
<%= client_form.text_field :name, placeholder: "Client name" %>
</div>
<% end %>
<%= link_to_add_fields "Add client", f, :clients %>
</div>
<div class="form-group">
<%= f.label :matter %>
<%= f.text_field :matter, placeholder: "Matter", rows: "4" %>
</div>
<div class="form-group">
<%= f.label :summary %>
<%= f.text_area :summary, placeholder: "Deal summary", rows: "4" %>
</div>
<div class="form-group">
<div class="action-area">
<%= f.submit "Add deal" %>
</div>
</div>
</div>
<% end %>
Controller
EDIT 2: include deal_id param & change save calls
class DealsController < ApplicationController
before_action :require_login
def new
#deal = Deal.new
#client = #deal.clients
end
def create
#deal = current_user.deals.create(deal_params)
if #deal.save
flash[:success] = "Your deal was created!"
redirect_to root_url
else
render 'deals/new'
end
end
private
def deal_params
params.require(:deal).permit(:headline, :matter, :summary, clients_attributes: [:id, :deal_id, :name, :_destroy])
end
end
EDIT 2: No longer yields errors in browser and success flash message is triggered
EDIT 2: Here is the console output on submit (the record is saved and can be viewed it just doesn't have a client)
Started POST "/deals" for ::1 at 2017-04-26 00:13:08 +0200
Processing by DealsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"hbvpS6KsZOorR3u4LgNoG5WHgerok6j3yYzO+dFUHs9thsxRi+rbUkm88nb7A5WvlmWZEcvaDvCKywufP3340w==", "deal"=>{"headline"=>"headline", "client"=>{"name"=>"aaa"}, "matter"=>"", "summary"=>""}, "commit"=>"Add deal"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
Unpermitted parameter: client
(0.1ms) begin transaction
SQL (0.5ms) INSERT INTO "deals" ("headline", "matter", "summary", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?, ?) [["headline", "headline"], ["matter", ""], ["summary", ""], ["user_id", 1], ["created_at", 2017-04-25 22:13:08 UTC], ["updated_at", 2017-04-25 22:13:08 UTC]]
(3.8ms) commit transaction
(0.1ms) begin transaction
(0.1ms) commit transaction
Redirected to http://localhost:3000/
Completed 302 Found in 16ms (ActiveRecord: 4.6ms)
Models for reference
Users
class User < ApplicationRecord
has_many :deals
end
Deals
class Deal < ApplicationRecord
belongs_to :user
has_many :clients, inverse_of: :deal
validates :headline, presence: true
accepts_nested_attributes_for :clients, allow_destroy: true
end
Clients
class Client < ApplicationRecord
belongs_to :deal, inverse_of: :clients
validates :name, presence: true
validates :deal_id, presence: true
end
You are missing the deal_id on the params
def deal_params
params.require(:deal).permit(:headline, :matter, :summary, clients_attributes: [:id, :deal_id, :name, :_destroy])
end
and on the create of deal, you can make something like this
def create
#deal = Deal.new(deal_params)
if #deal.save
...
and just add a hidden field on the form for the user_id parameter.
The problem is the validations in the client model. Removing the validations will allow the records to be correctly saved from the nested form.
In the Client model remove the name and deal_id validators
class Client < ApplicationRecord
belongs_to :deal, inverse_of: :clients
end
In the controller add build to the new action so it's nicer for the users:
def new
#deal = Deal.new
#client = #deal.clients.build
end
.build is not strictly necessary since the helper method that was created from the Rails Cast will create new client entries on demand, but with it the user is presented with the placeholder first entry which is ignored if blank.
To keep empty client records from saving I added a reject_if: proc to the deal model
class Deal < ApplicationRecord
belongs_to :user
has_many :clients, inverse_of: :deal
validates :headline, presence: true
accepts_nested_attributes_for :clients, allow_destroy: true, reject_if: proc { |attributes| attributes['name'].blank? }
end
I'll leave this open/unanswered in case someone can explain better why my solution worked and if there's a better way to do it.

Unpermitted parameters for has_many :through nested form in Rails 5

Hi I am trying to implement a has_many :through nested form in my Rails 5 app to create a project with users assigned to it. However, when I try to create the project, I face the following validation errors:
- Project users user company must exist
- Project users user email can't be blank
- Project users user email is invalid
- Project users user password can't be blank
- Project users user firstname can't be blank
- Project users user lastname can't be blank
These are validations I did for the user model but I do not understand why they appear when I create a project with nested users. In addition, the console indicated :fullname as an unpermitted parameters, as shown below.
Parameters: {"utf8"=>"✓", "authenticity_token"=>"3RtrzKKVrSOiGLYnHwubF3GaRAbcQE/61doRM5clT8GpFYeJoEVnU2lEmQsNwVO8qecdqig8xjwbTqqaqL1gYQ==", "project"=>{"name"=>"dsada", "description"=>"dadasd", "project_users_attributes"=>{"0"=>{"user_attributes"=>{"fullname"=>["", "5", "4", "3"]}}},"choice"=>"1", "button"=>"", "company_id"=>"1"}
User Load (0.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
Company Load (0.3ms) SELECT "companies".* FROM "companies" WHERE "companies"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
Unpermitted parameter: fullname
Could anyone enlighten me on why I am facing these validation errors and why fullname is considered unpermitted although i included them in project strong params? My codes are as shown below. Thank you!
#Model - project.rb
class Project < ApplicationRecord
has_many :project_users, inverse_of: :project
has_many :users, through: :project_users
accepts_nested_attributes_for :project_users
end
#Model - project_user.rb
class ProjectUser < ApplicationRecord
belongs_to :project, inverse_of: :project_users
belongs_to :user, inverse_of: :project_users
accepts_nested_attributes_for :user
end
#Model - user.rb
class User < ApplicationRecord
has_many :project_users, inverse_of: :user
has_many :projects, through: :project_users
end
#Controller - projects_controller.rb
class ProjectsController < ApplicationController
def new
#company = Company.find(params[:company_id])
#project = #company.projects.build
#project.project_users.build.build_user
end
def create
#company = Company.find(params[:company_id])
#project = #company.projects.build(proj_params)
if #project.valid?
#project.save
render 'show'
else
render 'new'
end
end
private
def proj_params
params.require(:project).permit(:name, :description, project_users_attributes: [:id, user_attributes:[:fullname]])
end
end
end
#Views - new.html.erb
<%= form_for [#company, #project] do |f| %>
<%= f.label :title %>
<%= f.text_field :name %>
<%= f.label :description %>
<%= f.text_area :description %>
<%= f.fields_for :project_users do |project_user_form| %>
<%= project_user_form.fields_for :user do |user| %>
<%= user.label :team_members %>
<%= user.select :fullname, company.users.collect { |p| [ p.fullname, p.id ] }, {:prompt => 'Select Team Members'}, { :multiple => true} %>
<% end %>
<% end %>
<%= f.button :submit => "" %>
<% end %>
Here is why your strong parameters does not work well.
You're saying in proj_params that :fullname is a field. But it's not just a field - it's an array of values. So if you change that to :fullname => [] that'll permit the parameter.
But.. that doesn't make any sense.
Your code is trying to create new users, not assign them. That's why you get validations errors. The simplest way to prevent that is to pass IDs of user, which will try to update / build association, not create.
Then, you'd be better off to pass users' IDs from the view. Try passing user_ids instead of fullnames and don't forget user_ids => [] in strong parameters.

Rails Simple Form Association submission not updating

I have a problem with has_many relationships setting up forms that do not update the values after submission.
Example
Upon submission of a new character, vn_id does not get updated and in Rails Consoles when I try to check for characters in a Vn, it returns empty.
I am trying to set up a form for characters which belongs to Vn which will be linked through the association but upon submission, it is not linked to Vn.
class Character < ActiveRecord::Base
validates :name, :presence => true
validates :summary, :presence => true
belongs_to :vn
end
class Vn < ActiveRecord::Base
has_many :characters
validates :name, presence: true
accepts_nested_attributes_for :characters
end
Form to create a new Character
<%= simple_form_for #character do |f| %>
<div class="col-lg-12">
<%= f.input :summary,input_html: {style: "height:150px;"} %>
</div>
<div class="col-lg-12">
<%= f.association :vn, as: :check_boxes %>
</div>
<%= f.button :submit , class: "btn btn-primary" %>
<% end %>
Controllers
class CharactersController < ApplicationController
def show
#character = Character.find(params[:id])
end
def new
#character = Character.new
end
def create
#character = Character.new(char_params)
if #character.save
else
render :action=>"new"
end
end
private
def char_params
params.require(:character).permit(:name, :summary,:voiceactor,:vn_name,vn_id: [])
end
end
class VnsController < ApplicationController
def show
#vn = Vn.find(params[:id])
end
def new
#vn = Vn.new
end
def create
#vn = Vn.new(vn_params)
if #vn.save
else
render :action=>"new"
end
end
private
def vn_params
def vn_params
params.require(:vn).permit(:name, :summary,:genre,:developer,:rating,vn_id: [])
end
end
end
Submission unpermitted vn_id
Parameters: {"utf8"=>"✓", "authenticity_token"=>"O2s6GVs77GGUMC5u3eZ9ebv/0l5u0MwP44yS8WGCQnjgwSgHfkbCmhEOUo6WKIMSMo5IfDuNYtMzyphnT/5cwQ==", "character"=>{"name"=>"2222", "voiceactor"=>"111", "summary"=>"one two tthee", "vn_id"=>"32"}, "commit"=>"Create Character"}
Unpermitted parameter: vn_id
(0.1ms) begin transaction
SQL (0.6ms) INSERT INTO "characters" ("name", "summary", "voiceactor", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["name", "2222"], ["summary", "one two tthee"], ["voiceactor", "111"], ["created_at", "2015-10-23 10:34:00.285447"], ["updated_at", "2015-10-23 10:34:00.285447"]
The problem is you're trying to permit an array for a singular association.
The f.association input is for your belongs_to association, so why would it allow multiple records?
You can even see how this works here:
The above are methods only for has_many
In short, they don't exist for belongs_to
Thus, when you call f.association :vn, you're populating an attribute vn_id which can then be associated in your database.
The only time you'd have vn_ids is if you used has_many etc.
This means...
def character_params
params.require(:character).permit(:vn_id)
end
with
f.association :vn
... should work
Somehow, changing vn_id: [] in CharactersController back to :vn_id worked.

Adding data to nested model rails 4.0

New to rails and can't figure out how to do this. I have implemented a user log in system using devise and I am trying to let a user create multiple "listings". Sort of like a craigslist type site. I can populate the database from the rails console but I cant figure out how to put it on the site.
I have the following models:
listing.rb
class Listing < ActiveRecord::Base
belongs_to :user
default_scope -> { order('created_at DESC') }
#add validations
validates :user_id, presence: true
end
user.rb (used devise)
class User < ActiveRecord::Base
has_many :listings, dependent: :destroy
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
I am trying to create a page which allows a user to create a new listing. I am not exactly sure how to go about doing this. This is what I have currently:
listings_controller.rb
class ListingsController < ApplicationController
def index
#users = User.all
end
def show
#listing = Listing.find(params[:id])
end
def new
#listing = Listing.new
end
def create
#listing = Listing.new(listing_params)
if #listing.save
flash[:success] = "Success"
redirect_to #listing
else
render 'new'
end
end
private
def listing_params
params.require(:listing).permit(:id, :user_id, :title, :general_info)
end
end
models/views/listings/new.html.erb
<h1> POST A NEW LISTING </h>
<%= form_for #listing do |f| %>
Title: <%= f.text_field :title %> <br />
General Info: <%= f.text_field :general_info %> <br />
<%= f.submit %>
<% end %>
Ive been working on this for quite a while with no luck getting the database to populate. Currently the form once submits hits the "else" in def create and just renders the same page.
Here is the log output when I run this:
Started POST "/listings" for 127.0.0.1 at 2013-07-04 17:37:53 -0600
Processing by ListingsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"mlyDb24OMQkniOCFQ1JTvzxjplHk7kMgzEBEFBH8hGw=", "listing"=>{"title"=>"title should go here", "general_info"=>"hope this works"}, "commit"=>"Create Listing"}
[1m[35m (0.1ms)[0m begin transaction
[1m[36m (0.1ms)[0m [1mrollback transaction[0m
[1m[35mUser Load (0.3ms)[0m SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
Rendered listings/new.html.erb within layouts/application (4.4ms)
Completed 200 OK in 17ms (Views: 10.4ms | ActiveRecord: 0.5ms)
The following got this to work for anyone who comes across this problem:
In routes.db I put the listings in a block:
resources :users do
resource :listings
end
For the new/show/create methods I made sure to search for the user first (note since I am using devise the current_user.id)
def show
#listing = Listing.find(current_user.id)
end
def new
#user = User.find(current_user.id)
#listing = #user.listings.build
end
def create
#user = User.find(current_user.id)
#listing = #user.listings.build(listing_params)
if #listing.save
flash[:success] = "Success"
redirect_to root_path
else
render :action => 'new'
end
end
then finally, changed the form_for in new.html.erb to this:
<%= form_for [#user, #listing] do |f| %>
<%= f.label :title, 'Title' %> <br />
<%= f.text_field :title %>
...
<%= f.submit "submit" %>
<% end %>

Rails nested form inserts null attributes in database

I've searched a lot and the common cause of this problem is attr_ascessible :model_attributes not being declared but I can't seem to get it working.
Looking at the Log below :referee, and :ticket_order values are in the params hash but then are inserted as null in the table. Foreign keys for user_id and event_id are saved in a new record without any errors. The warning about mass assignment led me to the attr_ascessible declaration, tried different variations of it without luck. I'm using devise.
Development log
Started POST "/events/1" for 127.0.0.1 at 2011-07-14 17:38:16 +0100
Processing by EventsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"ddddddjTdnaLKQgZncSDGYt63JA=", "event"=>{"relationship"=>{"event_id"=>"1", "referee"=>"9", "ticket_order"=>"1"}}, "commit"=>"Confirm Purchase", "id"=>"1"}
User Load (20.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 8 LIMIT 1
Event Load (13.1ms) SELECT "events".* FROM "events" WHERE "events"."id" = 1 LIMIT 1
AREL (18.7ms) INSERT INTO "relationships" ("user_id", "event_id", "created_at", "updated_at", "referee", "ticket_order") VALUES (8, 1, '2011-07-14 16:38:16.963351', '2011-07-14 16:38:16.963351', NULL, NULL)
WARNING: Can't mass-assign protected attributes: relationship
[paperclip] Saving attachments.
Redirected to http://localhost:3000/events/1
Completed 302 Found in 588ms
Events
class Event < ActiveRecord::Base
attr_accessible :artist, :venue, :show_info, :date, :doors, :ticket_issue, :ticket_price,
:travel_cost, :accomodation_cost, :hire_cost, :image, :avatar_url, :relationships_attributes, :referee, :ticket_order
has_many :relationships
has_many :users, :through => :relationships
accepts_nested_attributes_for :relationships
Events Controller
def new
#event = Event.new
#users = Relationships.find(:all)
relationship = #event.relationships.build()
end
def create
#event = Event.new(params[:event])
current_user.attend!(#event)
if #event.save
redirect_to #event, :notice => "Successfully created event."
else
render :action => 'new'
end
end
User.rb
def attending?(event)
relationships.find_by_event_id(event)
end
Relationship.rb
class Relationship < ActiveRecord::Base
belongs_to :user
belongs_to :event
attr_accessible :event_id
form view
<%= semantic_form_for #event do |form| %>
<%= form.semantic_fields_for :relationship do |builder| %>
<%= builder.hidden_field :event_id, :value => #event.id %>
<%= builder.inputs do %>
<%= builder.input :referee, :as => :select, :collection => #event.users.all %>
<%= builder.input :ticket_order, :as => :number %>
<% end %>
<% end %>
Yes it must be this, replace:
form.semantic_fields_for :relationship
with:
form.semantic_fields_for :relationships

Resources