I have a recipient and category model. It's a simple association of 1 category has many recipients. When I try to update the recipient form and assign a category, it won't save to the record. If I use the console and update a record manually, e.g. Recipient.update(9, category_id: 13), I see the correct category assigned to the recipient but when I try to edit/update the record, it won't save to the new chosen category.
Here is my recipient model
class Recipient < ActiveRecord::Base
belongs_to :category
accepts_nested_attributes_for :category
end
Here is my category model
class Category < ActiveRecord::Base
has_many :recipients
validates :category, presence: true
default_scope { order('category')}
end
here is the recipient controller
class RecipientsController < ApplicationController
def index
#recipients = Recipient.order(:recipient_name).page(params[:page])
end
def new
#recipient = Recipient.new
end
def show
#recipient = Recipient.find(params[:id])
end
def create
#recipient = Recipient.new(recipient_params)
if #recipient.save
redirect_to recipients_path
else
render :new
end
end
def edit
#recipient = Recipient.find(params[:id])
end
def update
#recipient = Recipient.find(params[:id])
recipient_params = params.require(:recipient).permit(:recipient_name, :alternate_name, :description, :city, :state, :country, category_attributes: [:category, :id])
#recipient.update_attributes(recipient_params)
redirect_to recipient_path(id: #recipient.id)
end
def destroy
#recipient = Recipient.find(params[:id])
#recipient.destroy
redirect_to recipients_path
end
private
def recipient_params
params.require(:recipient).permit(:recipient_name, :alternate_name, :description, :city, :state, :country, product_attributes: [:product_name, recipient_products: [:recipient_id, :product_id]], channel_attributes: [:channel_name, recipient_channels: [:recipient_id, :channel_id]], category_attributes: [:id, :category])
end
end
here is the edit view
<div class="row">
<div class="col-sm-6">
<h2>Edit <%= #recipient.recipient_name %></h2>
<%= simple_form_for #recipient do |form| %>
<%= form.error_notification %>
<%= form.input :recipient_name, placeholder: 'Recipient', label: 'Recipient Name' %>
<%= form.input :alternate_name, placeholder: 'Alternate Name' %>
<%= form.association :category, label_method: :category, value_method: :id %>
<%= form.input :description, placeholder: 'Description'%>
<%= form.input :city, placeholder: 'City'%>
<%= form.input :state, placeholder: 'State' %>
<%= form.input :country, as: :country, priority: ['US', 'CA'] %>
<%= form.button :submit, 'Update Recipient', {:class=>"btn btn-secondary"} %>
<%= link_to "Cancel", :back, {:class=>"btn btn-default"} %>
<% end %>
</div>
</div>
and here is my routes.rb file
Rails.application.routes.draw do
root to: 'home#index'
resources :media_points
resources :products
resources :channels
resources :recipients
resources :media_point_products
resources :distributions
resources :categories do
resources :recipients
end
get '/listing' => "listing#index"
devise_for :admins
devise_for :users
resources :users
end
I wrote it as an answer since the format is a pain in comments:
Change your recipient_params private method from:
category_attributes: [:category, :id]
to
category_id: param_that_has_category_id_here
Also, you have two routes for recipients and it is going to utilize the first matching route which is not the nested recipients in categories route you have further down. If the first change in your private method didn't fix it I would specify the nested situation in simpleform doing as follows since you are probably looking to use the nested route:
simple_form_for [#category, #recipient], url: 'nested_route_path_here' do |f|
Just add #category = Category.new to your new action in the recipients controller and then in your create action you'll have the category param sent via params[:category_id]
Related
I am new in rails and programming at all. I have to add a kind of id_user_created and id_user_edited in a table called Plan. These ids will help me to know which user created and edited a plan, but I have no idea how to do it. On my db schema, there is no relation between User and Plan but now that I have to add theses ids, I assume that I will have to create a relation, right? Thanks a lot.
Models
class Plan < ApplicationRecord
has_many :users
end
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
belongs_to :plan
end
Controller
class PlansController < ApplicationController
def new
#plan = Plan.new
end
def create
#user = current_user
#plan = Plan.new(plan_params)
#plan.user = #user
if #plan.save
redirect_to plan_path(#plan), notice: 'O plano foi criado com sucesso.'
else
render :new
end
end
def edit
#plan = Plan.find(params[:id])
end
def update
#plan = Plan.find(params[:id])
if #plan.update(plan_params)
redirect_to #plan, notice: 'O plano foi editado com sucesso.'
else
render :edit
end
end
private
def plan_params
params.require(:plan).permit(:name, :duration, :price, :status, :default)
end
end
Route
Rails.application.routes.draw do
devise_for :users
root to: 'pages#home'
resources :plans do
resources :accounts, only: %i[new create] do
end
end
resources :payments, only: %i[index]
resources :accounts, only: %i[index show edit update destroy] do
resources :users, only: %i[new create] do
resources :roles
end
end
resources :users, only: %i[index show edit update destroy]
Plans Form
<%= simple_form_for [#user, #plan] do |f| %>
<%= f.input :name, label: 'Nome' %>
<%= f.input :duration, label: 'Duração' %>
<%= f.input :price, label: 'Preço' %>
<%= f.input :status %>
<%= f.input :default %>
<%= f.button :submit, class:"btn-outline-secondary" %>
<% end %>
In Rails you generate migrations to create foreign keys by using the references (aka belongs_to) type:
rails g migration add_user_to_plans user:references
Which generates the following migration:
class AddUserToPlans < ActiveRecord::Migration[6.0]
def change
add_reference :plans, :user, null: false, foreign_key: true
end
end
When you run the migration it creates a plans.user_id column which points to the users table.
If you want to call the column/association something else like creator_id you need to explicitly tell rails which table you are referencing. Just don't call your columns id_user_created unless you want to come off as a complete snowflake.
class AddCreatorToPlans < ActiveRecord::Migration[6.0]
def change
add_reference :plans, :creator,
null: false,
foreign_key: { to_table: :users }
end
end
And you also have to explicitly set up your association:
class Plan
belongs_to :creator,
class_name: 'User',
inverse_of: :plans
end
class User
has_many :plans,
foreign_key: :creator_id,
inverse_of: :plans
end
Your form is also off. When you're dealing with creating resources as the logged in user you don't want/need to nest the route.
<%= simple_form_for #plan do |f| %>
<%= f.input :name, label: 'Nome' %>
<%= f.input :duration, label: 'Duração' %>
<%= f.input :price, label: 'Preço' %>
<%= f.input :status %>
<%= f.input :default %>
<%= f.button :submit, class:"btn-outline-secondary" %>
<% end %>
And you can also trim that create method down by building the resource off the association on the current user:
def create
#plan = current_user.plans.new(plan_params)
if #plan.save
redirect_to #plan,
notice: 'O plano foi criado com sucesso.'
else
render :new
end
end
While you could do the same thing and add an editor_id column to plans its probably not what you want as it will only let you record a single id and not something more useful like a history of who edited the record and when which requires a join table and this is really an entire question on its own.
**Try add 's' to model in database relationship**
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
belongs_to :plans
end
<%= simple_form_for [#user, #user.plans] do |f| %>
<%= f.input :name, label: 'Nome' %>
<%= f.input :duration, label: 'Duração' %>
<%= f.input :price, label: 'Preço' %>
<%= f.input :status %>
<%= f.input :default %>
<%= f.button :submit, class:"btn-outline-secondary" %>
<% end %>
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
I got a problem which I can't solve. I made two models; one model called Film is the parent model and another model called Review is the child model. I got validation conditions on the child model but it does not display on the view.
Film model
class Film < ApplicationRecord
has_many :reviews
validates_presence_of :filmtitle, presence: true
validates_presence_of :filmdescription, presence: true
validates_presence_of :filmdirector, presence: true
validates_presence_of :filmrating, presence: true
validates_presence_of :filmstarname, presence: true
end
Review model
class Review < ApplicationRecord
validates :rating, presence: true
validates :commenter, presence: true
validates :body, presence: true
belongs_to :film
end
Review Controller
class ReviewsController < ApplicationController
def create
#film = Film.find(params[:film_id])
#review = #film.reviews.create(review_params)
redirect_to film_path(#film)
end
private
def review_params
params.require(:review).permit(:commenter, :body, :rating)
end
end
Film show.html.erb
<% if #film.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#film.errors.count, "error") %></h2>
<ul>
<% #film.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= form_for([#film, Review.new]) do |f| %>
<p>
<%= f.label :commenter %><br>
<%= f.text_field :commenter, :placeholder => 'Your name' %>
</p>
<p>
<%= f.label :body %><br>
<%= f.text_area :body, :placeholder => 'Your comment' %>
</p>
<p>
<%= f.label :rating %><br>
<%= f.select :rating, ['1 Star', '2 Stars', '3 Stars', '4 Stars', '5 Stars'] %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
Film Controller
class FilmsController < ApplicationController
before_action :set_film, only: [:show]
# GET /films
# GET /films.json
def index
#films = Film.all.paginate(page: params[:page], per_page: 30)
#reviews = Review.new
end
# GET /films/1
# GET /films/1.json
def show
end
# GET /films/new
def new
end
# GET /films/1/edit
def edit
end
# POST /films
# POST /films.json
def create
end
# PATCH/PUT /films/1
# PATCH/PUT /films/1.json
def update
end
# DELETE /films/1
# DELETE /films/1.json
def destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_film
#film = Film.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def film_params
params.require(:film).permit(:filmtitle, :filmdescription)
end
end
route.rb
Rails.application.routes.draw do
resources :films do
resources :reviews
end
resources :rentals
resources :buys
resources :admin
resources :adminrentals
resources :adminfilms
resources :logins
resources :admins_login
resources :games
get '/adminCool' => 'admins_login#index'
get '/adminlogin' => 'admins_sessions#new'
post '/adminlogin' => 'admins_sessions#create'
get '/adminlogout' => 'admins_sessions#destroy'
get '/adminsignup' => 'admins#new'
post '/admins' => 'admins#create'
get '/login' => 'sessions#new'
post '/login' => 'sessions#create'
get '/logout' => 'sessions#destroy'
get '/signup' => 'users#new'
post '/users' => 'users#create'
get '/cool' => 'logins#index'
end
Please try
def create
#film = Film.find(params[:film_id])
#review = #film.reviews.new(review_params)
if #review.save
redirect_to film_path(#film)
else
render "films/#{#film.id}"
end
end
Hoping someone can help out with this. I have two models order and date_order. Each order can have multiple date_orders, and I should be able to create many date_orders as I create an order.
How do I do that? As you can see, my code is working well for creating ONE date_order and relating it to the created order.
UPDATE: I have tried to create many "builders" in my orders/new file. It worked on the view, and created an order when I entered multiple dates and times. But the fields_for did not create any date_orders.
orders_controller.rb
def new
#order = Order.new
#order.date_orders.build
end
def create
#order = Order.new(order_params)
if #order.save
flash[:success] = "blah"
redirect_to #order
else
render 'new'
end
end
private
def order_params
params.require(:order).permit(:user_id, :purpose,
date_orders_attributes: [:id, :order_date, :time_start, :time_end, :order_id])
end
order.rb
class Order < ActiveRecord::Base
has_many :date_orders, :dependent => :destroy
accepts_nested_attributes_for :date_orders, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end
date_order.rb
class DateOrder < ActiveRecord::Base
belongs_to :order
end
order/new.html.erb
<%= form_for(#order, :html => {:multipart => true}) do |f| %>
## SOME QUESTIONS ##
<%= f.fields_for :date_orders do |builder| %>
<%= builder.label :date %>
<%= builder.date_field :order_date %>
<%= builder.label :starting_time %>
<%= builder.time_field :time_start %>
<%= builder.label :ending_time %>
<%= builder.time_field :time_end %>
<% end %>
<% end %>
Build more orders_dates:
class OrdersController < ApplicationController
def new
#order = Order.new
5.times { #order.date_orders.build } # < === HERE ===
end
private
def order_params
params.require(:order).permit(:user_id, :purpose,
# |- === HERE ===
date_orders_attributes: [:id, :content, :order_date, :time_start, :time_end, :order_id])
end
end
Update:
Also, add content to your strong params whitelist.
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" %>