I have a Rails app in which the user can subscribe to addons, the list is dynamic and contains currently about 10 addons.
The list specifies which is on/off.
Each addon has a rather unique set of properties.
My current solution is that the application has 2 "parent" models and the one new model for each addon:
class AddonPrototype
has_many :addons
end
class Addon
belongs_to :addon_prototype
belongs_to :user
end
class AddonAlpha
belongs_to :addon
end
class AddonBeta
belongs_to :addon
end
etc..
The model AddonPrototype has one instance of each addon, with the default name as the only property.
The model Addon with the properties enabled, custom_name. When the user is visiting the page with addons, a check is done to see whether the user has an Addon instance for each existing AddonPrototype, or else creating one on the fly.
For each addon there is a unique model (e.g. AddonAlpha, AddonBeta etc), with the set of properties apt for each particular addon.
This design feels cumbersome, what could be a leaner setup?
I'm probably missing out on some details, so take this post with a grain of salt.
Currently, the naming of the models seems to be a bit misleading. The AddonPrototype is an actual addon, whereas the Addon model represents the act of a user having installed an addon.
Here's the structure I would go for:
# app/models/user.rb
class User < ActiveRecord::Base
has_many :installations
has_many :addons, through: :installations
end
# app/models/addon.rb
class Addon < ActiveRecord::Base
has_many :installations
has_many :users, through: :installations
end
# app/models/installation.rb
class Installation < ActiveRecord::Base
belongs_to :addon
belongs_to :user
end
AddonPrototype has been renamed to Addon, and the old Addon model has been renamed to Installation (Subscription or something alike would also be a good name).
I would recommend against creating Installation records for each addon when a user visits the addons page. Just create these records when a user actually installs an addon. This keeps your code simpler and your database smaller.
Related
I am currently building my first application with Ruby on Rails (version 5.2.4.2 with ruby 2.6.3) and am having an issue with one of the associations. In my model there are applications in which you supply a single company that you are applying to. Logically I would like to be able to get an application and say application.company = Company.find_by(...).
To make this work, I have
# app/models/Application.rb
class Application < ApplicationRecord
has_one: company
end
# app/models/Company.rb
class Company < ApplicationRecord
end
# database migration
Class AddCompanyToApplication
def change
add_reference :companies, :application, foreign_key: true
end
end
Doing this allows me to use the desired syntax but has one issue. When I create a second application for the same company, the row in the companies table is changed that removes the company from the first application.
The list of companies is predetermined and I just want to refer to a company from within the application. Is there a way to be able to use the assignment syntax while having the foreign key placed in application rather than companies? From a database perspective I feel like application should hold the foreign key, but this breaks the assignment syntax. It seems like Ruby wants me to make Company have has_many :applications, but this breaks the semantics of what I am trying to accomplish. Is there a way to change the foreign key placement or should I forgo the assignment syntax and stick to SQL and assigning directly to id's?
Right now, you have a 1 to 1 relationship. An application has_one :company and a company belongs_to :application. That's why when you try to create a second application for the same company, the application_id in the companies table gets overridden. If you change your company association to has_many :applications, you should be able to add more than one application to a company. Then you can call application.company to return the application's current company or application.company = ... to assign a company to the current application.
class Application < ApplicationRecord
has_one :company
end
class Company < ApplicationRecord
has_many :applications
end
To make this work, you have to create a new migration:
Class AddCompanyIdToApplication
def change
add_reference :application, :company, foreign_key: true
end
end
Before you do that, you have to rollback your existing AddCompanyToApplication migration (which is named wrong, by the way, because you're not adding the company to the application, you're adding the application to the company) and delete the AddCompanyToApplication migration.
Read more about associations
Read more about add_reference
I am setting up an application and I am having issues with ActiveRecord relationships. here is the breakdown.
1. A Client has_many Sites therefore a Site belongs_to Client
class Client
has_many :sites
end
class Site
belongs_to :client
end
2. A Site has_many Reports therefore a Report belongs_to Site
class Site
has_many: reports
end
class Report
belongs_to :site
end
this is where I am getting stumped.
A Patrol has_many Hits therefore a Hit belongs_to Patrol
A Hit belongs_to Site therefore a Site has_many Hits
a Report belongs_to Site therefore a Site has_many Reports
the problem is setting this last part up not too sure to to layout the models or if a :through relationship is warranted?? Essentially what I am hoping to achieve here is that when an admin sets up a patrol and assigns a site to a hit the user can view the site through the hit show page and generate a new site report than can then be viewed under the client/site show page in the admin table.?
Am I out in outer-space here??
Nesting the client and site was a breeze and i can generate a new report from the site show page, but to streamline report creation for the end user I am hoping to go the above route.. just not sure about how to proceed.
If you require further assistance please let me know ill give you what I can!
Thanks.
EDIT # 1 My Model Structure
This group is nested as a site is built through the client show page
class Client
has_many :sites
end
class Site
belongs_to :client
end
This is where I am having the most troubel, as there is alot going on
here at least in my mind. I am open to any suggestions in configuring
this..
class PatrolRoute
has_many :patrol_hits
end
class PatrolHit
belongs_to :patrol_route
# A PatrolHit Should only have one Site and that site should not be created only selected from a dropdown box of pre-existing sites
end
class Site
belongs_to :patrol_hit
has_many: patrol_reports
end
Class PatrolReport
belongs_to :site
has_many :line_items
end
class LineItem
belongs_to :report
end
It is hard to provide you with exact answer since the whole thing and DB structure are unclear.
One thing that might help you to deal with all those associations is simple:
belongs_to :foo means that model's DB table should have foo_id column.
In your particular case
class PatrolHit
belongs_to :patrol_route
end
means that patrol_hits table should have columns like
id
patrol_route_id
...
I think keeping that rule in mind would help you.
I'm the middle of creating a RoR application which needs a Many-to-many association between the same table (at least in theory).
How so? Well, I'd need a User table which contains two kind of users: Server, and client, more or less like the idea of a Teacher and a student (with private lessons, but with multiple teachers), or a Doctor and a Patient
My first idea was to simply make a User table (you know, login, email, and personal info) and assign it a Role (Server, or client), but then I thought that making such association with a third-table would troublesome
USER <-----> USER_USER
But the idea of creating two "login" tables that represent each role, and a third-table for the association sounds wrong.
Client_Login <-----thru---> Client_Server <---thru---> Server
For simplicity sake, a client cannot be a server to another clients, and a server cannot be a client for another server.
Obviously, a server can have multiple clients, and a client has multiple servers
How would recommend modeling this relationship?
If you need to explicitly have different methods between the two, Server and Client, which I am assuming since you want different classes. Then you might want to look into Single Table Inheritance(STI). This will allow you to use one User table, but have two different models that use it.
class User < ActiveRecord::Base
belongs_to :another_model #example association that will exist for all user types
self.inheritance_column = :role
# if you need to be able to tell what role are available
def self.roles
%w(Client Server)
end
end
class Client < User
has_many :server_clients
has_many :servers, through: :server_clients
end
class Server < User
has_many :server_clients
has_many :clients, through: :server_clients
end
You then have to just setup a simple server_client.rb model for the bridge.
example from here: http://samurails.com/tutorial/single-table-inheritance-with-rails-4-part-1/
This will allow you to put common functionality for all Users in the User class, and specific functionality in the respective classes of Server and Client.
It's done all the time. It's quite common to have a many-to-many back to yourself. It's common in hierarchies dealing with people's relations to each other, (dependency, managers, children, etc... )
class User
has_many :user_relations, dependent: destroy, inverse_of: :user
has_many :dependent_users, through: :user_relations
has_many :dependent_upon_users, through: user_relations, source:
:dependent_upon
end
class UserRelation < ActiveRecord::Base
belongs_to :user
belongs_to :dependent_upon, class_name: User
validates_presence_of :user, :dependent_upon
end
I'm building my first Rails project and it's a checkout system for a computer loaner pool. Creating Technicians (who perform checkouts) and CheckOuts (like transactions) makes perfect sense. However, I'm struggling with the relationship between CheckOuts and LoanerComputers.
Technician and CheckOut have a 1:N relationship, and CheckOut and LoanerComputerhave a 1:1 relationship. I believe in my Rails-n00b heart that it would be nice to have association proxy, e.g. Technician.check_outs.loaner_computers or even better Technician.loaner_computers, but from what I've learned that would mean that my LoanerComputer class must contain the belongs_to, and that assumes that the LoanerComputer table in my database has a check_out_id column.
I've tried thinking about it from a "rental" approach, but I see lots of solutions that have a fourth model to store state changes of the thing being "rented." To me, it makes more sense to have technician_id and loaner_computer_id in a single CheckOut entry, but then how could I easily access a technician's checked-out loaner computers using association proxy? Is it possible to use :delegate in this instance, or does it look like I'd have to make a custom method to read loaner computers via technicians? Here's example code:
class Technician < ActiveRecord::Base
has_many :check_outs
end
class CheckOut < ActiveRecord::Base
belongs_to :technician
# has_one :loaner_computer
# OR
# belongs_to :loaner_computer (which means I need to have a "loaner_id" column in the db, right?)
end
class LoanerComputer < ActiveRecord::Base
# belongs_to :check_out (which means I need to have a "check_out_id" column in the db)
# OR
# has_one :check_out
end
P.S. Do I just have it all backwards? Should I say Technicians has_many LoanerComputers, and LoanerComputers has_many CheckOuts?
Thanks for your time! Let me know if anything needs clarification!
I think, what you're looking for - is "has_many through" association.
http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
In Your case - It'll look like this
class Technician < ActiveRecord::Base
has_many :check_outs
has_many :loaners, through: :check_outs
end
class CheckOut < ActiveRecord::Base
# Must have technician_id and check_out_id fields in DB
belongs_to :technician
belongs_to :check_out
end
class Loaner < ActiveRecord::Base
has_many :technicians, through: :check_outs
end
With this - You'll be able to access loaners from technician and reverse
Technician.loaners
Loaner.technicians
You can also access check_outs from both models
In my rails app i use active admin, now i have such model (only part)
class Distributor < ActiveRecord::Base
attr_accessible :co****ime
has_many :price_lists
has_many :other_products_cross_lists
end
class OtherProductsCrossList < ActiveRecord::Base
attr_accessible :cross***pe_id
belongs_to :product_type
belongs_to :distributor
belongs_to :accumulator
belongs_to :oil
end
class Accumulator < ActiveRecord::Base
attr_accessible :capa***age
has_many :other_products_cross_lists
end
For security, i * attr...
So if i create a new Accumulator, i also need so, that with accumulator field's i see OtherProductsCrossList fields, and create accumulator, and cross for it, also i need to do such button :more, so if i need to create more than one cross for this accumulator it will do this. Also note, that id and refferertable_id must be same... How to do this? Form with another form for create action?
if you have a complicated/nested form, I don't think Active Admin is a good choice. You have to learn its DSL and have to face its bugs.
And writting your own implementation is:
not so hard with ERB/HAML's help, ERB/HAML is more easy to use than ActiveAdmin's View DSL.
very flexible, you are free to change the pages(CSS) and the models.
I tried ActiveAdmin for several projects, but very soon I abandoned it, because not only it gives me too few benefits/convenience, but also gives me too many difficulties to make my own customization.
To me, Active Admin is similar with the "Scaffold", but much worse.