I've used dependent: :destroy on models before with out any problem, but in rails 4.2 I'm stuck. The past uses were mainly for classic has_many belongs_to models. It almost seems that #<ActiveRecord::Associations::CollectionProxy is causing my problems.
Models
class Subject < ActiveRecord::Base
has_many :properties
has_many :values, :through => :properties
has_many :tags, :through => :properties
class Property < ActiveRecord::Base
belongs_to :subject
belongs_to :tag
belongs_to :value
class Value < ActiveRecord::Base
has_one :property
has_one :subject, :through => :property
has_one :tag, :through => :property
class Tag < ActiveRecord::Base
has_many :properties
has_many :subjects, :through => :properties
My goals were to
Deleting a Subject would delete all associated Properties and Values
Deleting a Property would delete the associated Value, leaving Subject intact
or, Deleting a Value would delete the associated Property, leaving Subject intact
I tried adding dependent destroy on the values line in Subject and the property line in Value. It would delete the properties, but not the values. I tried putting it on the values properties line and value line in Property and got the same results - it would not delete the Values.
I then tried before_destroy filter and ran into the same type of problem or a ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR when I tried the the model associations. I then hacked it and got it to work:
# In Subject model
before_destroy :destroy_values
def destroy_values
# relations does not seem to work got the Values using a new query
#values.destroy_all
pids = values.pluck(:id)
Value.where(id:pids).destroy_all
end
# in Value model
before_destroy :destroy_property
def destroy_property
property.destroy
end
Not sure what is going on, read up as much as I could on dependent and tried delete_all, and every other thing I saw with no joy!
Yes, it is a strange model, just playing around and tried to replicate the "Whatit?" Apple II database in rails for grins.
Sometimes, when your stuck going around circles, you just have to ask a question. When you don't get an answer, you've had time to rethink the problem. I guess I didn't try all the options.
I think the problem is my not understanding default strategy of nullify. According to my goals, you can't have a value without a property and vice versa. Trying to destroy using a through association would raise the pg error. The simple solution that I apparently didn't try was to dependent destroy properties in the Subject model and dependent destroy value in the Property model. The warning in the guide not to use dependent destroy on a belongs_to association may have started my circle trip. I'm still not sure I understand the warning. My guess is that when subject.properties is destroyed, the subject_id is set to null before the property.value destroy call is made, avoiding the pg error. My cleaned up models:
class Subject < ActiveRecord::Base
has_many :properties, dependent: :destroy
has_many :values, :through => :properties
has_many :tags, :through => :properties
class Property < ActiveRecord::Base
belongs_to :subject
belongs_to :tag
belongs_to :value, dependent: :destroy
class Value < ActiveRecord::Base
has_one :property
has_one :subject, :through => :property
has_one :tag, :through => :property
Related
So im working on a rails app for users to create events (and attend other created events). You can read about the assignment here (for the Odin Project): https://www.theodinproject.com/courses/ruby-on-rails/lessons/associations
Anyways I thought I had understood many to many relationships in rails, but the way i've seen other people write the models is confusing to me.
To me it seems like it should be something like:
class User < ApplicationRecord
has_many :attendances
has_many :events, through: :attendances
end
class Attendance < ApplicationRecord
belongs_to :user
belongs_to :event
end
class Event < ApplicationRecord
has_many :users
has_many :users, through: :attendances
end
This makes sense to me because a User can create many events, and an event can have many users attending. (Although attendances is probably the wrong word, maybe invites or something).
But i've seen some weird examples (You can see others source code below on the project) and it seems like they are adding much more to the models and also renaming the source/foreign_key/class_name.
Am I missing something? This still allows a user to "own" an event right? Maybe im mis-understanding how many-to-many works. But this fits at least in my mind of how it should be.
For reference some other models I was seeing was similar to this:
class Event < ActiveRecord::Base
belongs_to :creator, :class_name => "User"
has_many :event_attendees, :foreign_key => :attended_event_id
has_many :attendees, :through => :event_attendees
end
class EventAttendee < ActiveRecord::Base
belongs_to :attendee, :class_name => "User"
belongs_to :attended_event, :class_name => "Event"
end
class User < ActiveRecord::Base
has_many :created_events, :foreign_key => :creator_id, :class_name => "Event"
has_many :event_attendees, :foreign_key => :attendee_id
has_many :attended_events, :through => :event_attendees, :foreign_key => :attendee_id'
end
Basically similar things to the above. Im not really sure what this is doing? Or why all the extra is necessary.
In your example everything according to conventions. Maybe except many-to-many table naming.
attendances table has 'user_id' and 'event_id' fields. But in case it could conflict with other fields, or not descriptive enough you could use different keys.
belongs_to :creator, :class_name => "User"
belongs_to :creator by default would look for Creator model, so it is needed to specify class name explicitly, like in the provided example.
has_many :event_attendees, :foreign_key => :attended_event_id
By default foreign key would be event_id, so here it is specified explicitly too.
has_many :created_events, :foreign_key => :creator_id, :class_name => "Event"
By default, rails would look for user_id foreign key and CreatedEvent model. And these attributes specified explicitly.
You just need to understand what attributes rails provides by default, to change if it is required.
ActiveRecord associations default to a class and foreign key with the same name as the association. The code here is specifically specifying these because they are not the default.
I have two models reservations and reviews I can go reservation.review and get a correct output in cases. However, when i go review.reservation im not getting the same respect i get the relation error StatementInvalid.
Reviews model:
belongs_to :reservation, :foreign_key => :reservation_id, class_name: 'Reservation'
belongs_to :reviser
has_one :reviser
has_one :reservation
belongs_to :user
Reservation model:
has_one :review, :dependent => :destroy
A few things to note about your reviews model. You have this line:
belongs_to :reservation, :foreign_key => :reservation_id, class_name: 'Reservation'
but since you're naming the foreign key reservation_id, which is what rails names the foreign key by default, you can simply get rid of that part and say:
belongs_to :reservation
Secondly, I'm not totally sure why you have has_one :reservation since you already have belongs_to :reservation. I would probably delete the has_one :reservation line unless you're absolutely sure it should be there.
So your new reviews model would look like this:
belongs_to :reservation
belongs_to :reviser
has_one :reviser
belongs_to :user
I would double check in your schema.rb file to ensure that you have the field reservation_id in your reviews table. That field must be present for the association to work properly.
I have this model
class XmlImport < ActiveRecord::Base
belongs_to :video
belongs_to :user
has_many :events, through: :event_import_records, dependent: :destroy
has_many :event_import_records, dependent: :destroy
has_attached_file :xml
validates_attachment_content_type :xml, :content_type => ["text/xml"]
end
The :event_import_records entries are being destroyed. But the :events are not.
Is the dependent:destroy on the has_many through association valid?
Is there another way of writing it? If that is not correct
How can I destroy all the events associated to the XmlImport through the event_import_records?
You can find at the Rails API that: "If using with the :through option, the association on the join model must be a belongs_to, and the records which get deleted are the join records, rather than the associated records." I understand that it delete the joins records but not the associated by through.
If I were you, I try:
class EventImportRecord < ActiveRecord::Base
has_many :events, dependent: :destroy
end
If not work I swap the order of the has_many relations on the XmlImport model, because of "Note that :dependent is implemented using Rails' callback system, which works by processing callbacks in order. Therefore, other callbacks declared either before or after the :dependent option can affect what it does." Also find at the same page of the Rails API.
As I am new to Rails, there is may be a trivial solution. But I could not even find this exact issue somewhere. Other posts deal with destroy vs. delete (I tried both with the same result) or just do not mention how the associated object behaves.
My problem: I want to create a many-to-many association via :through. When I delete an association (i.e. the relation object, not the related objects) I expect that this association is removed (updated) in all model instances of the associated objects. But this does not fully happen.
My example:
Meeting < ActiveRecord::Base
has_many :participations
has_many :users, :through => :participations
User < ActiveRecord::Base
has_many :participations
has_many :meetings, :through => :participations
Participation < ActiveRecord::Base
belongs_to :meeting, :foreign_key => :meeting_id
belongs_to :user, :foreign_key => :user_id
When I create a new association, the associated objects are updated accordingly:
u = User.find(...)
m = Meeting.find(...)
m.users<< u
The same when creating the association this way:
m.participations.create(:user_id => u.id) # this requires to make the user_id attribute accessible
When I now look at the associated user model instance, it got updated as expected:
u.meetings >> contains the newly created association to the meeting m
When I destroy (not delete!) this association, the associated object is not updated as I expect it:
m.users.find_by_user_id(u.id).destroy
m.users >> []
u.meetings >> still contains the destroyed association to meeting m
I would have expected that u.meetings is updated and empty ([]). Adding validations didn't help to solve this:
Meeting < ActiveRecord::Base
validates_associated :contacts
or
Participation < ActiveRecord::Base
validates_presence_of :contact, :interview
What am I doing wrong or what am I missing here?
I am using Rails 3.2.8
Thanks to everyone who is willing to help me.
You should be doing :dependent => :destroy.
Meeting < ActiveRecord::Base
has_many :participations, :dependent => :destroy
has_many :users, :through => :participations
User < ActiveRecord::Base
has_many :participations, :dependent => :destroy
has_many :meetings, :through => :participations
Participation < ActiveRecord::Base
belongs_to :meeting, :foreign_key => :meeting_id
belongs_to :user, :foreign_key => :user_id
This will make sure to destroy the participation if either of the associated user or meeting is destroyed.
You should update your model with the following relationship option:
dependent: destroy
Which will call destroy on the associated objects.
Reference: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Deleting+from+associations
I guess the problem could be like this. In your example,
m.users.find_by_user_id(u.id).destroy
m.users >> []
u.meetings >> still contains the destroyed association to meeting m
u and u.meetings were already loaded before m.users.find_by_user_id(u.id).destroy. Then u.meetings output the cached data.
You can try u.meetings(true) or u.reload; u.meetings to see if there are any difference.
Right down to business....
There are tasks, which have assigned users
class Task < ActiveRecord::Base
has_many :task_assignments, :dependent => :destroy
has_many :assigned_users, :through => :task_assignments, :source => :user
validates_associated :task_assignments
end
And users have assigned tasks
class User < ActiveRecord::Base
has_many :task_assignments, :dependent => :destroy
has_many :assigned_tasks, :through => :task_assignments, :source => :task
end
The task_assignments table looks like this
class TaskAssignment < ActiveRecord::Base
validates_presence_of :user, :message => 'You must add some USERS fool!'
belongs_to :user
belongs_to :task
end
Those associations seem to be working well :0)
Here's the rub - when I add a new task through /tasks/new, I also want to specify a list of users assigned to that task, which the form is returning in "params[:users_list][:id]".
I can get this to work, but I don't want the form to validate unless there is at least one user selected.
I can't for the life of me figure out how to get this validation to take place in the models rather than in the create method.
As you can see, I've thrown "validates _associated :task _assignments" in the tasks method, but to no avail. I'm clearly in over my head.
Thanks for your help.
I think you have to name the parameter user_ids...
f.e.:
params[:users_ids][:id]