Simple form- select collection and set up has_many through - ruby-on-rails

How to display products divided into three parts(three different filter like: product_1, product_2, product_3 ) and need choose only one product from each part
After submit. I should to save all that products for one order.
I have 4 tables:
Users
Orders
Order_details
Products
class Order < ActiveRecord::Base
has_many :order_details
belongs_to :user
has_many :products, through: :order_details
accepts_nested_attributes_for :order_details
end
class OrderDetail < ActiveRecord::Base
belongs_to :order
belongs_to :product
end
class Product < ActiveRecord::Base
has_many :order_details
has_many :orders, through: :order_details
def self.get_first_course
Product.where(product_type: "exem_product_1")
end
def self.get_main_course
Product.where(product_type: "exem_product_2")
end
def self.get_drink
Product.where(product_type: "exem_product_3")
end
end
I am not sure how to write strong params for that situation and how create that objects for save data.
class OrdersController < ApplicationController
before_action :authenticate_user!
def index
#order = Order.new
#I think need something like this..?!
##order.order_details.build
end
def create
end
private
def order_params
params.require(:order).permit(:date, :product_id => [])
end
end

You can do something like this in your controller:
class OrdersController < ApplicationController
before_action :authenticate_user!
def index
#order = Order.all
end
def new
#order = Order.new
end
def create
#order = current_user.orders.new(order_params)
if #order.save
#your actions here
else
#your actions to rescue error
end
end
private
def order_params
params.require(:order).permit(:date, :product_id => [])
end
end
And to use simple form for radio button collections, you have to do something like this:
= simple_form_for(#order, html: {:class => 'well form-horizontal', :method => :post, :action=> :create }) do |f|
.col-xs-12.col-sm-6.col-md-8
= render 'shared/error_messages', object: f.object
= f.collection_radio_buttons :product_ids, Product.get_first_course, :id, :product_name, :item_wrapper_class => 'inline'
%hr
= f.collection_radio_buttons :product_ids, Product.get_main_course, :id, :product_name, :item_wrapper_class => 'inline'
%hr
= f.collection_radio_buttons :product_ids, Product.get_drink, :id, :product_name,,:item_wrapper_class => 'inline'
%hr
= f.association :products, as: :radio_buttons
= f.button :submit, class: "btn btn-primary"

for select collection and get 3 different ids from form, that works for me..
post:
~ products_ids => {array ids}
= simple_form_for #order do |f|
= render 'shared/error_messages', object: f.object
= simple_fields_for :product_ids do |product|
= product.collection_select(nil, Product.get_first_course, :id, :product_name,
{prompt: "Select first course" },class: "form-control product-select")
= product.collection_select(nil, Product.get_main_course, :id, :product_name,
{prompt: "Select first course"},class: "form-control product-select")

Related

Request parameters not saving on model_params on n:n relationship with check_box_tag

I am using Rails 5.1 and im having some issues saving params on an n:n relationship.
I have three models:
class Course < ApplicationRecord
belongs_to :studio
has_many :reviews, dependent: :destroy
has_many :has_category
has_many :categories, through: :has_category
validates :name, presence: true
end
class Category < ApplicationRecord
has_many :has_category
has_many :courses, through: :has_category
end
class HasCategory < ApplicationRecord
belongs_to :category
belongs_to :course
end
and a simple form to create a new course with different categories using check_box_tag (not sure if using it correctly though)
<%= simple_form_for [#studio, #course] do |f| %>
<%= f.input :name %>
<%= f.input :description %>
<% #categories.each do |category| %>
<%= check_box_tag "course[category_ids][]", category.id, true %>
<%= category.name%>
<% end %>
<%= f.button :submit %>
<% end %>
And all is permitted and created on the courses controller:
def new
#studio = Studio.find(params[:studio_id])
#course = Course.new
#course.studio = #studio
#categories = Category.all
end
def create
#studio = Studio.find(params[:studio_id])
#course = Course.new(course_params)
#course.studio = #studio
#categories = params[:category_ids]
if #course.save
redirect_to course_path(#course)
else
render :new
end
end
def course_params
params.require(:course).permit(:studio_id, :name, :description, :category_ids)
end
With better_errors i know the categories are being requested, here the request info:
"course"=>{"name"=>"Course test", "description"=>"testing", "category_ids"=>["2", "3"]}, "commit"=>"Create Course", "controller"=>"courses", "action"=>"create", "studio_id"=>"16"}
but the categories are not saved on course_params, HasCategory instance or on the Course, i´ve tried with #course.categories = params[:category_ids] and other solutions without success.
How do i save the categories to the courses?
Try the following
Change the strong parameter with category_ids: []
def course_params
params.require(:course).permit(:studio_id, :name, :description, category_ids: [])
end
Comment out this line #categories = params[:category_ids]
Hope it helps

Rails polymorphic posts associations and form_for in views

I've been having trouble setting up the form for a polymorphic "department" post in the department view. I followed the rails-cast tutorial for polymorphic associations here
Models:
class Course < ActiveRecord::Base
belongs_to :department, inverse_of: :courses
has_and_belongs_to_many :users, -> { uniq }
has_many :posts, as: :postable #allows polymorphic posts
end
class Department < ActiveRecord::Base
has_many :courses, inverse_of: :department
has_many :posts, as: :postable #allows polymorphic posts
has_and_belongs_to_many :users, -> {uniq}
end
class Post < ActiveRecord::Base
belongs_to :user, touch: true #updates the updated_at timestamp whenever post is saved
belongs_to :postable, polymorphic: true #http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
belongs_to :department, counter_cache: true #for counting number of posts in department
belongs_to :course, counter_cache: true
validates :department_id, :course_id, presence: true
end
config/routes
devise_for :users
devise_scope :users do
match '/users/:id', to: "users#show", via: 'get'
end
resources :departments do
resources :courses
resources :posts
end
resources :courses do
resources :posts
end
views/departments/show.html.erb
<div class="tab-pane" id="posts"><br>
<center><h3>Posts:</h3></center>
<%= render "posts/form", postable: #department %>
</div>
views/posts/_form.html.erb
<%= render "posts/wysihtml5" %>
<center><h3>Create New Post:</h3></center>
<%= form_for [#postable, Post.new] do |f| %>
<%= f.label :title %>
<%= f.text_field :title, class: "form-control" %>
<%= f.label :description %>
<%= f.text_area :description, :rows => 3, class: "form-control" %>
<%= f.text_area :content, :rows => 5, placeholder: 'Enter Content Here', class: "wysihtml5" %>
<span class="pull-left"><%= f.submit "Create Post", class: "btn btn-medium btn-primary" %></span>
<% end %>
controllers/post_controller.rb
class PostsController < ApplicationController
before_filter :find_postable
load_and_authorize_resource
def new
#postable = find_postable
#post = #postable.posts.new
end
def create
#postable = find_postable
#post = #postable.posts.build(post_params)
if #post.save
flash[:success] = "#{#post.title} was sucessfully created!"
redirect_to department_post_path#id: nil #redirects back to the current index action
else
render action: 'new'
end
end
def show
#post = Post.find(params[:id])
end
def index
#postable = find_postable
#posts = #postable.posts
end
...
private
def post_params
params.require(:post).permit(:title, :description, :content)
end
def find_postable #gets the type of post to create
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
controllers/departments_controller.rb
def show
id = params[:id]
#department = Department.find(id)
#course = Course.new
#course.department_id = #department
end
The error is "undefined method `posts_path' for #<#:0x0000010d1dab10>"
I think the error has something to do with the path in the form, but I don't know what. I've tried [#postable, #postable.posts.build] as well but that just gives me undefined method: PostsController.
Anybody know what's going on and how I can fix it?
#department is passed into the form partial as a local variable, but the form calls an instance variable:
# views/departments/show.html.erb
<%= render "posts/form", postable: #department %> # <------ postable
# views/posts/_form.html.erb
<%= form_for [#postable, Post.new] do |f| %> # <------ #postable
Thus, the namespaced route is not properly determined
[#postable, Post.new] # => departments_posts_path
[ nil , Post.new] # => posts_path
Checking your routes, posts are only accessible via nested routes. posts_path is not a valid route, it's method does not exist, and the error is correct: undefined method `posts_path'
Fix:
Set a #postable instance variable in the departments controller so that the form helper can use it:
def show
id = params[:id]
#postable, #department = Department.find(id) # <-- add #postable
#course = Course.new
#course.department_id = #department
end
Then you can simply call render in the view:
<%= render "posts/form" %>

Rails: Form doesn't add data to model

I have a form for creating a new :thing, with a collection_select field to enter an existing :thing the new :thing is related to. Each :thing has_many :things, through an intermediary model :related_things, which has a thing_a_id and thing_b_id. So when I fill in the field and click submit, a :related_thing is supposed to be created with thing_a_id and thing_b_id equal to the two thing_ids, respectively. But no such :related_thing is created; the form doesn't do anything. The other textfields do work though. What's wrong with my code?
I'm using Rails 4.0.10.
Things/new View:
<h1>Add Something!</h1>
<p>
<%= form_for #thing, :url => things_path, :html => { :multipart => true } do |f| %>
<%= f.text_field :name, :placeholder => "Name of the thing" %>
<br>
<%= f.label :related_things %>
<%= f.collection_select :related_things, Thing.all, :id, :name %>
<br>
<%= f.label :display_picture %>
<%= f.file_field :avatar %>
<br>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</p>
Thing Model:
class Thing < ActiveRecord::Base
has_many :related_things
has_many :things, :through => :related_things
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "30x30!" }, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
def related_things
related_thing_ids = RelatedThing.
where("thing_a_id = ? OR thing_b_id = ?", self.id, self.id).
map { |r| [r.thing_a_id, r.thing_b_id] }.
flatten - [self.id]
Thing.where(id: related_thing_ids)
end
def related_thing_ids=(ids)
ids.each do |id|
record = RelatedThing.where(thing_a_id: self.id, thing_b_id: id).first
record ||= RelatedThing.where(thing_a_id: id, thing_b_id: self.id).first
record ||= RelatedThing.create!(thing_a_id: self.id, thing_b_id: id)
end
end
end
RelatedThing Model:
class RelatedThing < ActiveRecord::Base
has_many :things
end
Things Controller:
class ThingsController < ApplicationController
def show
#thing = Thing.find(params[:id])
#related_thing = RelatedThing.all
#thing.things.build
end
def new
#thing = Thing.new
#things = Thing.all
end
def create
#thing = Thing.new(thing_params)
if #thing.save
redirect_to #thing
else
render 'new'
end
end
private
def thing_params
params.require(:thing).permit(:name, :image_path, :avatar)
end
end
RelatedThings Controller:
class RelatedThingsController < ApplicationController
def new
#things = Thing.all.by_name
end
def create
#things = Thing.all.by_name
end
def edit
#things = Thing.all.by_name
end
end
There are two problems causing this:
As Jamesuriah pointed out, your collection_select should use the related_things_ids field instead.
Despite that change, the field is actually being filtered out of the parameter map because of Rails' Strong Parameters.
Specifically, in your controller, the thing_params method should look like:
def thing_params
params.require(:thing).permit(:name, :image_path, :avatar, :related_things_ids)
end
Read up on strong parameters in the link above for more info. Hope that helps!
The collection select should be named related_thing_ids for your model to work, I believe.

Add model/database association upon create

I have a model named Entry, which has many Categories. The page where I create/edit the Entry has checkboxes for every Category.
When I am editing an Entry, everything works okay. When I create the Entry, I get as an error for the #entry:
:entry_categories=>["is invalid"]
My thinking is that rails can't create the entry_categories because it doesn't know the id of the Entry ( which it shouldn't, it hasn't been assigned an id yet ).
I feel like this is a very common thing to try to do. I haven't been able to find an answer though. Here comes the code spamming, there must be something I am missing and hopefully some more experienced eyes can see it.
entry.rb
class Entry < ActiveRecord::Base
validates_presence_of :contents
validates_presence_of :title
has_many :entry_categories, dependent: :destroy
has_many :categories, through: :entry_categories
belongs_to :book
validates_presence_of :book
end
entry_category.rb
class EntryCategory < ActiveRecord::Base
belongs_to :entry
belongs_to :category
validates_presence_of :entry
validates_presence_of :category
end
category.rb
class Category < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name
has_many :entry_categories, dependent: :destroy
has_many :entries, through: :entry_categories
end
entries_controller.rb
class EntriesController < ApplicationController
before_action :find_entry, only: [ :show, :edit, :update, :destroy ]
before_action :find_book, only: [ :new, :create, :index ]
before_action :authenticate_admin!, only: [:new, :create, :edit, :update, :destroy ]
def new
#entry = Entry.new
end
def create
#entry = #book.entries.new( entry_params )
if #entry.save
redirect_to entry_path( #entry ), notice: 'Entry Created'
else
render :new
end
end
def show
#categories = Category.joins( :entry_categories ).where( "entry_categories.entry_id = #{#entry.id} " ).select( "name, categories.id " )
#category_class = #categories.first.name.downcase.gsub( / /, '_' ) if #categories.any?
end
def index
#entries = #book ? #book.entries : Entry.all
end
def edit
end
def update
if #entry.update( entry_params )
redirect_to entry_path( #entry ), notice: 'Entry Updated'
else
render :edit
end
end
def destroy
#book = #entry.book
#entry.destroy
redirect_to book_path( #book ) , notice: 'Entry Destroyed'
end
protected
def entry_params
params.require(:entry).permit( :title, :contents, :year, :month, :day, category_ids: [] )
end
def find_entry
#entry = Entry.find( params[:id] )
end
def find_book
#book = Book.find( params[ :book_id ] )
rescue
#book = nil
end
end
_form.html.erb
<%= form_for [ #book, #entry ] do | form | %>
<%= content_tag :p, title %>
<%= form.text_field :title, placeholder: 'Title', required: true %>
<%= form.number_field :year, placeholder: 'Year' %>
<%= form.number_field :month, placeholder: 'Month' %>
<%= form.number_field :day, placeholder: 'Day' %>
<%= form.text_area :contents %>
<fieldset>
<legend>Categories</legend>
<%= form.collection_check_boxes(:category_ids, Category.all, :id, :name ) %>
</fieldset>
<%= form.submit %>
<% end %>
So again, the entry_categories are invalid in the create method, in the update they are fine. It's the same html file.
There must be some way to tell rails to save the Entry before trying to save the EntryCategory?
Thanks.
I managed to get this working by taking the validations out of EntryCategory:
class EntryCategory < ActiveRecord::Base
belongs_to :entry
belongs_to :category
validates_presence_of :category
end
I'm not particularity happy about this solution, and would still appreciate other thoughts.
I think you can use any of the following approach:
You can use autosave functionality of Active Record association. By this, when you will save EntryCategory, it will automatically save Entry as well.
class EntryCategory < ActiveRecord::Base
belongs_to :entry , autosave: true
#rest of the code
end
You can also use before_save callback of active record. by this, whenever you will save EntryCategory, it will first call a specified method, than proceed with saving.
class EntryCategory < ActiveRecord::Base
before_save :save_associated_entries
#rest of the code
def save_associated_entries
# code to save associated entries here
end
end
Try this:
replace this code:
<%= form.collection_check_boxes(:category_ids, Category.all, :id, :name ) %>
with:
<% Category.all.order(name: :asc).each do |category| %>
<div>
<%= check_box_tag "entry[category_ids][]", category.id %>
<%= category.name %>
</div>
you can format it with fieldset instead of div

Using rails3-jquery-autocomplete to avoid duplicate entries?

I'm using the rails3-jquery-autocomplete gem my artist field on a releases form and want to make sure any new releases use the id of an existing artist if found via autocomplete or create a new artist if not found. At the moment the autocomplete gem returns artists as expected but created a new entry even if that artist exists.
My models are as follows:
class Artist < ActiveRecord::Base
has_and_belongs_to_many :releases
end
class Release < ActiveRecord::Base
has_and_belongs_to_many :artists
accepts_nested_attributes_for :artists, :reject_if => lambda { |a| a[:name].blank? }
end
And controllers:
class ArtistsController < ApplicationController
def index
#artists = Artist.find(:all, :order => :name, :group => :name)
end
def create
#release = Release.find(params[:release_id])
#artist = #release.artists.create(params[:artist])
redirect_to release_path(#release)
end
def destroy
#release = Release.find(params[:release_id])
#artist = #release.artists.find(params[:id])
#artist.destroy
redirect_to release_path(#release)
end
end
class ReleasesController < ApplicationController
autocomplete :artist, :name
def new
#release = Release.new
#release.artists.build
end
def create
#release = Release.new(params[:release])
#release.user_id = current_user.id
end
end
My routes contains:
resources :releases do
get :autocomplete_artist_name, :on => :collection
end
And finally the artist fields:
<%= f.fields_for :artists do |builder| %>
<%= render 'artist_fields', :f => builder %>
<% end %>
(The rendered fields)
<%= f.label :name, "Artist" %><br />
<%= f.autocomplete_field :name, autocomplete_artist_name_releases_path, :class => "text" %>
I suggest you watch this railscast on autcomplete associations
It clearly does what you need.
I don't want to spoil your viewing pleasure, but it all revolves around find_or_create_by
Why don't you fetch the id of the artist with
:id_element => '#artist_id' on the f.autocomplete_field?
If the artist_id param does not come back, you can create it

Resources