I've got 2 models with a 1 to many association that I try to get a rails cascading delete working on.
I'm fairly new to rails and I've tried everything I could but I still can't get it to work...
Here are my 2 models
class CmsHomeSlide < ActiveRecord::Base
attr_accessible :slide_order, :start_datetime, :end_datetime, :slide_type, :header, :headline, :cta_text, :cta_link, :active
has_many :cms_home_slide_detail, :dependent => :delete_all
validates_presence_of :slide_type, :slide_order
end
class CmsHomeSlideDetail < ActiveRecord::Base
attr_accessible :start_datetime, :end_datetime, :position, :image_url, :link, :cms_home_slide, :active
belongs_to :cms_home_slide
end
And the test script I'm using (as a runner, but dev environment and test environment fail the same way)
CmsHomeSlide.delete_all
CmsHomeSlideDetail.delete_all
slide = Factory(:home_slide)
det1 = Factory(:home_slide_detail, :cms_home_slide => slide, :position => 1)
det2 = Factory(:home_slide_detail, :cms_home_slide => slide, :position => 2)
puts "Slides length #{CmsHomeSlide.all.length}"
puts "Details length #{CmsHomeSlideDetail.all.length}"
slide.delete
puts "Slides length #{CmsHomeSlide.all.length}"
puts "Details length #{CmsHomeSlideDetail.all.length}"
Output is
Slides length 1
Details length 2
Slides length 0
Details length 2
What am I missing?
You need to use :dependent => :delete or :dependent => :destroy
From the guide:
If you set the :dependent option to :destroy, then deleting this
object will call the destroy method on the associated object to delete
that object. If you set the :dependent option to :delete, then
deleting this object will delete the associated object without calling
its destroy method.
http://guides.rubyonrails.org/association_basics.html
Related
This is a rather simple Rails 4 situation. Model Intranet has_many activities. Activities exists with sufficient records for several intranets. Current_intranet.activities.size returns 69 records. However, whenever I try to access any of the records, I receive "output error: <ArgumentError: wrong number of arguments (1 for 0)" even though I'm not passing any arguments. All of the following fail with that error.
Current_intranet.activities.first
Activity.where(intranet_id: 1).first
Activity.where(:intranet_id => 1).first
Activity.where{intranet_id.eq 1}.first
All of the above with [0] instead of first
There is no problem with any other models
I'd appreciate any suggestions. Thanks in advance.
There are no defined scopes. The code is:
class Activity < ActiveRecord::Base
acts_as_capitalized
# activities created during intranet creation.
DEFAULT_ACTIVITIES = { 'Document Preparation' => 'general income', 'No Charge' => 'general income'}
# static activity names that can not be deleted.
STATIC_ACTIVITY_NAMES = [ 'Document Preparation','No Charge']
before_destroy :check_if_used
before_destroy :cannot_delete_static_activities
belongs_to :created_by_user, :class_name => "User", :foreign_key => "created_by"
belongs_to :updated_by_user, :class_name => "User", :foreign_key => "updated_by"
belongs_to :intranet
belongs_to :chart_of_account
has_many :activity_rates, :dependent => :destroy
has_many :event_type
has_many :billings do
def find_by_name(name)
(find :all, :conditions => ["activity.name = ?",name])
end
end
has_many :document_templates, :foreign_key => "billing_activity_id"
validates_presence_of :intranet_id, :name, :chart_of_account_id
validates_uniqueness_of :name, :scope => :intranet_id
I'm use of Ruby on Rails.
My models like this.
Weblog model
class Weblog < ActiveRecord::Base
attr_accessible :body
has_many :comments, :dependent => :destroy
accepts_nested_attributes_for :comments
end
Comment model
class Comment < ActiveRecord::Base
attr_accessible :comment
belongs_to :weblog
end
My console log is here
$ attr = {}
$ attr["blog"] = {"body" => "test body", "comments_attributes" => {"0" => {"comment" => "comment1"}, "1" => {"comment" => "comment2"}}}
$ blog = Weblog.new(attr["blog"])
$ blog.save #=> comment data are saved with id 1 and 2 in comments table
$ attr["blog"] = {"body" => "update test body", "comments_attributes" => {"0" => {"comment" => "commentA", "id" => "1"}}} # I want to delete comment data whose id is 2 in comment table
$ blog2 = Weblog.first
$ blog2.update_attributes(attr["blog"]) #=> updates are correctly finished..
But the data whose id is 2 in comment table are not deleted.
How to delete comments table data through updating weblogs table.
You need to add allow_destroy => true option to your accepts_nested_attributes_for call:
class Weblog < ActiveRecord::Base
attr_accessible :body
has_many :comments, :dependent => :destroy
accepts_nested_attributes_for :comments, allow_destroy: true
end
You need to use the :allow_destroy option when declaring the model accepts nested attributes. After you do that, when you want to remove a nested model you need to pass the id of the model and the special attribute _destroy with value '1'
You can see the doc and an example at http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Let's say you have a SiteUpdate and a Comment model, and you want to make Comment polymorphic. You make comment hold a "commentable_id" and "commentable_type"...
Here's our comment model:
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
validates_presence_of :content
validates_presence_of :commentable
end
Here is our SiteUpdate:
class SiteUpdate < ActiveRecord::Base
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
has_many :comments, :as => :commentable
validates_presence_of :subject
validates_length_of :subject, :maximum => 80
validates_presence_of :intro
validates_length_of :intro, :maximum => 200
validates_presence_of :text
validates_presence_of :author
scope :sorted, order("site_updates.created_at desc")
end
Does Rails link the commentable to the site_update instance, or do I have to do that manually?
#site_update.comments << Factory.build(:comment, :commentable_id => nil)
#site_update.save
This fails -> it complains that the comment.commentable_id should not be blank (I set this validation in the Comment model).
So do I do this manually, or do I set this up incorrectly?
Or do I just not validate it at all?
I'm making an assumption that your #site_update object is a new object. If so...
There's a somewhat annoying thing about rails associations. You can't really add them before the record is saved.
What's happening is, you have a new site update object without an ID. You build a new comment object for that site update, so it sets the commentable_type to "SiteUpdate", however, there's no ID yet, so it sets the commentable_id to nil. You save, and it bubbles out to save associated objects, but it doesn't set the comment commentable_id to the SiteUpdate ID, because it doesn't exist.
So if you change it around to be :
#site_update.save
#site_update.comments << Factory.build(:comment, :commentable_id => nil)
#site_update.comments.map { |c| c.save }
it should work.
If it's not a new record...it should work as is.
I've been using the association_collection method "other_ids" throughout my Rails app with no issues. However whenever I try to access it from within the model defining the association, Rails has no idea what I'm taking about. For example:
class Membership < ActiveRecord::Base
belongs_to :course, :touch => true
belongs_to :person, :touch => true
end
class Day < ActiveRecord::Base
belongs_to :course, :touch => true, :counter_cache => true
has_many :presents, :dependent => :delete_all
has_many :people, :through => :presents
before_destroy :clear_attendance
def clear_attendance
mems = Membership.where(:course_id => course.id, :person_id => person_ids)
mems.update_all(["attendance = attendance - ?", (1 / course.days.size.to_f)])
end
end
In this case, person_ids is always null. I've tried self.person_ids, people.ids, etc. All nothing. I have used day.person_ids elsewhere with no issues, so why can't I use it here?
I am using Ruby 1.9.1 and Rails 3.0.3. Here is the SQL call from my log:
[1m[36mAREL (0.0ms)[0m [1mUPDATE "memberships" SET attendance = attendance - 0.3333333333333333 WHERE ("memberships"."course_id" = 4) AND ("memberships"."person_id" IN (NULL))[0m
edit: added more code to clarify question
What you really want there is:
def a_method
self.people.all
end
But to answer your question, person_ids is the correct method, and it should return an empty array, not nil. I just tried an association like that out in 2.3.10. Maybe you can post some more of your code, rails version, etc.
Thanks for your help - I figured it out myself. The problem was the order of my callbacks. I was trying to call person_ids after the association had been deleted. Changing the order to this solved my issues.
class Day < ActiveRecord::Base
before_destroy :clear_attendance
belongs_to :course, :touch => true, :counter_cache => true
has_many :presents, :dependent => :delete_all
has_many :people, :through => :presents
I have some STI structure like following:
class Assignment < ActiveRecord::Base
has_many :criteria, :class_name => "Criterion", :order => :position
end
class Criterion < ActiveRecord::Base
# Represents a criterion used to mark an assignment that
# being the super class for all different criterias
set_table_name "criteria" # set table name correctly
belongs_to :assignment
validates_associated :assignment, :message => 'association is not strong with an assignment'
validates_presence_of :assignment_id
validates_numericality_of :assignment_id, :only_integer => true, :greater_than => 0, :message => "can only be whole number greater than 0"
validates_uniqueness_of :criterion_name, :scope => :assignment_id, :message => 'is already taken'
validates_presence_of :criterion_name,:assignment_id
end
class FlexibleCriterion < Criterion
has_one :flexible_criterion_attribute, :class_name => "FlexibleCriterionAttribute"
accepts_nested_attributes_for :flexible_criterion_attribute
default_scope :include => :flexible_criterion_attribute
end
class FlexibleCriterionAttribute < ActiveRecord::Base
belongs_to :criterion, :class_name => "FlexibleCriterion"
validates_presence_of :max
validates_numericality_of :max, :message => "must be a number greater than 0.0", :greater_than => 0.0
DEFAULT_MAX = 1
end
Alright, I have uploaded my current working codes. So basically the problem is:
When I use a method like criterion = assignment.criteria.find_or_create_by_criterion_name("AAA"), I will get an object of criterion. But I want cast this object to flexiblecriterion so that I can assign the value of criterion.flexible_criterion_attribute
Thx in advance!
If assignment.criteria.find_or_create_by_criterion_name("AAA") returns a record in which type column has value FlexibleCriterion, it will automatically cast the object to FlexibleCriterion.
If you want to ensure that only FlexibleCriterion is returned, modify your finder like this: assignment.criteria.find_or_create_by_criterion_name_and_type("AAA", 'FlexibleCriterion')