I'm rather new to Rails and have a question about how to successfully add sub-categories to an existing Join Table relationship.
For example, assume I'm building a job board. Each job has 5 major filter categories ( Job_Type [General Management, Finance & Operations, etc.], Industry [ Technology, Healthcare, etc.], Region [Northwest, Southeast, etc.], and so on ). I want each of those to have subcategories as well ( ex. This job post has a Region > Southeast > South Carolina > Greenville ).
Setting up an initial Join Table association for the 5 major filter types made sense for future filtering and searchability.
EDIT
Here is my current Posting Model
class Posting < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
belongs_to :recruiter, class_name: "User"
belongs_to :company
has_many :interests
has_many :comments, as: :commentable
has_and_belongs_to_many :job_types
has_and_belongs_to_many :industries
has_and_belongs_to_many :regions
has_and_belongs_to_many :market_caps
has_and_belongs_to_many :ownerships
has_many :users, through: :interests
acts_as_followable
end
I'm currently using join tables instead of an array directly on the ActiveRecord for speed sake and for filtering/searching capabilities later on. It also allows me to use these join table in conjunction with a plethora of other necessary ActiveRecord associations.
Here is a snippet of what job_type looks like:
class JobType < ActiveRecord::Base
has_and_belongs_to_many :postings
has_and_belongs_to_many :news_items
has_and_belongs_to_many :companies
has_and_belongs_to_many :users
has_and_belongs_to_many :mkt_ints
end
This allows me access to a simple array of associated models, but I'm confused as to how to move past that to an array of arrays with potential further nested arrays. It feels clunky to add additional join tables for the first join tables. I'm sure there's a better solution and would love to get any insight you might have.
SECOND EDIT
Here's a representational picture of what I am trying to do if it helps.
Thanks!
SOLVED
The data for this table will, most likely, not be changing which eases the complexity and presented a more straightforward solution.
I created separate roles within a single table to limit queries and join tables. The resulting Region ActiveRecord looks like this:
class CreateRegions < ActiveRecord::Migration
def change
create_table :regions do |t|
t.string :role # role [ region, state, city ]
t.integer :parent # equal to an integer [ state.id = city.parent ] or null [ region has no parent ]
t.string :name # [ ex: Southeast, South Carolina, Charleston ]
t.timestamps null: false
end
end
end
This gives me all of the fields necessary to create relationships in a single table and easily sort it into nested checkboxes using the parent.id.
Thank you to everyone who viewed this question and to Val Asensio for your helpful comment and encouragement.
Related
I want to create a has_many_through relationships, by creating two model named as subjects and lesssons. I need to create a join table instead of creating a seperate model.Expecting a better solution.
Thanks in advance!
The has_and_belongs_to_many association is a many to many association without a join model:
class Subject < ApplicationRecord
has_and_belongs_to_many :lessons
end
class Lesson < ApplicationRecord
has_and_belongs_to_many :subjects
end
To generate the join table run rails g CreateJoinTableLessonsSubjects lessons subjects:
class CreateJoinTableLessonsSubjects < ActiveRecord::Migration[5.0]
def change
create_join_table :lessons, :subjects do |t|
# t.index [:lesson_id, :subject_id]
# t.index [:subject_id, :lesson_id]
end
end
end
Note that the table naming is different lessons_subjects compared to lesson_subjects for has_many through:.
While has_and_belongs_to_many is somewhat simpler and saves memory by not instantiating join model instances there are some big drawbacks:
No good way to query the join table directly.
No way to access additional columns on the join table that describes the relation between A & B.
You can quite simply go from has_and_belongs_to_many to has_many through: at a later point if you find that you need those features by just renaming the table and creating a join model.
I am trying to do a webpage for my project at school where a user can list out the stocks that he or she has and can check the weekly prices ( I know, not the best frequency of updates)
I have already managed to get chartkick and charjs to sort of work in my rails application, but I realised am missing something. I tried to map out my models to display the price changes.
Currently I have following models:
User
has_many :investment_stocks
has_many :investment_price
Investment_stock
belongs_to :user
belongs_to :investment_price
belongs_to :advisor
Investment_price
has_many :investment_stocks
has_many :users, through: :investment_stocks
Migration
Investment_price
t.string :name
t.string :price
t.date :date
My idea is to manually change the investment_price.price for each of the stocks that can be referenced and displayed on the charts. But with a user having many stocks, I am wondering if my tables need something else as the way chartkick and chartjs works is that I seem to need 3 variables to be displayed on the chart but yet i always seem to be able to work around 2 only
Edited
So now I have the following
User model
has_many :investment_stocks
has_many :investment_prices
Investment_stock model
belongs_to :user
has_many :investment_prices, dependent: :destroy
accepts_nested_attributes_for :investment_prices
Investment_price model
belongs_to :investment_stock, optional: true
belongs_to :user, optional: true
But I am having difficulty in adding in a nested form to create on investment_stock and investment_price tables a user_id
investment_stocks controller
def new
#investment_stock = InvestmentStock.new
#investment_stock.investment_prices.build
end
def create
#investment_stock= InvestmentStock.new(investment_stock_params)
#investment_stock["user_id"]= current_user.id
How do I make it such that the user_id appears on both the stock and price tables?
ok, I think that you can try it :
Investment_price model-> belongs_to :investment_stocks, belongs_to :user .
the stocks have the price, the user have the stocks and prices.
I would like to understand the process which can be followed to create models and migrations which in turn create a joining table within the database to resolve a many-to-many relationship. For instance. If i have a course table, a students table and i want to create a joining table called studies with the id from course and students along with extra data such as the grade and date started. Exactly What is the process for doing this?
If i am to use the generate model command for each of the above 3 table names, this will create a separate migration for all of them. Even if i go into the models and add the relevant associations this will not affect how the database is created? Please could you give me some guidance on what steps i must take in order to create the required foreign key relationships here with some examples?
Use a has_many :through association. Set it up manually.
Step 1: Generate your models separately. The joining model Study will contain foreign keys, so remember to include the columns.
rails g model Course title:string
rails g model Student name:string
rails g model Study course_id:integer student_id:integer start_date:date grade:string
Step 2: Set up associations:
# models/course.rb
class Course
has_many :studies
has_many :students, through: :studies
end
# models/student.rb
class Student
has_many :studies
has_many :courses, through: :studies
end
# models/study.rb
class Study
belongs_to :course
belongs_to :student
end
Step 3: Run migrations, restart server (if necessary), and that's it. Rails will handle the rest.
Accessing attributes in the joining table may require careful timing to ensure the correct object is being accessed, since the joining object is not returned via Rails' built-in methods, ie #course.students. Check out this answer for some ideas.
Read the guide for more information.
for many-to-many relationships use:
class Car < ActiveRecord::Base
has_and_belongs_to_many :tires
end
class Tire < ActiveRecord::Base
has_and_belongs_to_many :cars
end
Then you create a migration like this:
class CreateCarsAndTires < ActiveRecord::Migration
def change
create_table :cars do |t|
t.string :name
end
create_table :tires do |t|
t.string :something
end
create_table :cars_tires do |t|
t.belongs_to :car
t.belongs_to :tire
t.string :additional_dataA //optional
t.int :something_else //optional
end
end
end
It is very important that you name your join table in the migration in alphabetical order (c in cars comes before t for tires) as ActiveRecord will look in has_many_and_belongs_to relations for a table which is named this way pluralized-classA_pluralized_classB like apples_bananas vs bananas_apples which would not work and you would have to add the table name to your classes and it goes against the convention over configuration paradigm.
Hope it helps.
I have 3 models: Question, Option, Rule
Question has_many options;
Option needs a foreign key for question_id
Rule table consists of 3 foreign_keys:
2 columns/references to question_ids -> foreign keys named as 'assumption_question_id' and 'consequent_question_id'
1 column/reference to option_id -> foreign key named as option_id or condition_id
Associations for Rule:
Question has_many rules; and
Option has_one rule
I want to understand how to write up migrations for this, and how that associates to the 'has_many'/'belongs_to' statements I write up in my model, and the ':foreign_key' option I can include in my model.
I had this for my Option migration, but I'm not sure how the "add_index" statement works in terms of foreign keys, and how I can use it for my Rule migration: (my Question and Options models have appropriate has_many and belongs_to statements - and work fine)
class CreateOptions < ActiveRecord::Migration
def change
create_table :options do |t|
t.integer :question_id
t.string :name
t.integer :order
t.timestamps
end
add_index :options, :question_id
end
end
Thank you for the help!
Note: I have found this way to solve the problem.Kindness from China.
If you have RailsAdmin with you,you may notice that you can see all rules of one question as long as one field of both question fields(assumption_question_id,consequent_question_id) equals to id of the question.
I have done detailed test on this and found out that Rails always generates a condition "question_id = [current_id]" which make to_sql outputs
SELECT `rules`.* FROM `rules` WHERE `rules`.`question_id` = 170
And the reason that the following model
class Question < ActiveRecord::Base
has_many :options
# Notice ↓
has_many :rules, ->(question) { where("assumption_question_id = ? OR consequent_question_id = ?", question.id, question.id) }, class_name: 'Rule'
# Notice ↑
end
makes Question.take.rules.to_sql be like this
SELECT `rules`.* FROM `rules` WHERE `rules`.`question_id` = 170 AND (assumption_question_id = 170 OR consequent_question_id = 170)
Is that we have not yet get ride of the annoy question_id so no matter how we describe or condition properly, our condition follows that "AND".
Then,we need to get ride of it.How?
Click here and you will know how,Find sector 8.1,and you can see
Article.where(id: 10, trashed: false).unscope(where: :id)
# SELECT "articles".* FROM "articles" WHERE trashed = 0
Then lets do it:
class Question < ActiveRecord::Base
has_many :options
# Notice ↓
has_many :rules, ->(question) { unscope(where: :question_id).where("assumption_question_id = ? OR consequent_question_id = ?", question.id, question.id) }, class_name: 'Rule'
# Notice ↑
end
class Rule < ActiveRecord::Base
belongs_to :option
belongs_to :assumption_question, class_name: "Question", foreign_key: :assumption_question_id, inverse_of: :assumption_rules
belongs_to :consequent_question, class_name: "Question", foreign_key: :consequent_question_id, inverse_of: :consequent_rules
end
class Option < ActiveRecord::Base
belongs_to :question
has_one :rule
end
All done.
Finally
This is my first answer here at stackoverflow,and this method is never found anywhere else.
Thanks for reading.
add_index adds an index to column specified, nothing more.
Rails does not provide native support in migrations for managing foreign keys. Such functionality is included in gems like foreigner. Read the documentation that gem to learn how it's used.
As for the associations, just add the columns you mentioned in your Question to each table (the migration you provided looks fine; maybe it's missing a :rule_id?)
Then specify the associations in your models. To get you started
class Question < ActiveRecord::Base
has_many :options
has_many :assumption_rules, class_name: "Rule"
has_many :consequent_rules, class_name: "Rule"
end
class Rule < ActiveRecord::Base
belongs_to :option
belongs_to :assumption_question, class_name: "Question", foreign_key: :assumption_question_id, inverse_of: :assumption_rules
belongs_to :consequent_question, class_name: "Question", foreign_key: :consequent_question_id, inverse_of: :consequent_rules
end
class Option < ActiveRecord::Base
belongs_to :question
has_one :rule
end
Note This is just a (untested) start; options may be missing.
I strongly recommend you read
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
http://guides.rubyonrails.org/association_basics.html
Edit: To answer the question in your comment
class Option < ActiveRecord::Base
belongs_to :question
# ...
The belongs_to tells rails that the question_id column in your options table stores an id value for a record in your questions table. Rails guesses the name of the column is question_id based on the :question symbol. You could instruct rails to look at a different column in the options table by specifying an option like foreign_key: :question_reference_identifier if that was the name of the column. (Note your Rule class in my code above uses the foreign_key option in this way).
Your migrations are nothing more than instructions which Rails will read and perform commands on your database based from. Your models' associations (has_many, belongs_to, etc...) inform Rails as to how you would like Active Record to work with your data, providing you with a clear and simple way to interact with your data. Models and migrations never interact with one another; they both independently interact with your database.
You can set a foreign key in your model like this:
class Leaf < ActiveRecord::Base
belongs_to :tree, :foreign_key => "leaf_code"
end
You do not need to specify this in a migration, rails will pull the foreign key from the model class definition.
I'm modelling a scenario with Users and Tools, where a Tool is owned by one User but can be used by many Users including to one owning it.
I was thinking about adding an owner_id column to Tools and say it has_many Users or by adding a new relationsship table.
I'm really new to Rails and I have problems setting up the right associations in the models though, maybe you can point me in the right direction?
Thank you very much.
Your should add owner_id to the Tools table.
Associations will be like that.
class User < ActiveRecord::Base
has_and_belongs_to_many :tools
end
class Tool < ActiveRecord::Base
has_and_belongs_to_many :users
belongs_to :owner, :class_name => 'User'
end
You'll need a tools_users table in order to use habtm-association. Generate a migration and create a table with option id: false and two columns user_id and tool_id:
class CreateToolsUsersTable < ActiveRecordMigration
def change
create_table :tools_users, id: false do |t|
t.integer :tool_id
t.integer :user_id
end
end
end
After that you can call something like #user.tools or #user.owner
Read more there
User has many tools
Tool belongs to user in owner
Tool has many users
is what I would do.
I'm not sure about the wording because I don't use Active Record but this is how it works in other orms