Why is accepts_nested_attributes_for not working as expected? - ruby-on-rails

I am trying to figure out why my create is not working. If I have the following two models below
class Product < ActiveRecord::Base
belongs_to :product_template
has_many :presentations, through: :product_presentations
has_many :product_presentations
accepts_nested_attributes_for :product_presentations
validates :start_date, :product_template_id, presence: true
validates :start_date, uniqueness: true
end
class ProductPresentation < ActiveRecord::Base
belongs_to :product
belongs_to :course
belongs_to :presentation
validates_presence_of :product_id, :course_id, :presentation_id
validates_uniqueness_of :presentation_id, :scope => :product_id
end
And I enter the following into my console.
product = Product.new(
{"start_date"=>"Sat, 06 Sep 2014 00:00:00 +0200",
"product_template_id"=>"5", "product_presentations_attributes"=>{
"0"=>{"course_id"=>"1", "presentation_id"=>"1"},
"1"=>{"course_id"=>"2", "presentation_id"=>"2"}}})
It should save, but I get the error below
#<ActiveModel::Errors:0x007f9eaeb9d698 #base=#<Product id: nil, product_template_id: 5, start_date: "2014-09-05 22:00:00", created_at: nil, updated_at: nil>, #messages={:"product_presentations.product_id"=>["can't be blank"]}>
Now I understand that there is not product_id in the product_presentations_attributes hash, but I thought this would be rails automigically populated because it's being created via the product.
I had this working and now I can't figure out how I messed it up. Any help appreciated.

Cool ... after posting this, reading over my own post I tried to remove the validation for the product_id presence.
This seems to fix the error.
I am guessing that this is a limitation of accepts_nested_attributes_for in rails - the parent object cannot have it's foreign key validated in the model.

Related

Ruby on rails Many to many relationship with through for creating data using active records

I have looked into most of the question and answer related to this has_many :through relationship but i am not able to find any question that deals with has many :through with validation. I would be really glad if someone would help me with this validation issue i am having.
Here is my models
class Dev < ApplicationRecord
validates :name, uniqueness: true, presence: true
validates :abbrev, uniqueness: true, presence: true
has_many :social_dev
has_many :social_ads, through: :social_dev
end
class SocialAds < ApplicationRecord
has_many :social_dev, dependent: :destroy
has_many :devs, through: :social_dev
validates :devs, :presence => true
end
class SocialDev < ApplicationRecord
belongs_to :social_ads
belongs_to :dev
end
I am basically using rspec to create some test scenario for which i am trying to write a active record.
How can i create an active record which would allow me to create record in join table and in main table. Currently i am getting - Devs cannot be blank error.
dev = Dev.find_by_name('Developer')
SocialDev.create!(
dev: dev,
social_ads: SocialAds.create!(
language: language,
gender: gender,
start_date: start_date,
name: name,
tax: tax,
end_date: end_date
)
)
I think the problem is that you are trying to create the SocialAds before you actually set the SocialDev on it.
Try this:
dev = Dev.find_by_name('Developer')
# note it's a has_many, it should be plural
dev.social_devs.create(
# note it's a "belongs_to", it should be singular
social_ad: SocialAd.new(
language: language,
gender: gender,
start_date: start_date,
name: name,
tax: tax,
end_date: end_date
)
)
Now the SocialAd object will be validated when the SocialDev is being validated and not before.
Note that you are not following rails conventions for naming and it can also affect your validations:
SocialAd instead of SocialAds
belongs_to :social_ad instead of belongs_to :social_ads
has_many :social_devs instead of has_many :social_dev
You can create both the model and its join table records using rails' create method.
SocialAd.create(devs: Dev.where(name: "foo"))
Rails is smart enough to know that devs is a has_many association and it will create the corresponding SocialDev join records. It will also satisfy your validates :devs, :presence => true validation.

Rails 5.1 Multiple belongs_to associations with optional: true, rollback - 'required association'

I want to use belongs_to parent_model, optional: true assocation for 1 child - and 2 parents. Found this answer: Model belongs_to eiher/or more than one models. Tried that. But with 2 associated parent models one of them causes rollback when saving the child model.
Is there any limitation on belongs_to with multiple parent models and optional: true? Or am I missing some other presence validation?
Important to say: I would rather avoid using polymorphic associations in this case.
PrivateAttachment has a file attached using Paperclip.
private_attachment.rb
Class PrivateAttachment < ApplicationRecord
belongs_to :first_class, optional: true
belongs_to :second_class, optional: true
has_attached_file :certificate
validates_attachment_presence :certificate
validates_attachment_size :certificate, :less_than => 10.megabytes
validates_attachment_content_type :certificate,
:content_type => [
"image/jpg",
"image/jpeg",
"image/png",
"application/pdf",
"file/txt",
"text/plain",
"application/doc",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.oasis.opendocument.text",
]
end
first_class.rb
Class FirstClass < ApplicationRecord
validates_length_of :message, :maximum => 2000, allow_blank: true
has_many :private_attachments, dependent: :destroy
has_many :approvals
accepts_nested_attributes_for :approvals
end
second_class.rb
Class SecondClass < ApplicationRecord
has_many :private_attachments, dependent: :destroy
end
When creating PrivateAttachment:
second_class.private_attachments.new(certificate: file)
there's a Rollback on save with a message, despite optional attribute is set to true.
Full error
ActiveModel::Errors:0x00007f8bbe03a380 #base=#PrivateAttachment id:
nil, first_class_id: nil, company_id: nil, user_id: nil, doc_type:
nil, url: nil, active: nil, blind: nil, permit_period: nil, views:
nil, downloads: nil, created_at: nil, updated_at: nil,
certificate_file_name: "test.pdf", certificate_content_type:
"application/pdf", certificate_file_size: 443632,
certificate_updated_at: "2018-07-24 20:18:20", second_class_id: 23>,
#messages={:first_class=>["First class required."]},
**#details={:first_class=>[{:error=>:blank}]}>
When creating Attachment with a first_class, everything works just fine, no error.
UPDATE 1---------------------------------------------------------------------
I just realized, that first_class has a child model Approval which has own validation. But at this point I don't understand why should be this deep assocation being taken into consideration when using optional: true ?
approval.rb
class Approval < ApplicationRecord
belongs_to :job_application
validates_acceptance_of :operator_approval, :allow_nil => false,
end
Rails 5.1.6.
PG DB

Why does `validates_associated` exist?

This section of railsguide says:
You should use this helper when your model has associations with other models and they also need to be validated.
So I thought validation of associated models wouldn't be run without validates_associated.
But actually, It was run without it.
There are two models, School and Student.
class School < ActiveRecord::Base
has_many :students
validates :name, presence: true
end
class Student < ActiveRecord::Base
belongs_to :school
validates :name, presence: true
end
On rails console,
school = School.new
=> #<School id: nil, name: nil, created_at: nil, updated_at: nil>
school.students << Student.new
=> #<ActiveRecord::Associations::CollectionProxy [#<Student id: nil, name: nil, school_id: nil, created_at: nil, updated_at: nil>]>
school.name = "test shcool"
=> "test shcool"
school.save
(0.1ms) begin transaction
(0.1ms) rollback transaction
=> false
school.errors.full_messages
=> ["Students is invalid"]
If with validates_associated like below:
class School < ActiveRecord::Base
has_many :students
validates :name, presence: true
validates_associated :students
end
class Student < ActiveRecord::Base
belongs_to :school
validates :name, presence: true
end
On rails console, I ran the exact same commands as above. But the last command school.errors.full_messages returned different result. (It is strange that there are duplicate error messages.)
school.errors.full_messages
=> ["Students is invalid", "Students is invalid"]
My questions are
Is this a RailsGuide's mistake?
Why does validates_associated exist?
Or do I have any mistaken idea?
My environment is
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin14.0]
Rails 4.2.0
it checks to see if the associated objects are valid before saving

Add new record to a new record

I'm using rails 4.1.6
I have a problem creating new records and then saving them. Here are the models:
class Function < ActiveRecord::Base
belongs_to :theater
has_many :showtimes, dependent: :destroy
belongs_to :parsed_show
validates :theater, presence: :true
validates :date, presence: :true
end
class Theater < ActiveRecord::Base
has_many :functions, :dependent => :destroy
validates :name, :presence => :true
accepts_nested_attributes_for :functions
end
class Showtime < ActiveRecord::Base
belongs_to :function
validates :time, presence: true
validates :function, presence: true
end
showtime = Showtime.new time: Time.current
theater = Theater.first # read a Theater from the database
function = Function.new theater: theater, date: Date.current
function.showtimes << showtime
function.showtimes.count # => 0
why is the showtime not being added to the function's showtimes? I need to save the function later with the showtimes with it.
Your Function object hasn't been persisted. You need to make sure that it's persisted (which of course also requires that it be valid) before you can add things to its list of showtimes.
Try saving the function beorehand, and if it succeeds (that is if function.persisted? is true), it should allow you to << directly into function.showtimes, as you like. Alternatively, you can use the Function#create class method instead of the Function#new class method, since the former automatically persists the record.
You can use Function#create
theater = Theater.first # read a Theater from the database
function = Function.new theater: theater, date: Date.current
function.showtimes.create(time: Time.current)
I forgot to check if saving the function also saved the theaters, and even though function.showtimes.count returns 0, the showtimes are saved to the database anyway.
function.showtimes.count returns:
=> 0
but
function.showtimes returns:
=> #<ActiveRecord::Associations::CollectionProxy [#<Showtime id: nil, time: "2015-01-09 04:46:50", function_id: nil>]>

rails basic polymorphism

i'm trying to create a polymorphic relationship between votes can be submitted by users and apply to articles. my code
class Vote < ActiveRecord::Base
attr_accessible :value, :voteable_id, :voteable_type
belongs_to :voteable, :polymorphic => true
end
class User < ActiveRecord::Base
has_many :votes, :as => :voteable
end
class Article < ActiveRecord::Base
has_many :votes, :as => :voteable
end
<Vote id: 1, value: 1, created_at: "2012-07-27 03:13:14", updated_at: "2012-07-27 03:13:14", voteable_id: nil, voteable_type: nil>
From looking at the rails documentation via http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
I feel that my code is set correctly but i'm having a bit of trouble triggering it correctly, ie, how do I actually create a vote object with a properly define relationship to either an article or user?
Is votable_type is string?
Next example should work correctly..
#user.votes.new :value => 1
#user.save
.
I was able to make this work, I was setting the voteable_type attribute incorrectly.

Resources