ActiveModel::MissingAttributeError - rails - ruby-on-rails

I have some problem with my code:
Models:
class UsefulPhrase < ActiveRecord::Base
has_many :useful_phrase_contents
accepts_nested_attributes_for :useful_phrase_contents
validates_presence_of :key
end
class UsefulPhraseContent < ActiveRecord::Base
belongs_to :useful_phrase
attr_accessor :useful_phrase_id
validates_presence_of :language, :content
end
Controller:
def new
#useful_phrase = UsefulPhrase.new
#available_languages = available_languages
#useful_phrase.useful_phrase_contents.build
end
def create
#useful_phrase = UsefulPhrase.new(useful_phrase_params)
#useful_phrase.useful_phrase_contents.build(upc_params)
respond_to do |format|
if #useful_phrase.save
format.html { redirect_to #useful_phrase, notice: 'bla-bla' }
format.json { render :show, status: :created, location: #useful_phrase }
else
format.html { render :new }
format.json { render json: #useful_phrase.errors, status: :unprocessable_entity }
end
end
end
def useful_phrase_params
params.require(:useful_phrase).permit(:key)
end
def upc_params
params.require(:useful_phrase).require(:useful_phrase_content).permit(:language, :content)
end
When i'm trying save any record I get:
ActiveModel::MissingAttributeError at /useful_phrases
can't write unknown attribute useful_phrase_id
I don't know how to repair it.

try edit your parameter in your upc_params
params.require(:useful_phrase)
.permit(:language, content, :useful_phrase_content => [puttheattributefor use_ful_phrase_content])

Related

Foreign key not saving rails 5

I am trying to save a product which belongs_to category with foreign_key :category_id. The failed product.save is show below:
Processing by ProductsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"2V3zVrgU3SoGAwyHDLYQuyDFWq9rI7U4GdDeZNDRwrLTwsGloio4MIXUOdU/ckvnTsMGF9B9TL4tuNWKSqZpVg==", "product"=>{"name"=>"trek"}, "commit"=>"Save Product", "category_id"=>"1"}
Everytime I try to save the product it says "category must exist". I understand I have my models and migrations a little off, but can't figure out how. Below are the files.
product.rb:
class Product < ApplicationRecord
belongs_to :category
end
category.rb: empty
create_categories.rb:
class CreateCategories < ActiveRecord::Migration[5.2]
def change
create_table :categories do |t|
t.string :name
t.timestamps
end
end
end
create_products.rb:
class CreateProducts < ActiveRecord::Migration[5.2]
def change
create_table :products do |t|
t.string :name
t.belongs_to :category, index: {unique: true}, foreign_key: true, null: $
t.timestamps
end
end
end
I can also post my product_controller.rb however it is pretty standard with just #product.save and a redirect_to products_path or 'products#index'.
Thank you.
EDIT: products_controller.rb:
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to category_products_path([#category, #product])$
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_enti$
end
end
end
def product_params
params.require(:product).permit( :name, :category_id)
end
It seems like you want to do something like:
class ProductsController < ApplicationController
def create
#category = Category.find(params[:category_id])
#product = #category.products.new(product_params)
if #product.save
format.html { redirect_to category_products_path([#category, #product]) }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
private
def product_params
params.require(:product).permit(:name, :category_id)
end
end
This assumes, naturally, that you have something like:
class Category < ApplicationRecord
has_many :products
end
class Product < ApplicationRecord
belongs_to :category
end
As you currently have it written, there are at least a couple of problems.
First, you say:
def product_params
params.require(:product).permit( :name, :category_id)
end
But, params[:product] doesn't have category_id.
Second, you say:
format.html { redirect_to category_products_path([#category, #product])}
But, you don't define #category.
You should find the category from the category id, then under that category create product.
class ProductsController < ApplicationController
def create
#category = Category.find(params[:category_id])
#product = #category.products.new(product_params)
if #product.save
format.html { redirect_to category_products_path([#category, #product]) }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity}
end
end
private
def product_params
# as your category_id is outside of product hash, doesn't need to permit in product hash though
params.require(:product).permit(:name)
end
end
In your product/_form.html.erb create the form_for with nested resources, then you'll get the category_id under params[:product] hash.

Rails generate records for secondary model on create

I am making a sort of checklist section for my site. I have a model called commission that will contain data about a commissioning task. What I need to do is when a new commission entry is created I need to create a series of about 30 commission tasks that will link to it. A sort of checklist of predefined values for a person to go down through and check. What would be the best way to do this?
Here are my models and controller:
commission.rb
class Commission < ApplicationRecord
has_many :comtasks
belongs_to :project
belongs_to :user
accepts_nested_attributes_for :comtasks, allow_destroy: true
end
comtask.rb
class Comtask < ApplicationRecord
belongs_to :commission
belongs_to :user
end
commissions_controller.rb
class CommissionsController < ApplicationController
before_action :set_commission, only: [:show, :edit, :update, :destroy]
# GET /commissions
# GET /commissions.json
def index
#commissions = Commission.all
end
# GET /commissions/1
# GET /commissions/1.json
def show
end
# GET /commissions/new
def new
#commission = Commission.new
end
# GET /commissions/1/edit
def edit
end
# POST /commissions
# POST /commissions.json
def create
#commission = Commission.new(commission_params)
respond_to do |format|
if #commission.save
format.html { redirect_to #commission, notice: 'Commission was successfully created.' }
format.json { render :show, status: :created, location: #commission }
else
format.html { render :new }
format.json { render json: #commission.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /commissions/1
# PATCH/PUT /commissions/1.json
def update
respond_to do |format|
if #commission.update(commission_params)
format.html { redirect_to #commission, notice: 'Commission was successfully updated.' }
format.json { render :show, status: :ok, location: #commission }
else
format.html { render :edit }
format.json { render json: #commission.errors, status: :unprocessable_entity }
end
end
end
# DELETE /commissions/1
# DELETE /commissions/1.json
def destroy
#commission.destroy
respond_to do |format|
format.html { redirect_to commissions_url, notice: 'Commission was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_commission
#commission = Commission.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def commission_params
params.require(:commission).permit(:project_id, :user_id, :description, :objectname, :location, comtasks_attributes: [:id, :content, :notes])
end
end
Any thoughts or suggestions are appreciated.
below is the idea,
def create
#commission = Commission.create!(commission_params)
# use create not new to generate #commission.id value
# so comtask records can use the id value as reference
create_comtasks_job
# for comtask create I put in other method
respond_to do |format|
if #commission.save
format.html { redirect_to #commission, notice: 'Commission was successfully created.' }
format.json { render :show, status: :created, location: #commission }
else
format.html { render :new }
format.json { render json: #commission.errors, status: :unprocessable_entity }
end
end
end
def create_comtasks_job
# loop 30 tasks / or manual as follow
#commission.comtasks.build(content: 'content1',notes:'notes1')
#commission.comtasks.build(content: 'content2',notes:'notes2')
end
additional code for your model
make sure for your model has relation like sample below
for your model
class Commission < ActiveRecord::Base
has_many :comtasks
end
class Comtask < ActiveRecord::Base
belongs_to :commission
end

Matching up collection_select output with strong parameters

In rails console & using the models below, I connected grades K, 1, and 2 to the school whose Edit form has this select field:
As you can see, that association correctly selects the 3 items in the field, but if I click to select/deselect grades, those changes aren't getting saved.
Here are the models:
# app/models/school.rb
class School < ActiveRecord::Base
has_many :grades_schools, inverse_of: :school
has_many :grades, through: :grades_schools
accepts_nested_attributes_for :grades_schools, allow_destroy: true
end
# app/models/grades_school.rb
class GradesSchool < ActiveRecord::Base
belongs_to :school
belongs_to :grade
end
# app/models/grade.rb
class Grade < ActiveRecord::Base
has_many :grades_schools, inverse_of: :grade
has_many :schools, through: :grades_schools
end
The form looks like this:
# app/views/schools/_form.html.haml
= form_for(#school) do |f|
/ <snip> other fields
= collection_select(:school, :grade_ids, #all_grades, :id, :name, {:selected => #school.grade_ids, include_hidden: false}, {:multiple => true})
/ <snip> other fields + submit button
And the controller looks like this:
# app/controllers/schools_controller.rb
class SchoolsController < ApplicationController
before_action :set_school, only: [:show, :edit, :update]
def index
#schools = School.all
end
def show
end
def new
#school = School.new
#all_grades = Grade.all
#grades_schools = #school.grades_schools.build
end
def edit
#all_grades = Grade.all
#grades_schools = #school.grades_schools.build
end
def create
#school = School.new(school_params)
respond_to do |format|
if #school.save
format.html { redirect_to #school, notice: 'School was successfully created.' }
format.json { render :show, status: :created, location: #school }
else
format.html { render :new }
format.json { render json: #school.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #school.update(school_params)
format.html { redirect_to #school, notice: 'School was successfully updated.' }
format.json { render :show, status: :ok, location: #school }
else
format.html { render :edit }
format.json { render json: #school.errors, status: :unprocessable_entity }
end
end
end
private
def set_school
#school = School.find(params[:id])
end
def school_params
params.require(:school).permit(:name, :date, :school_id, grades_attributes: [:id])
end
end
I have a feeling that the crux of my problem has to do with a mismatch between the params generated by collection_select and the strong parameters. One or both of these is probably incorrect, but I can't for the life of me find example code online that shows me what I'm doing wrong.
After trying a load of failed variations, I'm at my wits end! Thanks in advance for your help!
Crap. I could have sworn I tried this before, but it must have been when using fields_for in the form instead of collection_select. The solution:
def school_params
params.require(:school).permit(:name, :date, :school_id, grades_attributes: [:id])
end
becomes
def school_params
params.require(:school).permit(:name, :date, :school_id, grade_ids: [])
end
I'm still curious how it would work when using fields_for #grades_schools, but will have to save that investigation for another day....

How to perform validation on associated attributes

I tried to validate associated attribute access_module_id in role
model but it doesn't work. How to give validation of presence: true to other model's attribute in role form? Here is associations
role.rb
class Role < ActiveRecord::Base
....
has_many :access_module_roles, :dependent => :destroy
has_many :access_modules, through: :access_module_roles
validates :name,:access_module_id, presence: true # I want to validate presence of access_module_ids in role form
end
access_module.rb
class AccessModule < ActiveRecord::Base
has_many :access_module_roles
has_many :roles, through: :access_module_roles
end
access_module_roles.rb
class AccessModuleRole < ActiveRecord::Base
belongs_to :access_module
belongs_to :role
end
Update
I have tried below validation and if I select one, two or all still getting an error like
"Access module ids can't be blank"
validates_presence_of :access_module_ids
Controller
def create
#role = Role.new(role_params)
respond_to do |format|
if #role.save
params[:role][:access_module_ids].each do |acmi|
AccessModuleRole.create!(:role_id => #role.id, :access_module_id => acmi) if acmi!=""
end
format.html { redirect_to roles_path, notice: 'Role was successfully created.' }
format.json { render :index, status: :created, location: #role }
else
format.html { render :new }
format.json { render json: #role.errors, status: :unprocessable_entity }
end
end
end
I found that I am doing mistake in Create method. I was inserting data to AccessModuleRole after doing save so it gets validation error while creating..And was getting parameter as nil
Corrected code:
def create
#role = Role.new(role_params)
#role.access_module_ids = params[:role][:access_module_ids]
respond_to do |format|
if #role.save
format.html { redirect_to roles_path, notice: 'Role was successfully created.' }
format.json { render :index, status: :created, location: #role }
else
format.html { render :new }
format.json { render json: #role.errors, status: :unprocessable_entity }
end
end
end
Permitted Role's attributes:
private
def role_params
params.require(:role).permit(:name,:chk_ids ,:description, :code, :is_active, :access_module_ids)
end
Now it works perfectly.. Thanks to RAJ to pointing me :)

Render action with parameters after form error

I have basic form that is accessed, for example via: http://url.com/rentals/new/dvd/10.
The problem is when form error happens I can't redirect it to the same page with the same
url segments and show the form error messages.
rentals_controller.rb:
def create
#rental = Rental.new(rental_params)
respond_to do |format|
if #rental.save
format.html { redirect_to #rental, notice: 'Rental was successfully created.' }
format.json { render :show, status: :created, location: #rental }
else
format.html { render :new }
format.json { render json: #rental.errors, status: :unprocessable_entity }
end
end
end
routes.rb
get 'rentals/new/dvd/:dvd_id' => 'rentals#new', as: :new_dvd_rental
I have the following models created:
dvd.rb
class Dvd < ActiveRecord::Base
has_many :rentals
has_many :users, through: :rentals
validates :title, presence: true
validates :year, inclusion: {in: 1900..Time.now.year.to_i}, :presence => {:message => 'Year must be from 1900 till current year.'}
validates :length, inclusion: {in: 1..999}, :presence => {:message => 'DVD length must be in minutes in range 1..999.'}
end
rental.rb
class Rental < ActiveRecord::Base
belongs_to :user
belongs_to :dvd
validates :user_id, presence: true
validates :total_price, presence: true
end
user.rb
class User < ActiveRecord::Base
has_many :rentals
has_many :dvds, through: :rentals
end
As well as rentals_controller.rb:
class RentalsController < ApplicationController
before_action :set_rental, only: [:show, :edit, :update, :destroy]
# GET /rentals
# GET /rentals.json
def index
#rentals = Rental.all
end
# GET /rentals/1
# GET /rentals/1.json
def show
end
# GET /rentals/new
def new
#rental = Rental.new
#users = User.all
#dvd = Dvd.find(params[:dvd_id])
end
# GET /rentals/1/edit
def edit
end
# POST /rentals
# POST /rentals.json
def create
#rental = Rental.new(rental_params)
respond_to do |format|
if #rental.save
format.html { redirect_to #rental, notice: 'Rental was successfully created.' }
format.json { render :show, status: :created, location: #rental }
else
format.html { render :new }
format.json { render json: #rental.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /rentals/1
# PATCH/PUT /rentals/1.json
def update
respond_to do |format|
if #rental.update(rental_params)
format.html { redirect_to #rental, notice: 'Rental was successfully updated.' }
format.json { render :show, status: :ok, location: #rental }
else
format.html { render :edit }
format.json { render json: #rental.errors, status: :unprocessable_entity }
end
end
end
# DELETE /rentals/1
# DELETE /rentals/1.json
def destroy
#rental.destroy
respond_to do |format|
format.html { redirect_to rental_url, notice: 'Rental was successfully deleted.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_rental
#rental = Rental.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def rental_params
params.require(:rental).permit(:dvd_id, :user_id, :rent_date, :return_date, :total_price, :returned)
end
end
I've tried to modify rental controller like this, but still do not know how to pass other segments like new and dvd:
render :action => "new", :dvd_id => params[:dvd_id]
Any ideas?
I think if you draw a more restful route like this
resources :dvds do
resources :rentals
end
you will get the routes like http://url.com/dvd/10/rentals/new
here you will always get dvd_id
and in rentals_controller create method look like
def create
#dvd = Dvd.find(params[:dvd_id])
#rental = Rental.new(rental_params)
respond_to do |format|
if #rental.save
format.html { redirect_to #rental, notice: 'Rental was successfully created.' }
format.json { render :show, status: :created, location: #rental }
else
format.html { render :new }
format.json { render json: #rental.errors, status: :unprocessable_entity }
end
end
end
-- Waiting for #Sanket's ideas
Routes
The issue will almost certainly be with your redirect_to method
The problem is that your controller doesn't know you're using a nested resource, and consequently when you redirect to an object, it will likely just take you to the simplest route it can find
I would try this:
def create
...
else
format.html { render your_nested_resource_path(dvd_id: params[:dvd_id], other: params[:params]) }
...
end
This allows you to send the request to the nested route, which Rails won't route to without support

Resources