I have rails version 3.2.13 and ruby version 1.9.3.
I have caught into very strange and interesting situation.
In my application have a model 'Product' with custom validator.
product.rb
class Product < ActiveRecord::Base
attr_accessible :description, :name, :price, :short_description, :user_id
validates :name, :short_description, presence: true
validates :price, :numericality => {:greater_than_or_equal_to => 0}
validate :uniq_name
belongs_to :user
belongs_to :original, foreign_key: :copied_from_id, class_name: 'Product'
has_many :clones, foreign_key: :copied_from_id, class_name: 'Product', dependent: :nullify
def clone?
self.original ? true : false
end
private
#Custom validator
def uniq_name
return if clone?
user_product = self.user.products.unlocked.where(:name => self.name).first
errors[:name] << "has already been taken" if user_product && !user_product.id.eql?(self.id)
end
end
In products controller's create action when I am trying to create new product
def create
#product = current_user.products.new(params[:product])
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render json: #product, status: :created, location: #product }
else
#product.errors[:image] = "Invalid file extension" if #product.errors[:image_content_type].present?
format.html { render action: "new" }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
Custom validator is being called when this line executed #product = current_user.products.new(params[:product]) and line # 2 of custom validator giving me error
undefined method `products' for nil:NilClass
I have inspected product object in custom validator but user_id is nil.
Why user_id is not being autoassigned?
Your help will be appreciated :)
So ... bypassing your question. Why aren't you just validating the uniqueness of name?
validates_uniqueness_of :name, :unless => :clone?
try to change .new to .build
#product = current_user.products.build(params[:product])
and be sure that you have relation in your User model
Class User < ActiveRecord::Base
has_many :products
Related
I have a very basic example
app/models/user.rb
#name string
class User < ActiveRecord::Base
has_one :project,dependent: :destroy
validates :name,presence: true
validates_associated :project
accepts_nested_attributes_for :project
end
app/models/project.rb
#name string
#user_id integer
class Project < ActiveRecord::Base
belongs_to :user
has_many :tasks,dependent: :destroy
validates :name,presence: true
validates_associated :tasks
accepts_nested_attributes_for :tasks
end
app/models/tasks.rb
#name string
#project_id integer
class Task < ActiveRecord::Base
belongs_to :project,dependent: :destroy
validates :name,presence: true
end
That's all I have in model
On the Controller end for testing purpose, I have the following code.
app/controllers/users_controller.rb
def update
#user.project_attributes = {:name => "P#rand(100)",:tasks_attributes => [{name: "Task#{rand(100)}"}]}
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User 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
Every time, I try to update a given User, I run into stack level too deep
problem
Any Clue?
Note :
Rails version 4.0.4
Ruby 2.1.2p95
Write dependent: :destroy only in one of models that share an association. If both models have dependent: :destroy in 'em, it goes for an infinite number of calls causing Stack level too deep error to occur.
I am brand new here. I have been fighting with a rails app for hours now and need an answer. I have searched and tried many suggestions related to what I am trying to accomplish, but to no avail. I got the paperclip gem running for my rails app yesterday, and it was a breeze to add an attachment to a single model. However, I defined an agnostic, polymorphic attachments table to hold attached files for all models that need this functionality.
My issue is that I cannot get the attached file to save through nested parameters. All my parameters are accepted, but the db rolls back and doesn't save (using guard). Message is: 'attachments.attachable_id'=>'can't be blank'. I need this to be the foreign key of the related table, and this has to be saved along with the attachment_type. Here's what I have:
class ReportsController < ApplicationController
def new
#report = Report.new
#report.attachments.build(attachable_id: #report.id)
end
def create
#report = Report.new(params)
#report.attachments.build
respond_to do |format|
if #report.save
format.html { redirect_to #report, notice: 'Report was successfully created.' }
format.json { render json: #report, status: :created, location: #report }
else
format.html { render action: "new" }
format.json { render json: #report.errors, status: :unprocessable_entity }
end
end
end
private
def report_params
params.require(:report).permit(:filing_year, :filing_number, :order_number, :location, :environmental_review,:biological_review, :cultural_review, :date_received, :status, attachments_attributes: [:id, :attachable_id, :attachable_type, :attachment])
end
end
And for the models:
class Attachment < ActiveRecord::Base
belongs_to :attachable, polymorphic: true
validates :attachable_id, :attachable_type, presence: true
do_not_validate_attachment_file_type :attachment
Paperclip.interpolates :attached_to do |attachment, style|
attachment.instance.attachable.class.to_s.downcase
end
has_attached_file :attachment,
:url => "/attachments/:id/:basename.:extension",
:path => ":rails_root/public/attachments/:attached_to/:id/:basename.:extension",
:default_url => "/attachments/original/no-file.txt"
end
class Report < ActiveRecord::Base
has_one :environmental_review
has_many :attachments, as: :attachable
accepts_nested_attributes_for :attachments
validates :order_number, presence: true
.
.
.
end
And view (in slim):
.report
= form_for #report do |f|
.
.
.
= f.fields_for :attachments do |a|
= a.file_field :attachment
.
.
.
Thank you.
Well for one in your create method you call #reports.attachments.build again, but don't set the assignable_id, you need to do that. The other thing you can do is add a hidden form field that has the name attachable_id.
New updates
Moved params permit responsability from model to controller and used comment_attributes instead of comments as #vinodadhikary pointed me
Using better_errors REPL, I traced the problem down to sanitize_for_mass_assignment method. When doing attributes.permitted? it returns false. But doing attributes.permit(:article_id, :name, :email, :body) returns me exactly que entry parameters!:
>> attributes
=> {"name"=>"Commenter", "email"=>"commenter#mail.com", "body"=>"Here is the comment >> body!! :D"}
>> attributes.permit(:article_id, :name, :email, :body)
=> {"name"=>"Commenter", "email"=>"commenter#mail.com", "body"=>"Here is the comment body!! :D"}
>> attributes.permitted?
=> false
Context and code
Trying to get in touch with Rails 4, I encountered a problem with (I think) strong parameters use.
I have an Article class which can have many Comments. When creating a new comment doing:
#comment = #article.comments.build(params[:comment])
I get the following error (pointing this line):
ActiveModel::ForbiddenAttributesError at /articles/1/comments
The models are the following:
class Article < ActiveRecord::Base
validates_presence_of :title, :content
validates_uniqueness_of :title
has_many :comments, :dependent => :destroy
accepts_nested_attributes_for :comments
end
Comments:
class Comment < ActiveRecord::Base
belongs_to :article
validates_presence_of :article_id, :author, :body, :content
end
Article controller have this in the private section:
def article_params
params.require(:article).permit(:title, :content, comments_attributes: [:article_id, :name, :email, :body])
end
Comments controller code is:
def create
#article = Article.find(params[:article_id])
#comment = #article.comments.build(params[:comment]) # <--- It fails here
respond_to do |format|
if #comment.save
format.html { redirect_to #comment, notice: 'Comment was successfully created.' }
format.json { render action: 'show', status: :created, location: #comment }
else
format.html { render action: 'new' }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
The methods article_params and comment_params that you have in the models belong in their respective controllers not in models. The idea is to filter the parameters passed to the model in the controller rather than in the model. Take a read on http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html, on how to allow attributes for nested attributes.
You models should be as follows:
# Articles.rb
class Article < ActiveRecord::Base
validates_presence_of :title, :content
validates_uniqueness_of :title
has_many :comments, :dependent => :destroy
accepts_nested_attributes_for :comments
end
# Comment.rb
class Comment < ActiveRecord::Base
belongs_to :article
validates_presence_of :article_id, :author, :body, :content
end
Then move the strong parameters to Articles Controller as follows:
#ArticlesController.rb
def create
#article = Article.find(params[:article_id])
#comment = #article.comments.build(params[:comment])
respond_to do |format|
if #comment.save
format.html { redirect_to #comment, notice: 'Comment was successfully created.' }
format.json { render action: 'show', status: :created, location: #comment }
else
format.html { render action: 'new' }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
private
def article_params
params.require(:article).permit(:title, :content, comments_attributes: [:article_id, :author, :email, :body, :content])
end
permit params method name should be same as model/controller
e.g if model name is "recent_post" then permit method name should be
def recent_post_params
..............
end
# article.rb
class Article < ActiveRecord::Base
has_many :article_categories, dependent: :destroy
has_many :categories, through: :article_categories
validates :title, :description, :body, presence: true
validates :categories, presence: true
validates_associated :categories
end
#articles_controller.rb
def create
#article = Article.new(params[:article])
respond_to do |format|
if #article.save
format.html { redirect_to #article, notice: 'Article was successfully created.' }
format.json { render json: #article, status: :created, location: #article }
else
format.html { render action: "new" }
format.json { render json: #article.errors, status: :unprocessable_entity }
end
end
end
I have an Article model related to one or more Categories. I want to make sure that an article is assigned to a category each time the record is saved. On update, this works fine, but on create, I get an unprocessable entity because the ArticleCategory association can't be created because it needs the id of the article. But, the id isn't set until the Article is saved. But I can't save invalid models. You get the idea. How can I create objects without sacrificing my validations?
Edit: I fixed the validation line. This is cleaned up to remove some other stuff so I accidentally deleted the presence: true
Instead of validating that an article has many categories, can you try validate the presence of the join table, like:
# article.rb
validates :article_categories, presence: true
Have a look at the Rails Guides for the proper format here:
has_many :patients, :through => :appointments
From here! I think you'll find your declarations in the improper format.
I am having that problem that my model dont want to save. I have a token field input for tags.
I have followed this guide for the token input: http://railscasts.com/episodes/258-token-fields
I get this error when I try to create a new konkurrancer:
NoMethodError in Admin/konkurrancersController#create
undefined method `class_name' for nil:NilClass
Rails.root: C:/Rails/konkurranceportalen
Application Trace | Framework Trace | Full Trace
app/models/konkurrancer.rb:15:in `tag_tokens='
app/controllers/admin/konkurrancers_controller.rb:48:in `new'
app/controllers/admin/konkurrancers_controller.rb:48:in `create'
http://pastie.org/1834194
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"yo7wcAQl81tx3zZpPP44ENPYzYRZLpgyYKY+HK3yFKM=",
"konkurrancer"=>{"name"=>"Vind en rejse",
"banner2"=>"asdasd",
"tracking"=>"sadasd",
"vaerdi"=>"12222",
"tid"=>"1 min",
"tag_tokens"=>"1",
"bedom"=>"2",
"kategori_id"=>"9",
"form"=>"Nyhedsbrev",
"partner"=>"Iqmedier",
"udtraekkes(3i)"=>"30",
"udtraekkes(2i)"=>"4",
"udtraekkes(1i)"=>"2011",
"udtraekkes(4i)"=>"08",
"udtraekkes(5i)"=>"26",
"arrangeor"=>"",
"note"=>""},
"commit"=>"Opret konkurrence"}
My konkurrancer model:
class Konkurrancer < ActiveRecord::Base
attr_accessible :name, :tag_tokens
has_many :tagsmenus
has_many :tags, :through => :tagsmenus
attr_reader :tag_tokens
def tag_tokens=(ids)
self.tag_ids = ids.split(",")
end
end
My tag model:
class Tag < ActiveRecord::Base
has_many :tagsmenus
has_many :konkurrancers, :through => :tagsmenus
has_friendly_id :name, :use_slug => true
before_save :assign_cached_slug, :unless => :cached_slug?
protected
def assign_cached_slug
self.cached_slug = self.name.gsub(/\s+/, '_').gsub(/[^\w\-]/, '')
end
end
My tagmenu model:
class Tagsmenu < ActiveRecord::Base
end
My controller:
def new
#konkurrancer = Konkurrancer.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #konkurrancer }
end
end
# POST /konkurrancers
# POST /konkurrancers.xml
def create
#konkurrancer = Konkurrancer.new(params[:konkurrancer])
respond_to do |format|
if #konkurrancer.save
format.html { redirect_to(:admin_konkurrancers, :notice => 'Konkurrancer was successfully created.') }
format.xml { render :xml => :admin_konkurrancers, :status => :created, :location => #konkurrancer }
else
format.html { render :action => "new" }
format.xml { render :xml => #konkurrancer.errors, :status => :unprocessable_entity }
end
end
end
I have created the join table and the model and also added the relation to my tag model.
Your model has some conflicting statements. You first define:
attr_accessor ... :tag_tokens
then later have:
attr_reader :tag_tokens
which is not necessary given the first line or vice versa given that later you have a deinition for the setter:
def tag_tokens(ids)
self.tag_ids = ids.split(',')
end
I don't see tag_ids defined either given it is not a column in your table. You should probably remove the attr_accessor definition for the tag_tokens and then define the tag_ids method for starters.
class Tagsmenu < ActiveRecord::Base
belongs_to :konkurrancer
belongs_to :tag
end