How can I delete an entry from a HABTM join table in rails? - mysql

Through many iterations of testing, I just noticed that my join table that represents a HABTM relationship between two models isn't removing entries when instances of these models get deleted. Do I need to do something special when removing an instance of a model that has HABTM relationships?

Upon closer inspection HABTM relationships should be removing join table entries. However neither HABTM relationships or the relationship I described in the original version (see post history) of this solution will remove those join table entries when you eliminate the record with the delete method. ActiveRecord::Base#delete does not trigger any callbacks, such as the ones a HABTM relationship establishes to remove orphaned entries from the join table. Instead you should be using ActiveRecord::Base#destroy.
You will have to use raw SQL to remove the unneeded entries. If you decide to create a join model, you can iterate through entries in the join model, deleting those without an association.
Example:
class Foo < ActiveRecord::Base
has_many :foo_bars, :dependent => :destroy
has_many :bars, :through => :foo_bars
end
class FooBar < ActiveRecord::Base
belongs_to :foo
belongs_to :bar
end
class Bar < ActiveRecord::Base
has_many :foo_bars, :dependent => :destroy
has_many :foos, :through => :foo_bars
end
FooBar.all.each{|fb| fb.destroy? if fb.foo.nil? || fb.bar.nil? }

The entries in the join table should be getting deleted without you doing anything special. You can add the :delete_sql option to your code to change the behavior if you have a weird situation. Deleting the object on the other side of the join is not a default behavior.

Related

Many-to-many on the same table in Phoenix

Using Ecto v2.2.6, Phoenix 1.3
I have a table of people (who happen to be related to each other), and another table of relations for those people. The relations table lists the id of the parent in one column, and the id of the child in another.
mix phx.gen.json Accounts Person persons name:string age:integer
mix phx.gen.json Accounts Relationship relationships parent_id:references:persons child_id:references:persons
Now, I am about to add belongs_to relationships in the relationships schema (which currently looks like this)
schema "relationships" do
field :parent_id, :id
field :child_id, :id
timestamps()
end
But without the ability to set the id explicitly, it would looks like this (which doesn't seem to work)
schema "relationships" do
belongs_to :person UserRelations.Accounts.Person
belongs_to :person UserRelations.Accounts.Person
timestamps()
end
How can I write the relationships schema so that I capture these belongs_to relationships?
Edit
I have set the schema up like this, as per suggestion:
schema "relationships" do
belongs_to :parent UserRelations.Accounts.Person
belongs_to :child UserRelations.Accounts.Person
timestamps()
end
I have tried to do something similar with the Person schema as well:
schema "persons" do
field :age, :integer
field :name, :string
many_to_many :parent_of, UserRelations.Accounts.Person, join_through: "relationships"
many_to_many :child_of, UserRelations.Accounts.Person, join_through: "relationships"
timestamps()
end
However, when I try to access these relationships (I am doing this through an absinthe/graphql schema), I see that it is looking for a person_id somewhere:
[error] Task #PID<0.400.0> started from #PID<0.395.0> terminating
** (Postgrex.Error) ERROR 42703 (undefined_column): column f2.person_id does not exist
The name of the two belongs_to relationships need to be different. For example:
belongs_to :child, UserRelations.Accounts.Person
belongs_to :parent, UserRelations.Accounts.Person
With these names, Ecto will infer the foreign key correctly: child_id and parent_id.
For the many_to_many, you need to provide join_keys to both as Ecto can't infer it from the name of the relationship and the schema module.
For the first one, you need to add:
, join_keys: [parent_id: :id, child_id: :id]
And for the second one:
, join_keys: [child_id: :id, parent_id: :id]

Optional relation between two model objects in rails

I have two objects A and B in my Rails model which have an optional one-to-one relationship. Say, A can have 0 or 1 B. In most cases A has 0 Bs.
Also, other such relationships can be introduced between A and other objects X, Y, Z later.
Given that these are one-to-one relations, a has-a relationship between A and B makes sense. But given that it is an optional and infrequent relationship, and also that other such relationships can be introduced later with A, adding a new column for each new relationship seems questionable.
How can I model this relationship better? Should using a correlation table between A and B make better sense in this particular case?
You would add the id of Model A to all the other models, and Model A would has_one for each of them.
Each other would belong_to Model A
I would go with the presented solution, that is adding a new column b_id to A.
Else you could create a third model C which holds both a_id and b_id and then have A access B through C.
# Pluralization is off here. Remember to pluralize has_many
class A < ActiveRecord::Base
has_many :c
has_many :b, through: :c
end
class B < ActiveRecord::Base
has_many :c
has_many :a, through: :c
end
class C < ActiveRecord::Base
belongs_to :a
belongs_to :b
end
This would prevent any null values in the database. This is the solution I use most often when I will build something around the relation, since now you have a place to store more information about the relation.
You could also make C polymorphic so that it can store any model (not only A). http://guides.rubyonrails.org/association_basics.html#polymorphic-associations

How to add link a table to a row

I'm developing a Ruby on Rails application with MySQL and i have a Categories table in my Database.
I want to add sub categories to one of the categories.
i thought of creating a new table for sub categories or just add them to the same Categories table. can anyone help?
As sub_category is nothing but a category with a parent association, you could just store both categories and sub_categories in one table and wire up the association. This type of association is referred as self join association.
Try the following:
class Category < ActiveRecord::Base
has_many :sub_categories, class_name: 'Category', foreign_key: :parent_id
belongs_to :parent, class_name: 'Category'
end
Refer to Self Joins Association for more info.
You need to setup the following migration:
class AddParentIdToCategories < ActiveRecord::Migration
def change
add_column(:categories, :parent_id, :integer)
end
end
And run the migration:
bundle exec rake db:migrate
If you are expecting your tree structure to be more complex, for example, sub_categories having sub_sub_categories and more, then you could take a look at several gems that abstract this feature.
Refer to acts_as_tree, and ancestry fore more info,

Modeling my Database

this is my first post on here. My question is pertaining to model relationships.
I'm new to rails and a bit confused as to what type of associations I need. I checked rails associations but this confused me more. I've tried quite a few things and can't figure out exactly what I need.
I have three models/tables:
Students
Teachers
Groups (as in classes)
I have it setup as:
teacher -> belongs_to :group
student -> belongs_to :group
group -> has_many :students & has_many :teachers
This setup works okay because it allows me to make calls like:
g.students.all
s.group.name
However I would like to be able to make a view page that generates all of the Groups(classes) that a teacher has. Such as: t.groups.all or t.groups.count
One thing that makes my table unique is that a group(class) can have different teachers. I don't know if I need a has_and_belongs_to_many relationship or a has_many :through relationship.
What is the ideal setup for my 3 tables, what table id's and associations are required?

Adding records to multiple tables when only have one model in Ruby on Rails?

I have three models clients, client_categories and clients_category_merge.
I want to store clients_id and client_categories_id into clients_category_merge table, as a single client can have multiple client categories.
How do I add the record to 2 tables (clients and clients_category_merge) when I only have one model (clients) when submitting the form?
I am sure there is a good way of doing this. But I am pretty new to Rails and lost on this one.
The has_many :through association will add the proper records for you.
class Client < ActiveRecord::Base
has_many :client_categories_merges
has_many :client_categories, :through => :clients_categories_merges
end
class ClientCategories < ActiveRecord::Base
has_many :client_categories_merges
has_many :clients, :through => :clients_categories_merges
end
class ClientCategoryMerges < ActiveRecord::Base
belongs_to :client_category
belongs_to :client
end
Check out this guide
Edit: And this one for the corresponding forms