Rails Association, undefined method - ruby-on-rails

I'm working on two simple model
class Permit < ApplicationRecord
belongs_to :user, optional: true
validates :user_id, presence: true
end
class User < ApplicationRecord
has_many :permits
has_secure_password
end
In rail console, I am doing the following
user = User.find(10)
Which assign the a User object to user.
But when I want to create a permit for the user it gives this error
NoMethodError in PermitsController#create
undefined method `Permit' for #<User:0xaaa3528>
How to solve this problem? Thanks!
This is my permit_controller which the problem I believe is in CREATE action
class PermitsController < ApplicationController
before_action :set_permit, only: [:show, :destroy]
def index
#permits = Permit.all
end
def new
#permits = Permit.new
end
def create
#permits = user.permits.create(permit_params)
if #permits.save
redirect_to #permits
else
redirect_to contact_path
end
end
def destroy
Permit.destroy_all(user_id: 1)
respond_to do |format|
format.html { redirect_to users_url, notice: 'Permit was successfully canceled.' }
format.json { head :no_content }
end
end
def show
#permits = Permit.find(params[:id])
end
def update
respond_to do |format|
if #permits.update(user_params)
format.html { redirect_to #user, notice: 'Permit was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_permit
#permits = Permit.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def permit_params
params.require(:permit).permit(:vehicle_type, :name, :studentid, :department, :carplate, :duration, :permitstart, :permitend)
end
end
Permit/new/html.erb
<% provide(:title, 'New Permit') %>
<h1>Permit Application</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#permits) do |f| %>
<%= f.label :"Vehicle" %>
<%= f.text_field :vehicle_type, class: 'form-control' %>
<%= f
.label :"License Plate" %>
<%= f.text_field :carplate, class: 'form-control' %>
<%= f.label :"Student ID" %>
<%= f.text_field :studentid, class: 'form-control' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :"Department of applicant" %>
<%= f.text_field :department, class: 'form-control' %>
<%= f.label :permit_start %>
<%= f.date_select :permitstart, class: 'form-control' %>
<%= f.label :permit_end %>
<%= f.date_select :permitend, class: 'form-control' %>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>

You have to create the permit as follows:
user.permits.create(..)

You set before_action :set_permit, only: [:show, :destroy], that mean you can only use #permits (should be singular) in those 2 methods, I see you use #permits in update. Didn't see you permit user_id for permit_params.
In create #permits = user.permits.create(permit_params) you didn't declare user.
You set before_action :set_permit, only: [:show, :destroy], so why don't #permits.destroy
belongs_to :user, optional: true
validates :user_id, presence: true
optional: true but user_id with presence: true, isn't it conflict? how do you require an optional?
You have more bugs to deal thank you thought.
Good luck

I'm not a rails expert but i'll try to help out with this and try to work out the possible places of errors.
In your schema ensure that in your Permits table you have a user_id column.
Test out that the association works in rails console. user = User.first
user.permits
If it works, great it means your association is working.
Test your permits create function on its own first without including "User" in it. Add the user aspect in only after your permits is fully functioning!
Hope it helps! Cheers

Related

Rails: One-to-One relationship not working

I am working on a Rails project and I am using namespacing for the models and controllers. That is the child models (admin and student) are put in a directory called user and the controllers are put in a directory called users.
I also have admins_controller and students_controller that use the admin model and the student model respectively. These controllers are namespaced using users directory.
I then have a personal_info model that contains more details about the user, such as gender, age, date of birth. The personal_info table has a one-to-one relationship with the user model.
Here's my code;
Personal Info model:
class PersonalInfo < ApplicationRecord
belongs_to :user
end
User model:
class User < ApplicationRecord
has_secure_password
has_one :personal_info, class_name: 'PersonalInfo', dependent: :destroy
accepts_nested_attributes_for :personal_info, allow_destroy: true
end
Admin model:
class User::Admin < User
end
Admin Controller:
class Users::AdminsController < ApplicationController
before_action :set_admin, only: [:show, :edit, :update, :destroy]
# GET /admins
# GET /admins.json
def index
#admins = User::Admin.all
end
# GET /admins/1
# GET /admins/1.json
def show
end
# GET /admins/new
def new
#admin = User::Admin.new
#admin.build_personal_info
end
# GET /admins/1/edit
def edit
end
# POST /admins
# POST /admins.json
def create
#admin = User::Admin.new(admin_params)
#admin.build_personal_info
respond_to do |format|
if #admin.save
format.html { redirect_to users_admin_path(#admin), notice: 'Admin was successfully created.' }
format.json { render :show, status: :created, location: users_admin_path(#admin) }
else
format.html { render :new }
format.json { render json: #admin.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /admins/1
# PATCH/PUT /admins/1.json
def update
respond_to do |format|
if #admin.update(admin_params)
format.html { redirect_to users_admin_path(#admin), notice: 'Admin was successfully updated.' }
format.json { render :show, status: :ok, location: users_admin_path(#admin) }
else
format.html { render :edit }
format.json { render json: #admin.errors, status: :unprocessable_entity }
end
end
end
# DELETE /admins/1
# DELETE /admins/1.json
def destroy
#admin.destroy
respond_to do |format|
format.html { redirect_to users_admins_url, notice: 'Admin was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_admin
#admin = User::Admin.find(params[:id])
end
# Only allow a list of trusted parameters through.
def admin_params
params.require(:user_admin).permit(
:email, :password, :role_id,
personal_info_attributes: [ :id, :first_name, :last_name, :phone,
:gender, :dob, :address, :city, :state,
:country ]
)
end
end
Routes:
Rails.application.routes.draw do
namespace :users do
resources :admins
resources :students
end
end
_form.html.erb:
<% if #admin.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#admin.errors.count, "error") %> prohibited this admin from being saved:</h2>
<ul>
<% #admin.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= fields_for :personal_info do |form| %>
<div class="field">
<%= form.label :first_name %>
<%= form.text_field :first_name %>
</div>
<div class="field">
<%= form.label :last_name %>
<%= form.text_field :last_name %>
</div>
<% end %>
<div class="field">
<%= form.label :email %>
<%= form.text_field :email %>
</div>
<%= fields_for :personal_info do |form| %>
<div class="field">
<%= form.label :phone %>
<%= form.text_field :phone %>
</div>
<% end %>
<div class="field">
<%= form.label :password %>
<%= form.text_field :password %>
</div>
<div class="actions">
<%= form.submit %>
</div>
new.html.erb:
<h1>Editing Admin</h1>
<%= form_with(model: #admin, url: users_admins_path, local: true) do |form| %>
<%= render partial: 'form', admin: #admin, locals: { form: form } %>
<% end %>
<%= link_to 'Back', users_admins_path %>
However, when I try to create a new admin or update an already existing admin after adding inputs to the displayed form no Personal Info data is saved on the database. They are all nil.
PersonalInfo Load (0.3ms) SELECT "personal_infos".* FROM "personal_infos" WHERE "personal_infos"."user_id" = $1 LIMIT $2 [["user_id", 6], ["LIMIT", 1]]
=> #<PersonalInfo id: 2, first_name: nil, last_name: nil, phone: nil, gender: nil, dob: nil, address: nil, city: nil, state: nil, country: nil, user_id: 6, created_at: "2020-06-26 13:37:16", updated_at: "2020-06-26 13:37:16">
I have tried to get this resolved, but no luck yet. Any form of help will be highly appreciated.
you should call your fields_for from admin form as:
<%= form.fields_for :personal_info do |form| %>
otherwise it sends these params independent from admin, rather than nested as you'd like.
fwiw, for better reading, I would consider rename your inner personal_info form variable to not clash with admin form variable. But that's just a suggestion. :)

How to pass an account_id through a nested form in Rails

I'm trying to set up a nested form in rails and both the parent and child objects in the form need to have an "Account ID" so that they are both scoped to the current user's account, but I can't figure out how to pass the current user's Account ID for the child object through the nested form. I keep getting a validation error of "Account id must be present" for the nested object.
The parent form is "Product" and I'm trying to nest "Options" into the Product.new form.
I'm trying to do something like this:
#product.options.account_id = current_user.account.id
But it's not working.
Here is the Product model:
class Product < ApplicationRecord
belongs_to :account
has_many :options, dependent: :destroy
accepts_nested_attributes_for :options, allow_destroy: true
validates :account_id, presence: true
validates :name, presence: true, length: { maximum: 120 }
end
And options model:
class Option < ApplicationRecord
belongs_to :account
belongs_to :product
has_many :option_values, dependent: :destroy
validates :account_id, presence: true
validates :name, presence: true,
length: { maximum: 60 }
end
Here's how I'm nesting "Options" into the Product form:
<%= form.fields_for :options do |builder| %>
<fieldset class='form-group'>
<%= builder.label :name, 'Add option(s)' %>
<%= builder.text_field :name %>
<small id="optionHelp" class="form-text text-muted">
(e.g. "Sizes" or "Color")
</small>
</fieldset>
<% end %>
And here is my ProductsController:
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
before_action :restrict_access
def index
#products = Product.where(:account_id => current_user.account.id).all
end
def show
end
def new
#product = Product.new
#product.options.build
end
def edit
end
def create
#account = current_user.account
#product = #account.products.build(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
else
format.html { render :new }
end
end
end
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to #product, notice: 'Product was successfully updated.' }
else
format.html { render :edit }
end
end
end
def destroy
#product.destroy
respond_to do |format|
format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }
end
end
private
def set_product
if Product.find(params[:id]).account_id == current_user.account.id
#product = Product.find(params[:id])
else
redirect_to dashboard_path
end
end
def restrict_access
if index
authorize #products
else
authorize #product
end
end
def product_params
params.require(:product).permit(:account_id, :name,
options_attributes: [:id, :account_id, :name ])
end
end
What is the correct way to do this?
Alternatively, you can pass a hidden_field with form and nested_form as given below: -
<%= form_for #product do |form|%>
<%= form.fields_for :options do |builder| %>
<fieldset class='form-group'>
<%= builder.label :name, 'Add option(s)' %>
<%= builder.text_field :name %>
<small id="optionHelp" class="form-text text-muted">
(e.g. "Sizes" or "Color")
</small>
<%=builder.hidden_field :account_id, value: current_user.account.id%>
</fieldset>
<% end %>
<%= form.hidden_field :account_id, value: current_user.account.id%>
<%end%>
Other than this you can set account_id at the controller
def new
##product = Product.new
#product = current_user.account.products.new
#product.options.build(account_id: current_user.account.id)
end
The easiest option would be to add a hidden field into both forms:
https://apidock.com/rails/ActionView/Helpers/FormHelper/hidden_field
So in your case, for the options form, something like:
<%= form.fields_for :options do |builder| %>
<fieldset class='form-group'>
<%= builder.label :name, 'Add option(s)' %>
<%= builder.hidden_field :account_id, value: current_account.id %>
<%= builder.text_field :name %>
<small id="optionHelp" class="form-text text-muted">
(e.g. "Sizes" or "Color")
</small>
</fieldset>
<% end %>
This will give you access to the [:option][:account_id] param, which will match the current user's.

Rails 4 - Fields Which are Not a Part of a Model in Form

I'm having problem with some attributes which are not a part of my model. I'm trying to design a simple payment page as you can see below, all the fiels are represented in my database, except card_number and card_verification for security reasons.
When I load the page it is throwing an error:
undefined method `card_number' for #<Order:0x00000004eddb00>
undefined method `card_verification' for #<Order:0x00000004eddb00>
What's wrong with my form?
Thanks.
The Form:
<%= simple_form_for(#order, html:{class: "well"}) do |f| %>
<%= f.input :first_name %>
<%= f.input :last_name %>
<%= f.input :card_type, collection: ["Visa", "MasterCard"] %>
<%= f.input :card_expires_on %>
<%= f.input :card_number %>
<%= f.input :card_verification %>
<%= f.button :submit %>
<% end %>
order.rb:
class Order < ActiveRecord::Base
belongs_to :user
belongs_to :participation
end
orders_controller.rb:
class OrdersController < ApplicationController
before_action :set_order, only: [:show, :edit, :update, :destroy]
def new
#order = Order.new
end
def create
#order = Order.new(order_params)
respond_to do |format|
if #order.save
format.html { redirect_to #order, notice: 'Order was successfully created.' }
format.json { render action: 'show', status: :created, location: #order }
else
format.html { render action: 'new' }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
private
def set_order
#order = Order.find(params[:id])
end
def order_params
params.require(:order).permit(:ip_address, :first_name, :last_name, :card_type, :card_expires_on, :user_id, :participation_id)
end
end
I think the part that is throwing the error should be this in your form:
<%= f.input :card_number %>
<%= f.input :card_verification %>
You could try adding:
attr_accessor :card_number, :card_verification
to your model.

ActionView::Template::Error: undefined method `StoreTitle' for nil:NilClass

Updated version - I have taken the initial advice provided (thanks for that!) but I'm still having the same issue. I have updated everything below.
I have two models, products that belong to a store.
I'm attempting to display a related object's column (Store.name) in a couple of views for products that belong to a store and can't seem to get the store to save correctly. Please note: Still very new to this and learning.
Model for Product:
class Product < ActiveRecord::Base
belongs_to :store
validates_presence_of :name, :url, :price
end
Model for Store:
class Store < ActiveRecord::Base
has_many :products
has_many :pins, through: :products
accepts_nested_attributes_for :products
end
Controller for Product:
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
# GET /products
# GET /products.json
def index
#products = Product.all
end
# GET /products/1
# GET /products/1.json
def show
end
# GET /products/new
def new
#product = Product.new
#stores = Store.all
end
# GET /products/1/edit
def edit
end
# POST /products
# POST /products.json
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render action: 'show', status: :created, location: #product }
else
format.html { render action: 'new' }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /products/1
# PATCH/PUT /products/1.json
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to #product, notice: 'Product was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# DELETE /products/1
# DELETE /products/1.json
def destroy
#product.destroy
respond_to do |format|
format.html { redirect_to products_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product
#product = Product.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def product_params
params.require(:product).permit(:name, :description, :imageurl, :url, :price, :Store_id)
end
end
Form for products
<%= form_for(#product) do |f| %>
<% if #product.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#product.errors.count, "error") %> prohibited this product from being saved:</h2>
<ul>
<% #product.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="form-group">
<%= f.label :name, "Name" %>
<%= f.text_field :name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :description, "Description" %>
<%= f.text_area :description, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :imageurl, "Image" %>
<%= f.text_field :imageurl, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :url, "Web Address" %>
<%= f.text_field :url, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :price, "Price" %>
$<%= f.text_field :price, class: "form-control" %>
</div>
<div class="form-group">
<%= collection_select(:product, :Store_id, Store.all, :id, :name, {:prompt=> "Select A Store"}, {:class => "form-control"} ) %>
</div>
<div class="form-group">
<%= f.submit class: "btn btn-primary" %>
</div>
<% end %>
<%= params.inspect %>
View for products (show.html.erb):
<p id="notice"><%= notice %></p>
<p>
<strong>Name:</strong>
<%= #product.name %>
</p>
<p>
<strong>Description:</strong>
<%= #product.description %>
</p>
<p>
<strong>Store Id:</strong>
<%= #product.Store_id %>
</p>
<p>
<strong>Store Name:</strong>
<%= #product.store.try(:name) %>
</p>
<p>
<strong>Image:</strong>
<%= #product.imageurl %>
</p>
<p>
<strong>Url:</strong>
<%= #product.url %>
</p>
<p>
<strong>Price:</strong>
$<%= #product.price %>
</p>
<%= params.inspect %>
<%= link_to 'Edit', edit_product_path(#product) %> |
<%= link_to 'Back', products_path %>
You'll notice that I have .try in place where I'm referencing product.store.name from product.store so that I stop getting the error listed in the subject of this post.
When I look up the product I'm viewing using the console I see that Store_id: 2
When I look up the store with id of 2 I see that Store_id: 1 - so there is a value present there.
I printed params on the show view and only get this: {"action"=>"show", "controller"=>"products", "id"=>"2"}.
Can anyone find what I'm missing in this whole set up to get product.store.name to display in my product views? Let me know if I can provide more info!
First, you should be using snake_case for attributes. UpperCamelCase is reserved for constant names in Ruby, that includes things like classes and modules. Update your code to not use UpperCamelCase style naming for attributes (eg. ProdDesc, ProdImageUrl). Also it is unnecessary to use prefixes like Prod* for attributes.
So instead of
class Product < ActiveRecord::Base
belongs_to :store
validates_presence_of :ProdTitle, :ProdUrl, :ProdPrice
end
Your class will look like this:
class Product < ActiveRecord::Base
belongs_to :store
validates_presence_of :title, :url, :price
end
You can read more about Ruby and Rails naming conventions here:
http://rubylearning.com/satishtalim/ruby_names.html
https://github.com/bbatsov/rails-style-guide
I suspect that this causes the problem. You will also need to rename columns in database and change your controller and view code to reflect the change. If you can, please start again with your application.
You have taken name for field In database which are non conventional. By default The active record looks for store_id field in product table . Field name in a table should be snake case .so now you have to explicitly tell in product model the foreign key for store model

Nested model, update_attributes not working

I am having a hard time with what I thought would be an textbook update example. Searched SO but couldn't find the answer. In short, when I click submit the user_id in the profile model gets wiped out and no other data gets saved. I am using Rails 3.2.2.
Here is what I have...
User model...
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :profile_attributes
has_one :profile
accepts_nested_attributes_for :profile
end
Profile model...
class Profile < ActiveRecord::Base
validates :first_name, :presence => true
validates :last_name, :presence => true
belongs_to :user
attr_accessible :first_name, :last_name
end
Users controller...
class UsersController < ApplicationController
def new
#user = User.new
#user.accounts_users.build()
#user.build_profile()
respond_to do |format|
format.html # new.html.erb
format.json { render json: #user }
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, notice: 'Profile was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
end
Nested form...
<%= form_for #user, :validate => true do |f| %>
<%= f.fields_for :profile do |p| %>
<fieldset>
<div class="field">
<%= p.label :first_name %>
<%= p.text_field :first_name %>
</div>
<div class="field">
<%= p.label :last_name %>
<%= p.text_field :last_name %>
</div>
<% end %>
<div class="field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class="actions">
<%= f.submit 'Edit Profile', :class => "btn btn-large btn-success" %>
<%= cancel %>
</div>
</fieldset>
<% end %>
Edit: I edited the UsersController to include the new action. Why would the new action affect the edit/update actions?
I had a similar problem before. Can we see the code for your new action? Do you have #user.build_profile in there (right beneath #user = User.new)? Or is the new action working fine?

Resources