Add validation and error message if empty params - ruby-on-rails

I need to validate whether or not a Credit has been assigned to a Track in a form. The track is saving, the errors.add isn't working.
Ideas?
class Track
has_many :credits
accepts_nested_attributes_for :credits, allow_destroy: true
attr_accessible :credits_attributes
has_many :vendors, through: :credits
accepts_nested_attributes_for :vendors
attr_accessible :vendor_ids
end
And trying to validate that the Credit bit of the form is filled out on a Track edit page:
def verify_credits
#track = Track.find(params[:id])
if !params.has_key?(:split) || params[:track][:credits_attributes][:vendor_id].empty?
puts '*' * 300
#track.errors.add(:base, 'You must credit this track to a Vendor with a split')
end
end
and I call verify_credits in overwritten contoller actions in ActiveAdmin track.rb. The puts '*' is coming through so I know my conditional is fine. Example:
def update
referrer = params[:track][:referrer]
params[:track].delete(:referrer)
s3_path = params[:track][:file_cache] if params[:track][:file_cache]
params[:track].delete(:file_cache) unless s3_path.empty?
#track = Track.find(params[:id])
#track.assign_attributes(params[:exclusivities])
verify_credits
authorize! :update, #track
if s3_path
Delayed::Job.enqueue ProcessTrackFileJob.new(#track.id, s3_path) unless s3_path.empty?
end
super do |format|
format.html { redirect_to referrer } unless referrer.to_s.empty?
end
end
ADDED:
_form.html.erb
<%= f.inputs "Vendors" do %>
<% f.has_many :credits do |c|%>
<% c.input :vendor, as: :string, required: true, input_html: { %>
<% class: 'autocomplete', %>
<% name: '', %>
<% value: c.object.vendor.try(:name), %>
<% data: { %>
<% url: autocomplete_manage_vendors_path %>
<% } %>
<% } %>
<% c.input :vendor_id, as: :hidden %>
<% c.input :_destroy, as: :boolean, required: false, label: "Remove" %>
<% c.input :split %>
<% end %>
<% end %>
<% end %>

You don't have any validations in your Track model. If you're trying to make sure there is a vendor_id, you would need validates :vendor_id, presence: true in your Track model. If not, your Track model doesn't know there is an error if vendor_id is missing.

Related

Using JSONB in rails 6 forms and permitted params

I am trying to use JSONB in a deeply tested form that works
<%= simple_form_for(#company, local: true) do |form| %>
<%= form.simple_fields_for #schedule do |ff| %>
<%= ff.select :status, Schedule.statuses.keys.collect {
|status| [Schedule.human_enum_name(:status, status), status] },
class:"custom-control", label: false %>
<%= ff.date_field :valid_from %>
<%= ff.date_field :valid_through %>
<%= ff.simple_fields_for :business_hours do |field| %>
<% I18n.t('date.day_names').each_with_index do |day, wday| %>
<%= field.label :"#{day.downcase}" %></td>
<%= field.select :"#{day.downcase}_closes_at",
collection: time_select_options,
class: 'custom-select',
include_blank: "Closed" %>
<%= field.select :"#{day.downcase}_closes_at",
collection: time_select_options,
class: 'custom-select',
include_blank: "Closed" %>
<% end %>
<% end %>
<% end %>
The nested business_hours field is the JSONB col.
The Schedule model belongs_to Company, which has_may Schedules.I am using store_accessor :business_hours in the Schedule model.
Schedule model looks like:
has_many :schedules, inverse_of: :company, dependent: :destroy
accepts_nested_attributes_for :schedules, allow_destroy: true,
reject_if: proc { |att| att["day"].blank? }
the Company controller is set as follows:
def new
#account = current_account
#company = #account.company.new
#chedule = #company.schedules.new
end
def create
#account = current_account
#company = #account.build_company(company_params)
#company.save
end
def edit
#schedules = #company.schedules.all
end
def update
#company.update(company_params)
end
i am whitelisting with
def company_params
params.require(:company).permit(:name, ...,
...,
schedules_attributes: [:status, :id, :_destroy, :day, :valid_from, :valid_through,
business_hours: [:sunday_opens_at, ...]
end
the form submit as follows:
Processing by CompaniesController#update as HTML
Parameters: {"authenticity_token"=>"...", "company"=>{"schedule"=>{"status"=>"active", "valid_from"=>"2020-06-01", "valid_through"=>"2020-06-01", "business_hours"=>{"monday_closes_at"=>"00:00:00", "tuesday_closes_at"=>"00:00:00", ...}}}, "button"=>"", "locale"=>"en", "id"=>"acme-sas"}
however I receive: Unpermitted parameter: :schedule
you need to send schedule as schedules
Processing by CompaniesController#update as HTMLParameters: {"authenticity_token"=>"...", "company"=>{"schedules"=>{"status"=>"active", "valid_from"=>"2020-06-01", "valid_through"=>"2020-06-01", "business_hours"=>{"monday_closes_at"=>"00:00:00", "tuesday_closes_at"=>"00:00:00", ...}}}, "button"=>"", "locale"=>"en", "id"=>"acme-sas"}

undefined method `class_detail' for nil:NilClass

I have two tables class_details and user_classes. user_classes table is dependent of user table and class_details and class_details is independent of user table. Now my requirement is that when i click a button few details must be saved to the database which belong to different tables. User dependent table are only getting saved to database and not the user independent tables and i am getting error undefined method class_detail for nil:NilClass
Controller code
def update_profile
if #user.update(user_params)
redirect_to profile_index_path, notice: 'Profile was successfully updated.'
else
render :index
end
end
end
private
def set_user
#user = User.find(current_user.id)
#user.fee || #user.build_fee
#user.user_class || #user.build_user_class
end
def user_params
params.require(:user).permit(:name, fee_attributes: %i[id fee_hour fee_month], user_class_attributes: %i[id class_detail [id class_name class_category]])
end
class_detail.rb
class ClassDetail < ApplicationRecord
has_one :user_class, dependent: :destroy
end
user_class.rb
class UserClass < ApplicationRecord
belongs_to :user
belongs_to :class_details
validates_presence_of :user_id
end
user.rb
has_one :fee, dependent: :destroy
accepts_nested_attributes_for :fee
has_one :user_class, dependent: :destroy
view code
<%= form_for(#user, url: {action: 'update_profile'}, html: {class: 'm-form m-form--fit m-form--label-align-right'}) do |f| %>
<%= f.fields_for :fee, #user.fee do |u| %>
<%= u.number_field :fee_hour, class: 'form-control m-input', placeholder: t('.fee_for_hour') %>
<% end %>
<%= f.fields_for :user_class, #user.user_class do |k| %>
<%= f.fields_for :class_detail, #user_class.class_detail do |c| %>
<%= c.text_field :class_name, class: 'form-control m-input' %>
<% end %>
<% end %>
Can anyone help me updating user independent table! Thank you in advance
params.require(:user).permit(:name, fee_attributes: %i[id fee_hour fee_month], user_class_attributes: %i[id class_detail [id class_name class_category]])
That means you want to update class_detail from user_class, but you need to define nested attributes:
class UserClass < ApplicationRecord
belongs_to :user
belongs_to :class_details
accepts_nested_attributes_for :class_details, update_only: true
validates_presence_of :user_id
end
Also, the form must look like this:
<%= form_for(#user, url: {action: 'update_profile'}, html: {class: 'm-form m-form--fit m-form--label-align-right'}) do |f| %>
<%= f.fields_for :fee, #user.fee do |u| %>
<%= u.number_field :fee_hour, class: 'form-control m-input', placeholder: t('.fee_for_hour') %>
<% end %>
<%= f.fields_for :user_class, #user.user_class do |k| %>
<%= k.fields_for :class_detail, #user.user_class.class_detail do |c| %>
<%= c.text_field :class_name, class: 'form-control m-input' %>
<% end %>
<% end %>
<% end %>
And in your controller:
def user_params
params.require(:user).permit(:name, fee_attributes: %i[id fee_hour fee_month], user_class_attributes: [:id, { class_detail: %i[class_name class_category] }])
end
Edited:
That all means class_details and user_class are associated to the user already. Build references model - child-child-model - child-parent-model from the single call not possible. You can build this references in the edit action. For example:
def edit
#user.create_user_class!(class_detail: ClassDetail.find(n)) unless #user.user_class
end

Rails nested form attributes not getting saved

I've already looked through every other stackoverflow for this issue, but none of the solutions have fixed this. My elements in a nested_form are not being saved in the database. I've also made sure that all model associations are correct. I've been trying to fix this for nearly 8 hours now, and would really appreciate some help, especially considering every other solution hasn't worked.
Basically, I have a Playlist model that contains multiple Song models. I'm trying to use a nested_form to add the Song models to the Playlist. However, none of the Songs are ever being saved. I apologize if my methods are misguides, as I'm still fairly new to Rails.
GitHub Repo:https://github.com/nsalesky/Ultra-Music
playlists_controller.rb
def index
#user = current_user
#playlists = #user.playlists
end
def show
#user = current_user
#playlist = #user.playlists.find(params[:id])
end
def new
#playlist = Playlist.new
#I was told to do this
#playlist.songs.build
end
def create
#user = current_user
#playlist = #user.playlists.create(playlist_params)
if #playlist.save
redirect_to #playlist
else
render :action => 'new'
end
end
def edit
#playlist = current_user.playlists.find(params[:id])
end
def update
#user = current_user
#playlist = #user.playlists.find(params[:id])
if #playlist.update_attributes(playlist_params)
redirect_to #playlist
else
render :action => 'edit'
end
end
def destroy
#user = current_user
#playlist = #user.playlists.find(params[:id])
#playlist.destroy
redirect_to playlists_path(#user.playlists)
end
private
def playlist_params
params.require(:playlist).permit(:name, :description, songs_attributes: [:id, :name, :link, :_destroy])
end
playlist.rb
belongs_to :user
has_many :songs, dependent: :destroy
accepts_nested_attributes_for :songs, :allow_destroy => true, :reject_if => lambda { |a| a[:content].blank? }
validates :name, presence: true
validates_associated :songs, presence: true
_form.html.erb
<%= nested_form_for #playlist do |f| %>
<div>
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div>
<%= f.label :description %>
<%= f.text_field :description %>
</div>
<!--<div>
<button type="button" id="addsong">Add Song</button><br>
<button type="button" id="removesong">Remove Song</button><br>
</div> !-->
<div>
<%= f.fields_for :songs do |song_form| %>
<%= song_form.text_field :name %>
<%= song_form.text_field :link %>
<%= song_form.link_to_remove "Remove Song" %>
<% end %>
<p><%= f.link_to_add "Add Song", :songs %></p>
</div>
<div>
<%= f.submit %>
</div>
<% end %>
In your playlist.rb, you wrote:
:reject_if => lambda { |a| a[:content].blank? }
Here the block parameter |a| stands for attributes of a specific song. So a[:attribute] relates to a single attribute. The problem is your Song doesn't have a :content attribute. So this a[:content].blank? will always be true, means you would be rejected building a song.
Just change a[:content] to a valid attribute such as a[:name]

Rendering error messages for form_tag that creates multiple objects?

I'm not sure how to display the error messages for my form when using it in this form_tag scenario. My code below allows me to create 5 products at once on a form but unfortunately only renders the notice that "an error occurred...".
Here is my code:
Product.rb
class Product < ActiveRecord::Base
attr_accessible :price, :name, :purchase_date, :product_store, :in_category
belongs_to :user
belongs_to :store
attr_reader :product_store
validates_inclusion_of :in_category, :in => [true, false]
validates_presence_of :name, :price, :store_id, :user_id
validates_numericality_of :price
def product_store=(id)
self.store_id = id
end
end
Products_controller.rb
class ProductsController < ApplicationController
def new
#products = Array.new(5) { Product.new }
end
def create_multiple
#products = current_user.products.create(params[:products].map { |_k, p| p.merge params[:product] })
if #products.each(&:save)
redirect_to :back, :notice => "Success!"
else
redirect_to :back, :notice => "An error occured, please try again."
end
end
end
Form.html.erb
<%= form_tag create_multiple_products_path, :method => :post do %>
<%= error_messages_for #product %>
# the :purchase_date and :in_category are merged into all 5 Products.
<%= date_select("product", "purchase_date") %>
<%= label_tag :in_category, 'Add to Category?' %>
<%= radio_button("product", :in_category, 1) %>
<%= radio_button("product", :in_category, 0) %>
<% #products.each_with_index do |product, index| %>
<%= fields_for "products[#{index}]", product do |p| %>
<%= render "fields", :f => p %>
<% end %>
<% end %>
<%= submit_tag "Done" %>
<% end %>
Theirs 2 issues. 1. Getting the validations for the fields outside of the fields_for to show .2. And then the ones inside of the fields_for. How could I do this?
Thank you.
I've been trying to do much the same thing, with this:
<% #products.each_with_index do |product, index| %>
<% product.errors.full_messages.each do |value| %>
<li><%= value %></li>
<% end %>
However, this only shows errors for the first product with errors. You submit it, and if there is a subsequent product with errors, you are sent back to that page, and that next product with errors shows its errors, etc.
EDIT: Got it. It has to do with how I was validating. Instead of this:
if #products.all?(&:valid?)
do this:
#products.each(&:valid?) # run the validations
if #products.all? { |t| t.errors.empty? }

How do I use a nested attribute with a form_for 's select method?

We have a user model which :has_one detail. In a form_for a user, I want a drop-down to select the user's details' time_zone.
I've tried
<% form_for #user do |f| %>
... user stuff ...
<%= f.select :"detail.time_zone", ...other args... %>
<% end %>
but I get a NoMethodError for detail.time_zone. What's the correct syntax for doing this - or if it's not possible to do it this way, how should I be doing it?
Don't forget to use accepts_nested_attributes_for in your user model:
class User < ActiveRecord::Base
has_one :detail
accepts_nested_attributes_for :detail
end
Detail model:
class Detail < ActiveRecord::Base
belongs_to :user
end
Users controller:
class UsersController < ActionController::Base
def new
#user = User.new
#user.build_detail
end
end
User new/edit view:
<% form_for #user do |u| %>
<% u.fields_for :detail do |d| %>
<%= d.select :country, Country.all.map { |c| [c.name, c.id] }
<%= d.time_zone_select :time_zone %>
<% end %>
<% end %>
Why is there a colon after f.select? Also it should be <%=... and not <%..
Assuming you have a 'time_zone' column in your table,
<% form_for #user do |f| %>
... user stuff ...
# args: user object, time_zone method, prioritizing zones(separating list), default
<%= f.time_zone_select :time_zone, nil, :default => "Pacific Time (US & Canada)", :include_blank => false %>
<% end %>
Additional resource :
http://railscasts.com/episodes/106-time-zones-in-rails-2-1
http://railsontherun.com/2007/12/21/timezone-selection/

Resources