RoR Many to many association - ruby-on-rails

I have Category and Specification models and many to many association via CategoriesSpecifications table which looks like this:
create_table :categories_specifications, id: false do |t|
t.belongs_to :specification, null: false
t.belongs_to :category, null: false
t.integer :status, null: false, default: 0
end
What is best practice and shortest way to select all specifications via Category

#category = Category.first
#category.specifications # this is the shortest way to select all specifications via Category
Make sure the associations are declared in your models, of course:
class Category < ActiveRecord::Base
has_many :categories_specifications
has_many :specifications, through: :categories_specifications
end
class Specification < ActiveRecord::Base
has_many :categories_specifications
has_many :categories, through: :categories_specifications
end
class CategoriesSpecification < ActiveRecord::Base
belongs_to :category
belongs_to :specification
end
Recommended reading: http://guides.rubyonrails.org/association_basics.html

Assuming you have something like the below:
class Category < ActiveRecord::Base
has_many :specifications
has_many :categories, :through => :categories_specifications
end
class CategoriesSpecification < ActiveRecord::Base
belongs_to :category
belongs_to :specification
end
class Specification < ActiveRecord::Base
has_many :categories_specifications
has_many :categories, :through => :categories_specifications
end
You can simple find the category and than select all specifications:
Category.find(1).specifications

Related

Trouble with model associations

The goal is for a shop to create rewards and associate each reward to a follower of his choice. This is my setup:
class Shop < ApplicationRecord
has_many :rewards
has_many :follows
has_many :users, through: :follows
end
class Reward < ApplicationRecord
belongs_to :shop
end
class Follow < ApplicationRecord
belongs_to :shop
belongs_to :user
has_many :reward_participant
end
class User < ApplicationRecord
has_many :follows
has_many :shops, through: :follows
end
I created this model in order to capture the reward and follower association.
class RewardParticipant < ApplicationRecord
belongs_to :reward
belongs_to :follow
end
And I have created the following migrations:
class CreateRewards < ActiveRecord::Migration[6.0]
def change
create_table :rewards do |t|
t.string :title
t.text :body
t.date :expires
t.integer :shope_id
t.timestamps
end
end
end
class CreateRewardParticipants < ActiveRecord::Migration[6.0]
def change
create_table :reward_participants do |t|
t.integer :reward_id
t.integer :follow_id
t.timestamps
end
end
end
I'm having trouble figuring out if this is the correct approach to the model associations and migrations. Thanks for the help in advance!
Generally you are right.
We want users to follow a shop, and a shop can create rewards and grant many rewards to many followers.
1. Visual schema:
2. Model associations (complete version)
user.rb
has_many :follows
has_many :reward_follows, through: :follows
has_many :rewards, through: :reward_follows # NOT through shops
has_many :shops, through: :follows
follow.rb
belongs_to :user
belongs_to :shop
has_many :reward_follows
shop.rb
has_many :rewards
has_many :reward_follows, through: :rewards # NOT through follows
has_many :follows
has_many :users, through: :follows
reward.rb
has_many :reward_follows
belongs_to :shop
has_many :follows, through: :reward_follows
has_many :users, through: :follows
3. Do not use date field. Use datetime field.
Justification: https://www.ruby-forum.com/t/time-without-date/194146
This personally saved me hours of work long-term.

ActiveRecord: Uninitialze constant exception when define many-to-many relationship

I have a model named Post and a model named Category. Relationship between Post and Category is many-to-many. So I create a join table as following:
create_table :categories_posts do |t|
t.integer :post_id, index: true
t.integer :category_id, index: true
t.timestamps
end
Here is my model Post (file name: post.rb)
class Post < ApplicationRecord
has_many :categories, :through => :categories_posts
has_many :categories_posts
end
Here is my model CategoryPost (file name: category_post.rb)
class CategoryPost < ApplicationRecord
self.table_name = "categories_posts"
belongs_to :post
belongs_to :category
end
But when I try: Post.last.categories or Post.last.categories_posts I meet exception:
NameError: uninitialized constant Post::CategoriesPost
Please tell me where I am wrong.
Thanks
NameError: uninitialized constant Post::CategoriesPost
The plural form of CategoryPost is CategoryPosts, so you should use category_posts instead of categories_posts when defining associations
class Post < ApplicationRecord
has_many :category_posts
has_many :categories, :through => :category_posts
end
However, if you want to use categories_posts, you can do with by defining class_name on the association
class Post < ApplicationRecord
has_many :categories_posts, class_name: "CategoryPost"
has_many :categories, :through => :categories_posts
end

Ruby on Rails Nested Form

I have three Models: Deal, Zipcode, DealIncludeZipcode.
Now, the association looks like below:-
Deal Model:
class Deal < ActiveRecord::Base
has_many :deal_include_zipcodes, dependent: :destroy
has_and_belongs_to_many :zipcodes, dependent: :destroy
accepts_nested_attributes_for :deal_include_zipcodes,:reject_if => :reject_include_zipcodes, allow_destroy: true
private
def reject_include_zipcodes(attributes)
if attributes[:deal_id].blank? || attributes[:zipcode_id].blank?
if attributes[:id].present?
attributes.merge!({:_destroy => 1}) && false
else
true
end
end
end
end
class Zipcode < ActiveRecord::Base
has_and_belongs_to_many :deals
end
class DealIncludeZipcode < ActiveRecord::Base
belongs_to :deal
belongs_to :zipcode
end
Now in view I have a checkbox,on unchecking it I can select multiple zipcode to select from DealIncludeZipcode.But when I save the data it is not saving.
I have used migration for joining Zipcode and Deal Model in which my exclude zipcode functionality is working correctly.
Please provide a soloution.I have tried various method but didn't got succeed.
The whole point of has_and_belongs_to_many is that you don't have a model which joins the two parts.
class Deal < ActiveRecord::Base
has_and_belongs_to_many :zipcodes
end
class Zipcode < ActiveRecord::Base
has_and_belongs_to_many :deals
end
Would join through a "headless" table called deals_zipcodes. If you want to have a join model you need to use has_many :through instead.
class Deal < ActiveRecord::Base
has_many :deal_zipcodes, dependent: :destroy
has_many :zipcodes, through: :deal_zipcodes
end
class DealZipcode < ActiveRecord::Base
belongs_to :deal
belongs_to :zipcode
end
class Zipcode < ActiveRecord::Base
has_many :deal_zipcodes, dependent: :destroy
has_many :deals, through: :deal_zipcodes
end
I think Max's right. So your migration should be
create_table :deals do |t|
t.string :name
...
end
create_table :zipcodes do |t|
t.string :zipcode
...
end
create_table :deals_zipcodes do |t|
t.belongs_to :deal, index: true
t.belongs_to :zipcode, index: true
end
And your models should be
class Deal < ActiveRecord::Base
has_and_belongs_to_many :zipcodes
end
class Zipcode < ActiveRecord::Base
has_and_belongs_to_many :deals
end
You should probably take a look at the ActiveRecord guide, where you'll find more explanation.

Rails polymorphic join table work both ways

I am starting to learn more advanced associations, and polymorphic join table looks very interesting, but I've come to a bad limitation.
My models:
school_class.rb:
class SchoolClass < ActiveRecord::Base
has_many :class_of_schools, as: :school_model, dependent: :destroy
has_many :basic_primary_schools, through: :class_of_schools
has_many :basic_shugenja_schools, through: :class_of_schools
has_many :basic_monk_schools, through: :class_of_schools
end
class_of_school.rb:
class ClassOfSchool < ActiveRecord::Base
belongs_to :school_model, polymorphic: true
belongs_to :school_class
end
basic_primary_School.rb:
class BasicPrimarySchool < ActiveRecord::Base
has_many :class_of_schools, as: :school_model, dependent: :destroy
has_many :school_classes, through: :class_of_schools
end
shugenja_school and monk_schools, has the same association as basic_primary_school.
And join model it self:
class CreateClassOfSchools < ActiveRecord::Migration
def change
create_table :class_of_schools do |t|
t.integer :school_class_id
t.integer :school_model_id
t.string :school_model_type
t.timestamps null: false
end
end
end
It joins well on the school side, I can make school.class_schools, and I get the array of school_classes associated. But on the other side I can`t do the same. In fact when I check school_class.classes_of schools I get empty array.
I make associations in my seed file by function like this:
def join_schools_with_classes(school_object_name, school_classes_array)
school_object_name.all.each do |school|
school_classes = school_classes_array[school.name]
school_classes.each do |class_name|
school_class = SchoolClass.find_by(name: class_name)
school.class_of_schools.create( school_class_id: school_class.id)
end
end
end
My question:
How can I make this association works both ways? So I can call ClassSchool.first.class_of_schools returns, all objects associated to this object. And still be able to call BasicPrimarySchool.first.school_classes to get associated school_class objects.
Just remove as: :school_model in SchoolClass relation
has_many :class_of_schools, dependent: :destroy
EDIT
change the relation in SchoolClass to
has_many :basic_primary_schools, through: :class_of_schools, source: :school_model, source_type: "BasicPrimarySchool"

Rails HABTM middle values

I have to following relationship:
class Course < ActiveRecord::Base
attr_accessible :name
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
attr_accessible :name
has_and_belongs_to_many :courses
end
Then I have the following table:
create_table :courses_users, :force => true, :id => false do |t|
t.integer :user_id
t.integer :course_id
t.integer :middle_value
end
How can I access (edit/update) the middle value in the many to many record?
HABTM should be used to only store the relation. If you have any fields you want to store in the relation, you should create another model, eg. CourseSignup. You would then use this model to create a has_many :through => :course_signups relation, so your models would look like this:
class Course < ActiveRecord::Base
has_many :course_signups
has_many :users, :through => :course_signups
end
class CourseSingup < ActiveRecord::Base
belongs_to :course
belongs_to :user
end
class User < ActiveRecord::Base
has_many :course_signups
has_many :courses, :through => :course_signups
end
Then you can add your middle_value in the CourseSignup model.
You can find more details in the guide to ActiveRecord associations.
You want a has_many :though, not a HABTM.
A HABTM does not have a join model, but a has_many :through does. Something like:
class Course < ActiveRecord::Base
has_many :enrollments
has_many :users, :through => :enrollments
end
class Enrollment < ActiveRecord::Base
belongs_to :course
belongs_to :user
end
class User < ActiveRecord::Base
has_many :enrollments
has_many :courses, :through => :enrollments
end

Resources