I've had some experience with some self projects in Rails and I'm having some trouble creating the data model for this specific scenario. Basically, there are many users, each of whom can play many instruments, and for each user/instrument pairing, there is a certain skill level associated with it.
For example, Joe can play saxophone with skill level 5, clarinet with skill level 2, and trumpet with skill level 3. Bob can play trombone with skill level 1, saxophone with skill level 4, and clarinet with skill level 5.
I understand how to make this with traditional SQL, but I really want to be able to take advantage of the Rails ActiveRecord features (so, in theory, I could do something like this:
#users = User.all
#users.each do |user|
user.instruments do |ins|
puts ins.level #The current user's skill level on a particular instrument
end
end
How do I create the migrations/models to achieve this? Thanks!
the models are basically:
class User < ActiveRecord::Base
has_many :instrument_skills
has_many :instruments, through: :instrument_skills
end
class InstrumentSkill < ActiveRecord::Base
belongs_to :instrument
end
class Instrument < ActiveRecord::Base
end
that said you could create the models like::
rails g model User name:string
rails g model InstrumentSkill instrument_id:integer user_id:integer level:integer
rails g model Instrument name:string
then your generated migration probably look like:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
t.timestamps
end
end
end
class CreateInstruments < ActiveRecord::Migration
def change
create_table :instruments do |t|
t.string :name
t.timestamps
end
end
end
class CreateInstrumentSkills < ActiveRecord::Migration
def change
create_table :instrument_skills do |t|
t.integer :user_id
t.integer :instrument_id
t.integer :level
t.timestamps
end
end
end
Related
Lets say that I have an User model with its attributes (first_name, last_name, etc) and I want to create two new models Teacher and Student.
They will inherit the User model attributes, and also, they will have specific attributes. For instance, the Student model will have a file attribute, and the Teacher model will have subject attribute.
I was reading about STI (Single Table Inheritance) and Polymorphic relationships.
What should I look for to accomplish this? Do you have any example to show?
If you create an attribute called "type" on your users table, Rails will automatically assume you want to implement STI. Then, creating Teacher and Student models is as simple as extending the User class. The name of the child class will automatically be inserted into the type column and used to filter queries as you would expect.
user.rb
class User < ApplicationRecord
end
teacher.rb
class Teacher < User
end
student.rb
class Student < User
end
With STI, you place all of the columns that either model will use in the same table and simply ignore (default to null) the ones that don't apply in any give situation.
A polymorphic relationship allows two or more tables to fill the same association. If you want to use three different tables but ensure that a User has either a Teacher or a Student, that could be modeled as a polymorphic belongs_to. The downside is that you would need to get back to the User model to access the shared information, i.e. teacher.user.first_name.
I have found this gem that looks like what I am looking for. I have played with it a little and it does work for me.
https://github.com/krautcomputing/active_record-acts_as
So, for my case I have added to the Gemfile:
gem 'active_record-acts_as'
Then:
$ bundle
These are my migrations:
# 20171202142824_create_users.rb
class CreateUsers < ActiveRecord::Migration[5.1]
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.date :birth_date
t.string :dni
t.string :cuil
t.string :email
t.string :phone
t.string :address
t.string :postal_code
t.string :city
t.string :state
t.string :country
t.actable # This is important!
t.timestamps
end
end
end
# 20171202142833_create_students.rb
class CreateStudents < ActiveRecord::Migration[5.1]
def change
create_table :students do |t|
t.string :file
# Look, there is no timestamp.
# The gem ask for it to be removed as it uses the User's timestamp
end
end
end
# 20171202142842_create_teachers.rb
class CreateTeachers < ActiveRecord::Migration[5.1]
def change
create_table :teachers do |t|
# Look, there is no timestamp.
# The gem ask for it to be removed as it uses the User's timestamp
end
end
end
These are my models:
# user.rb
class User < ApplicationRecord
actable
validates_presence_of :first_name, :last_name
def full_name
[last_name.upcase, first_name].join(', ')
end
end
# student.rb
class Student < ApplicationRecord
acts_as :user
validates_presence_of :file
end
# teacher.rb
class Teacher < ApplicationRecord
acts_as :user
end
Now, with all that set, you can simply create a new Student and a new Teacher doing:
Student.create!(first_name: 'John', last_name: 'Doe', file: 'A125')
=> #<Student id: 3, file: "A125">
Teacher.create!(first_name: 'Max', last_name: 'Power')
=> #<Teacher id: 1>
You have access to all the methods and attributes of the User. For instance:
Teacher.last.full_name
=> "POWER, Max"
Ok so what I have been doing for categories is I have had 2 tables Categories(id, name) and SubCategories(id, name, category_id) that are related through the models. I set inverse_of as you will see below.
My question is when I use to code in PHP years ago we use to have one table "Categories" and it had id, name, parent_id(0 by default) we then used that one table to control the outputs of navigations, breadcrumbs, and other navigational elements.
class CreateCategories < ActiveRecord::Migration[5.1]
def change
create_table :categories do |t|
t.string :name
t.timestamps
end
end
end
class CreateSubCategories < ActiveRecord::Migration[5.1]
def change
create_table :sub_categories do |t|
t.string :name
t.integer :category_id
t.timestamps
end
end
end
class Category < ApplicationRecord
has_many :sub_categories, inverse_of: :category
end
class SubCategory < ApplicationRecord
belongs_to :categories, inverse_of: :sub_category
end
I have been programming in Ruby on Rails now for over 4 years and I have yet to find a real nice "Rubyway" to achieve this. Over the years I have seen examples of using what I am already using with the 2 table method, however this does not seem very intuitive because when the system gets many categories and sub categories like 100's the page load time will be impacted in processing. Is anyone useing or know of a one table method like
class CreateCategories < ActiveRecord::Migration[5.1] def change
create_table :categories do |t|
t.string :name
t.integer :parent_id
t.timestamps
end
end
end
The problem I have always had is the model and how to get the system to realize that a record can belong to a record on the same table. I have been able to achieve it manually but I have not found a way to set it up where formtastic and other gems like rails admin would play nice with it.
Seems that you're looking for a tree structure. acts_as_tree has been around for some time. If you're using PostgreSQL, the ltree extension may be of interest as well (along with the pg_ltree gem).
I am building a rails exercise app that has both exercises and routines. I want each routine to be composed of several exercises (has_many :exercises), but an exercise doesn't necessarily have to belong to a routine. Is there a way to do that?
Reading the guides is always a good start. This works from Rails 5 onwards.
belongs_to :routine, optional: true
You probably want a many-to-many relationship here, rather than a one-to-many relationship.
It would seem you would want for an Exercise to be associated with any number of Routines, and for a Routine to be associated with one or more Exercises.
You end up with something like this:
# app/models/routine.rb
class Routine < ActiveRecord::Base
has_and_belongs_to_many :exercises
end
# app/models/exercise.rb
class Exercise < ActiveRecord::Base
has_and_belongs_to_many :routines
end
# db/migrate/1213123123123_create_exercises_routines_join_table.rb
class CreateExercisesRoutinesJoinTable < ActiveRecord::Migration
def self.change
create_table :exercises_routines, :id => false do |t|
t.integer :exercise_id
t.integer :routine_id
t.index [:category_id, :routine_id]
end
end
end
Model:
Material
Manager
Relationship
One item needs to be approve by many managers.
One manager need to approve many items.
I do not know how to design a many_to_many relationship.
I planed to create a joined table like this
Model Approval:
item_id,
manager_id
The problem is that if I design model like this approval will have one manager or several managers, but the situation is that the number of managers could change all the time, so it is hard to set a certain number of managers in the model Approval.
How to solve this problem?
models:
class Material < ApplicationRecord
has_and_belongs_to_many :managers
end
class Manager < ApplicationRecord
has_and_belongs_to_many :materials
end
migrations:
class CreateManagerssAndMaterials < ActiveRecord::Migration
def change
create_table :managers do |t|
end
create_table :materials do |t|
end
create_table :managers_materials, id: false do |t|
t.belongs_to :manager, index: true
t.belongs_to :material, index: true
end
end
end
You can find more here
rails migrations has_and_belogs_to_many
for example in controller:
#material = Material.find(5)
#material.managers << [Manager.find(1), Manager.find(2)]
I have 3 tables: proposals, items/proposals (items is nested inside proposals) and invoices.
I want to create invoices for those items in the proposals that got approved. How would the associations for these look like? Also, how would I set up the invoices form to choose only those items that got approved by the client?
Consider creating two different line items models for Proposal and Invoice.
class Proposal < ActiveRecord::Base
has_many :proposal_line_items
end
class ProposalLineItem < ActiveRecord::Base
belongs_to :proposal
end
class Invoice < ActiveRecord::Base
has_many :invoice_line_items
end
class InvoiceLineItem < ActiveRecord::Base
belongs_to :invoice
end
You can consider having an "approved" attribute in proposal line items. In the invoice form, you can show proposal line items approved by the client.
The suggestion of having separate line items for Proposal and Invoice is based on ERP data modeling principles to maintain the integrity of Invoice.
Update
For example here are the sample migrations for the models suggested
class CreateProposalLineItems < ActiveRecord::Migration
def change
create_table :proposal_line_items do |t|
t.references :proposal, index: true, foreign_key: true
t.string :name
t.integer :approved
t.timestamps null: false
end
end
end
class CreateProposals < ActiveRecord::Migration
def change
create_table :proposals do |t|
t.string :name
t.timestamps null: false
end
end
end
class InvoicesController < ActionController
def new
#approved_items = Proposal.find(params[:proposal_id]).proposal_line_items.where(:approved => 1)
end
end
You can iterate over the #approved_items in your view and display it to users.
V