Rails 4- Complex model validation (has_many, through) - ruby-on-rails

I am trying to validate the "name" attribute in my Ingredient model. However, when I add validates :name, :uniqueness => true in my Ingredient model, it does not validate the name (can insert ingredient with same name). The ingredient model has a complex relationship with other model. Please see the code below.
My ultimate goal is to allows user to create ingredient if the ingredient does not exist in the Ingredient table. If the ingredient already existed, used the ingredient id for quantity. If anyone has any idea how to achieve that, please provide the solution to it. Thanks in advance.
ingredient.rb
class Ingredient < ActiveRecord::Base
has_many :quantities
has_many :recipes, through: :quantities
validates :name, :presence => true, :uniqueness => true
end
quantity.rb
class Quantity < ActiveRecord::Base
belongs_to :ingredient
belongs_to :recipe
accepts_nested_attributes_for :ingredient,
:reject_if => :all_blank
validates :ingredient, :uniqueness => true
end
recipe.rb
class Recipe < ActiveRecord::Base
has_many :quantities,
dependent: :destroy
has_many :ingredients,
:through => :quantities
accepts_nested_attributes_for :quantities,
reject_if: :all_blank,
allow_destroy: true
accepts_nested_attributes_for :ingredients
end
view section for creating Ingredient:
%strong Ingredients:
%fieldset#recipe-ingredients
%br
= f.fields_for :quantities do |builder|
= render 'recipe/quantity_fields', f: builder
.links
= link_to_add_association 'add ingredient', f, :quantities, 'data-association-insertion-node' => '#recipe-ingredients', 'data-assoication-insertion-moethod' => "append", :wrap_object => Proc.new{|quantity| quantity.ingredient.build ; quantity}
%br
Ingredient controller:
class IngredientController < ApplicationController
before_action :set_ingredient, only: [:show, :edit, :update, :destroy]
# GET /ingredients
# GET /ingredients.json
def index
#ingredients = Ingredient.all
end
# GET /ingredients/1
# GET /ingredients/1.json
def show
end
# GET /ingredients/new
def new
#ingredient = Ingredient.new
end
# GET /ingredients/1/edit
def edit
end
def create
#ingredient = Ingredient.new(ingredient_params)
respond_to do |format|
if #ingredient.save
format.html { redirect_to #ingredient, notice: 'Ingredient was successfully created.' }
format.json { render :show, status: :created, location: #ingredient }
else
format.html { render :new }
format.json { render json: #ingredient.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /ingredients/1
# PATCH/PUT /ingredients/1.json
def update
respond_to do |format|
if #ingredient.update(ingredient_params)
format.html { redirect_to #ingredient, notice: 'Ingredient was successfully updated.' }
format.json { render :show, status: :ok, location: #ingredient }
else
format.html { render :edit }
format.json { render json: #ingredient.errors, status: :unprocessable_entity }
end
end
end
# DELETE /ingredients/1
# DELETE /ingredients/1.json
def destroy
#ingredient.destroy
respond_to do |format|
format.html { redirect_to ingredients_url, notice: 'Ingredient was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_ingredient
#ingredient = Ingredient.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def ingredient_params
params.require(:ingredient).permit(:name)
end
end

It seems like a known issue probably a bug. Check this: https://github.com/rails/rails/issues/20676

Related

How preview multiple images with nested form rails

I'm using paperclip to upload images and nested form.
I want to preview images as input, not just an image.
This is my form.
= nested_form_for #anime, html:{multipart:true} do |f|
- if #anime.errors.any?
#error_explanation
%h2= "#{pluralize(#anime.errors.count, "error")} prohibited this anime from being saved:"
%ul
- #anime.errors.full_messages.each do |msg|
%li= msg
.field
= f.label :name
= f.text_area :name
.fields
=f.fields_for :images do |i|
=i.file_field :content
=i.link_to_remove "Remove"
.field
=f.link_to_add "add Image", :images
.actions
= f.submit 'Save'
This is my model.
class Image < ApplicationRecord
belongs_to :imageable, :polymorphic => true, optional:true
has_attached_file :content, :styles=>{:medium => "300x300>", :thumb => "100x100>"}
validates_attachment_content_type :content, :content_type => %w(image/jpeg image/jpg image/png)
end
class Anime < ApplicationRecord
has_many :images, :as => :imageable, dependent: :destroy
accepts_nested_attributes_for :images, :allow_destroy => true
end
This is my controller
class AnimesController < ApplicationController
before_action :set_anime, only: [:show, :edit, :update, :destroy]
# GET /animes
# GET /animes.json
def index
#animes = Anime.all
end
# GET /animes/1
# GET /animes/1.json
def show
end
# GET /animes/new
def new
#anime = Anime.new
#anime.images.build
end
# GET /animes/1/edit
def edit
end
# POST /animes
# POST /animes.json
def create
#anime = Anime.new(anime_params)
respond_to do |format|
if #anime.save
format.html { redirect_to #anime, notice: 'Anime was successfully created.' }
format.json { render :show, status: :created, location: #anime }
else
format.html { render :new }
format.json { render json: #anime.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /animes/1
# PATCH/PUT /animes/1.json
def update
respond_to do |format|
if #anime.update(anime_params)
format.html { redirect_to #anime, notice: 'Anime was successfully updated.' }
format.json { render :show, status: :ok, location: #anime }
else
format.html { render :edit }
format.json { render json: #anime.errors, status: :unprocessable_entity }
end
end
end
# DELETE /animes/1
# DELETE /animes/1.json
def destroy
#anime.destroy
respond_to do |format|
format.html { redirect_to animes_url, notice: 'Anime was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_anime
#anime = Anime.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def anime_params
params.require(:anime).permit(:name, images_attributes: [:content])
end
end
Please help me
Thanks so much.
ignore this message

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....

Polymorphic Associations not saving values

My models:
class LineItem < ActiveRecord::Base
attr_accessible :itemable
belongs_to :itemable, polymorphic: true
belongs_to :lead
belongs_to :cart
end
class House < ActiveRecord::Base
has_many :line_items, :as => :itemable
end
class Appartment < ActiveRecord::Base
has_many :line_items, :as => :itemable
end
line_item_controller:
def create
#line_item = #cart.line_items.build item: #object
respond_to do |format|
if #line_item.save
format.html { redirect_to #line_item.cart,
notice: 'Vakantiehuis toegevoegd in lijst.' }
format.json { render action: 'show',
status: :created, location: #line_item }
else
format.html { render action: 'new' }
format.json { render json: #line_item.errors,
status: :unprocessable_entity }
end
end
end
private
def create_object
id = params[:house_id] || params[:appartment_id]
model = "House" if params[:house_id]
model = "Apartment" if params[:apartment_id]
model = model.constantize
#object = model.find(id)
end
When a new item list is created the values in de table line_items (itemable_id, itemable_type) are not saved. What am i doing wrong here? thanks..remco
try replace:
#cart.line_items.build item: #object
to:
#cart.line_items.build itemable: #object

User returning nil. undefined method `username' for nil:NilClass

Here is the error I receive:
Here is a Gist of the files (some of you may find this easier to read):
https://gist.github.com/drichards2013/7902811
Here is index.html.erb:
<%= render 'pages/home' if !user_signed_in? %>
<div id="things" class="transitions-enabled">
<% #things.each do |thing| %>
<div class='panel panel default'>
<div class="box">
<%= link_to image_tag(thing.image.url(:medium)), thing %>
<div class='panel-body'>
<% if thing.link.blank? %>
<strong><%= thing.title %></strong>
<% else %>
<strong><%= link_to thing.title, "http://#{thing.link}"%></strong>
<% end %>
<p><%= thing.description %></p>
By <%= link_to thing.user.username, user_path(thing.user) %>
<% if thing.user == current_user %>
<%= link_to edit_thing_path(thing) do %>
<span class='glyphicon glyphicon-edit'></span>
<% end %>
<%= link_to thing_path(thing), method: :delete, data: { confirm: 'Are you sure?' } do %>
<span class='glyphicon glyphicon-trash'></span>
<% end %>
</div>
<% end %>
</div>
</div>
<% end %>
</div>
<%= will_paginate #posts, renderer: BootstrapPagination::Rails, class: 'pull-left' %>
Here is thing.rb:
class Thing < ActiveRecord::Base
belongs_to :user
default_scope -> { order('created_at DESC') }
has_attached_file :image, :styles => { :large => '500x500>', :medium => '300x300>', :thumb => '100x100>' }
validates :image, presence: true
validates :title, presence: true, length: { minimum: 5, maximum: 50 }
# Returns microposts from the users being followed by the given user.
def self.from_users_followed_by(user)
followed_user_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
where("user_id IN (#{followed_user_ids}) OR user_id = :user_id",
user_id: user.id)
end
end
class ThingsController < ApplicationController
before_action :set_thing, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
# GET /things
# GET /things.json
def index
#things = Thing.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 50)
end
# GET /things/1
# GET /things/1.json
def show
end
# GET /things/new
def new
#thing = current_user.things.build
end
# GET /things/1/edit
def edit
end
# POST /things
# POST /things.json
def create
#thing = current_user.things.build(thing_params)
respond_to do |format|
if #thing.save
format.html { redirect_to #thing, notice: 'Thing was successfully created.' }
format.json { render action: 'show', status: :created, location: #thing }
else
format.html { render action: 'new' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /things/1
# PATCH/PUT /things/1.json
def update
respond_to do |format|
if #thing.update(thing_params)
format.html { redirect_to #thing, notice: 'Thing was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# DELETE /things/1
# DELETE /things/1.json
def destroy
#thing.destroy
respond_to do |format|
format.html { redirect_to things_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_thing
#thing = Thing.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def thing_params
params.require(:thing).permit(:title, :description, :image, :link)
end
end
Here is things_controller.rb:
class ThingsController < ApplicationController
before_action :set_thing, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
# GET /things
# GET /things.json
def index
#things = Thing.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 50)
end
# GET /things/1
# GET /things/1.json
def show
end
# GET /things/new
def new
#thing = current_user.things.build
end
# GET /things/1/edit
def edit
end
# POST /things
# POST /things.json
def create
#thing = current_user.things.build(thing_params)
respond_to do |format|
if #thing.save
format.html { redirect_to #thing, notice: 'Thing was successfully created.' }
format.json { render action: 'show', status: :created, location: #thing }
else
format.html { render action: 'new' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /things/1
# PATCH/PUT /things/1.json
def update
respond_to do |format|
if #thing.update(thing_params)
format.html { redirect_to #thing, notice: 'Thing was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# DELETE /things/1
# DELETE /things/1.json
def destroy
#thing.destroy
respond_to do |format|
format.html { redirect_to things_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_thing
#thing = Thing.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def thing_params
params.require(:thing).permit(:title, :description, :image, :link)
end
end
Here is user.rb:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, #:recoverable,
:rememberable, :trackable, :validatable
has_many :things
validates :name, presence: true, length: { minimum: 2, maximum: 20}
validates :username, presence: true, length: { minimum: 2, maximum: 20}
validates :username, uniqueness: true
validates :email, presence: true
validates :email, uniqueness: true
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" , :nav => "25x25"}
extend FriendlyId
friendly_id :username
def show
end
#follow features
has_many :followed_users, through: :relationships, source: :followed
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :followed_users, through: :relationships, source: :followed
def following?(other_user)
relationships.find_by(followed_id: other_user.id)
end
def follow!(other_user)
relationships.create!(followed_id: other_user.id)
end
def unfollow!(other_user)
relationships.find_by(followed_id: other_user.id).destroy!
end
def feed
Thing.from_users_followed_by(self)
end
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
end
Here is users_controller.rb:
class UsersController < ApplicationController
def show
#user = User.find_by_username(params[:id])
end
def user_params
params.require(:user).permit(:avatar)
end
def following
#title = "Following"
#user = User.find_by_username(params[:id])
#users = #user.followed_users.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find_by_username(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
end
What I have tried
I went to the console, did thing = Thing.last, then tried to call thing.user, and that returned nil. So it appears the user_id isn't saving. How can I make that happen?
This is where the "try" method is your friend.
You're trying to get the "username" value from your user, which is missing.
If you were to write your line like:
thing.user.try(:username)
Then it would not crash, and it would fetch the username if user existed.
Your user is not saving because your one-to-many relationship is not set up. You might want to add a user_id to your things table.
Fixed by adding :user_id to the thing_params method in my things controller.
def thing_params
params.require(:thing).permit(:title, :description, :image, :link, :user_id)
end

undefined method `title' for nil:NilClass in mailer

I am pretty new to ror, and I run into some issue with my application:
I have a ticket model and a comment model,
class Ticket < ActiveRecord::Base
attr_accessible :content, :title, :user, :priority, :category, :status
validates_presence_of :content, :title, :category
has_many :comments, dependent: :destroy
accepts_nested_attributes_for :comments
end
class Comment < ActiveRecord::Base
attr_accessible :content, :ticket_id, :user
belongs_to :ticket
end
Now I want to send a mail when a comment is created:
In comment controller:
def create
#comment = Comment.new(params[:comment])
respond_to do |format|
if #comment.save
TicketMailer.ticket_commented(#comment).deliver
format.html { redirect_to #comment, notice: 'Comment was successfull created.' }
format.json { render json: #comment, status: :created, location: #comment }
else
format.html { render action: "new" }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
Then in mailer:
class TicketMailer < ActionMailer::Base
default from: "helpdesk#testing.com"
def ticket_commented(comment)
#comment = comment
#ticket = Ticket.find_by_id(#comment.id)
mail(:to => #comment.user, :subject => 'New comment')
end
end
But when I try to call
<%= #ticket.title %>
In view, I got this error: undefined methodtitle' for nil:NilClass`
Did I do something wrong? Or how can I pass #ticket into the mailer correctly?
In your mailer you're trying to find a ticket by providing the id of a comment, change the line #ticket = Ticket.find_by_id(#comment.id) to #ticket = #comment.ticket

Resources