I've struggled with this for the past few hours, I'm trying add the capability to a model where they are able to enter more than one price options to a model.
For example
Regular(price)
Luxury (price) etc..
I currently have a model
Pass.rb
belongs_to :user
validates_numericality_of :price,
greater_than: 49,
message: "must be at least 50 cents"
validates :title
My form for this model is normal letting a user enter a price. In order to have more than one price option how would I design that in the database or is this done more within the controller. Lastly if you make it available for user choice does it save as the official price for that model or would it be something extra to include when referencing the model's price within another controller. Example I'm using stripe to handle payment so if I have
sale = Sale.new do |s|
s.amount = pass.price
Would that represent the price the user selected. Sorry if this explained badly I haven't been able to find any resources to help me get started at solving this issue
In order to have more than one price option how would I design that in the database
class PriceOption < ActiveRecord::Base
belongs_to :pass
...
end
class Pass < ActiveRecord::Base
has_many :price_options
...
end
create_table "price_options", force: :cascade do |t|
t.integer "pass_id"
...
end
Related
Say we have 3 different applications - serviceapp, subscriptionapp, ecomapp, all written in ruby on rails and uses the same database in backend and tables in the backend. So the user table for all these three applications are same. If a user is part of serviceapp using the same email and credentials he can login into subscriptionapp or ecomapp and vice versa.
The reason behind choosing same user table and other table for all the application is puerly business perspective - same single crm and ticketing system for sales and cdm team to track everything. Devise is being used in all three applications along with LDAP so login and signup works fine without any issue.
Problem:
Till now users' last_login_at is a single column so we really can't tell which app he last logged in at. But now we have to start logging these details separately, like when did he last login at serviceapp, ecomapp, subscription app separetly.
Also we are starting to use a new crm of one particular app - subscriptionapp and for the clients(users) of that particular app we have to store extra information like unq_id from crm and so on.
My intial thought is to add these columns in the user table itself. But in the future we might add few extra information to user table which are app specific. Hence adding it to the main user table won't be a good idea for this. How shall I proceed in this case? I though of creating three different tables like subscriptionapp_client, ecomapp_client, serviceapp_client had associating them with the user table like user has_one ***_client.
If the association is present like if user.subscriptionapp_client.present? he is a client of that app and we can store the last login at, crm_uniq_id and all in there in that table itself.
Is there anyother good approach that might fit the problem here? I am reading about MTI but it looks like it won't solve the problem.
Single table inheritance with JSON.
class CreateClientAccount < ActiveRecord::Migration[5.2]
def change
create_table :client_accounts do |t|
t.references :user
t.string :uid # the unique id on the client application
t.string :type
t.integer :sign_in_count
t.datetime :last_sign_in_at
t.jsonb :metadata
t.timestamps
end
add_index :client_accounts, [:user_id, :type], unique: true
end
end
class User
has_many :client_accounts
has_one :service_account, class_name: 'ServiceApp::ClientAccount'
# ...
end
class ClientAccount < ApplicationRecord
belongs_to :user
validates_uniqueness_of :user_id, scope: :type
end
module ServiceApp
class ClientAccount < ::ClientAccount
end
end
module SubscriptionApp
class ClientAccount < ::ClientAccount
end
end
module EcomApp
class ClientAccount < ::ClientAccount
end
end
This avoids the very unappealing duplication of having X number of tables in the schema to maintain and the JSONB column still gives you a ton of flexibility. However its in many ways just an upgrade over the EAV pattern.
It also has a lot in common with MTI. In MTI you would use an association to another table which fills the same purpose as the JSON column - to make the relational model more flexible. This can either be polymorphic or you can have X number of foreign keys for each specific type.
One table for each type.
class User < ApplicationRecord
has_one :subscription_account
has_one :service_account
# ...
end
class ClientAccount < ApplicationModel
self.abstract_class = true
belongs_to :user
end
class SubscriptionAccount < ClientAccount
end
class ServiceAccount < ClientAccount
end
# ...
This is the most flexible option but if you want to add a feature you will have to create migrations for each and every table. And this also means that you can't query a single homogenous collection if you want all the types. You have to perform X number of joins.
This is not really that appealing unless the requirements for each type are wildly different.
I have a Ruby on rails application where users can add one or more software.
These users can then subscribe (paid mode).
I would like to be able to display all paid user software only.
I tried several things and still can not find the solution.
for example:
Software.includes(:users).where(user: {subscribed: true})
EDIT:
Model USER:
class User < ActiveRecord::Base
has_many :softwares
end
Model Softwares:
class Software < ActiveRecord::Base
belongs_to :user
end
JoinTable
class CreateJoinTableUsersSoftwares < ActiveRecord::Migration[5.2]
def change
create_join_table :users, :softwares do |t|
t.index [:user_id, :software_id]
t.index [:software_id, :user_id]
end
end
end
ERROR with :
Software.includes(:users).where(user: {subscribed: true})
ActiveRecord::ConfigurationError (Can't join 'Software' to association named 'users'; perhaps you misspelled it?)
I think your error is here:
Software.includes(:users).where(user: {subscribed: true})
The includes should reflect the association, in this case a singular user. This is what's causing the error you're seeing.
Also, it's a common gotcha, but the association within the where clause needs to use the table name.
Try this:
Software.includes(:user).where(users: { subscribed: true })
This assumes you'll be using the info on the user elsewhere, i.e. in your view. If you don't need to access the record, rather just check the user for the query, you can switch includes to joins to improve the query's efficiency:
Software.joins(:user).where(users: { subscribed: true })
It's a separate topic, but there's a good read on it here.
I have a parent ProductCategory and child Product. For example:
ProductCategory --- Product
Drill --- DeWalt DWD 112
--- Black & Decker 5 C
Bicycle --- Motobecane Turino ELITE Disc Brake
--- Shimano Aluminum
For a given ProductCategory, there are a set of attributes that all Products should be comparable with each other on (i.e., have data on). However, this set of attributes is likely to vary between ProductCategories
For example, for the ProductCategory of Drill, the attributes might be Voltage, Amps, Corded vs Cordless. Every Product Drill needs to have such information. However, for the ProductCategory of Bicycle, the attributes should be Size, Road vs Mountain, and every Product bicycle needs to have this information. (Sorry I don't know anything about either drills or bikes... why I picked this was stupid)
I'm trying to design the DB such that for a given Product, the attributes are something that I can easily search. For example, ideally I can run this command:
drills = Product.where(product_category_id:1)
drills.where("voltage >= ?", 5)
-> returns the individual drills, which may include DeWalt but not Black & Decker
This seems to present an interesting trade-off... because then I'd have to have Product have columns for every attribute for every ProductCategory, even those that aren't relevant to it. For example:
# Product columns
:voltage, :integer #for Drill
:amps, :integer #for Drill
:corded, :boolean #for Drill
:size, :integer #for Bicycle
:mountain, :boolean #for Bicycle
...
This doesn't seem sustainable... you can see very quickly that for just a few ProductCategories there will soon be an infinite number of Product columns!
At the other end of the spectrum, I thought about having defining attributes required of each Product in the parent ProductCategory, and then requesting these attributes/storing them on Product as a stringified data:
# ProductCategory has a column...
:required_attributes, :text
ProductCategory.where(name:"Drill").first.required_attributes
-> "voltage,amps,corded"
ProductCategory.where(name:"Bicycle").first.required_attributes
-> "size,mountain"
# Product has a column...
:attribute_data, :text
Product.where(name:"DeWalt").first.attribute_data
-> "{'voltage':5,'amps':5,'corded':5}"
With the design above, I could create a front end that enforced that, upon Product creation, one has to provide information for each required_attributes after it's been split based on commas. But of course, this makes searching much less efficient, at least I THINK it does... so this is my question. How can I efficiently search stringified data? If I'm searching for all Drills with at least 5 volts, so complete the below.
drills = ProductCategory.where(name:"Drill")
drills.where("attribute_data ...")
The simplest solution is just to use a JSON or HSTORE datatype column on products to store the specifications.
But if you want to have a bit more control and validations per specification you can use a design with a join table:
class Product
has_many :specs
has_many :definitions, through: :specs
end
# This is the "normalized" table that defines an specification
# for example "Size". This just holds the name for example and the acceptable values.
class Definition
has_many :specs
has_many :products, through: :specs
end
# this contains the actual specs of a product (the value)
# specs.payload is a JSON column
class Spec
belongs_to :definition
belongs_to :product
delegate :name, to: :definition
def value
payload[:value]
end
def value=(val)
payload[:value] = val
end
end
One of the classic problems with using a design like this would be that the specs table would have to store the value as a text (or varchar) column and deal with the issues of type casting. But most modern DB's support dynamic column types like HSTORE or JSON that you can use to store the actual value.
The downside is that you have to use a special SQL syntax when querying:
Spec.where("payload->>'value' = ?", "foo")
This is a sort of normalized variation on what is called the Entity–attribute–value model which can be an anti-pattern but is often the only good solution to dynamic attributes in a relational database.
See:
ActiveRecord and PostgreSQL
ActiveRecord::ActsAs - Multi Table Inheritance for AR
Another way to approach the problem which avoids the issue of EAV tables is to use multi-table inheritance. This example uses ActiveRecord::ActsAs.
class Product < ActiveRecord::Base
actable
belongs_to :brand
validates_numericality_of :universal_product_code, length: 12
validates_presence_of :model_name, :price
end
class Bicycle < ActiveRecord::Base
acts_as :product
validates_numericality_of :gears
validates_presence_of :size
end
class PowerTool < ActiveRecord::Base
acts_as :product
validates_numericality_of :voltage, :amps
validates_presence_of :voltage, :amps
end
This would store the base information on a products table:
change_table :products do |t|
t.decimal :price
t.sting :model_name
t.integer :universal_product_code, length: 12
t.integer :actable_id
t.string :actable_type
end
And it uses a polymorphic association to store the subtypes of products on more specific tables:
create_table :bicycles do |t|
t.integer :gears
t.integer :size
end
create_table :power_tools do |t|
t.boolean :corded
t.integer :amps
t.integer :size
end
The advantage here is that you have a defined schema instead of a bunch of loose attributes.
The drawback is if you are designing a generic web shop then a fixed schema will not cut it.
I need to know, if there is a way to deal with different type of users/clients in Rails using a single model.
What I really need to do?
- I need to save a different type of clients in my database. So, I have this migration:
class CreateClients < ActiveRecord::Migration
def change
create_table :clients do |t|
t.string :name # Person name if "Personal", Company name if type "Company"
t.string :nif # fillable only if type is Company
t.string :identity_card_number # fillable only if type is Personal
t.integer :client_type # Type can be Personal or Company
t.references :company, index: true # if type is personal, it can belong to a company
t.timestamps null: false
end
end
end
Then I create this model
class Client < ActiveRecord::Base
has_many :employees, class_name: 'Client', foreign_key: 'company_id'
belongs_to :company, class_name: 'Client'
end
Note: A personal account can belong to a company or not.
Based on your experience, am I doing this in the right way? There are another way to do that?
EDIT:
Hi #manfergo25,
Whit this I have another question. "Company" and "Personal" are both "Clients Account", in that way, both must be able to buy services.
If I need to associent the client with the service, can I do this?
class Personal < Account
has_many :services
end
and
class Service < ...
belongs_to :account
end
??
The right way is Single Table Inheritance (STI) as Sontya say.
class Account < ActiveRecord::Base
end
Then,
class Client < Account
end
class Provider < Account
end
You only have to add a type column in 'Account' to contain a string representing the type of the stored object.
For example in a controller you could do this:
account = Client.find(params[:autocomplete_client])
params[:service][:account_id] = account.id
#service = Service.new(params[:service])
You can use STI(Single Table Inheritance)
class Account < ActiveRecord::Base
end
class Company < Account
has_many :services, :dependent => :destroy
end
class Personal < Account
has_many :services, :dependent => :destroy
end
class Service < ActiveRecord::Base
belongs_to: personal
belongs_to: company
end
With the above definition, a personal and company should be able to buy services.
and you should be able to call
#company.services # it will return you the number of services of company
#personal.services # it will return you the number of services of personal
Personally, I think the way you have suggested in your question is the way I'd do it.
While Single Table Inheritance is intellectually better modelling -- I've found STI to be a bit hard to work with and sometimes unreliable in Rails. And probably won't improve the cleanliness or conciseness of your code all that much in the end anyway. STI is good to keep in mind as an option, if you find the OP approach is not working well as far as allowing you to write clear concise code and it seems STI could work out better.
But I'd start without STI, with just the one Client class, as you've outlined in your question. If you later add STI, you'd still have the Client class, you'd just have sub-classes for, say, PersonalClient and CompanyClient. It won't be that hard to switch to STI later if you want to, although it might require some minor db schema alterations.
But I don't think it'll get you enough benefit to justify the added complexity, in an area of Rails that has sometimes had some rough edges.
Here's some more info about STI and it's plusses and minuses: http://eewang.github.io/blog/2013/03/12/how-and-when-to-use-single-table-inheritance-in-rails/
I have 3 models in my rails app
class Contact < ActiveRecord::Base
belongs_to :survey, counter_cache: :contact_count
belongs_to :voter
has_many :contact_attempts
end
class Survey < ActiveRecord::Base
has_many :questions
has_many :contacts
end
class Voter < ActiveRecord::Base
has_many :contacts
end
the Contact consists of the voter_id and a survey_id. The Logic of my app is that a there can only be one contact for a voter in any given survey.
right now I am using the following code to enforce this logic. I query the contacts table for records matching the given voter_id and survey_id. if does not exist then it is created. otherwise it does nothing.
if !Contact.exists?(:survey_id => survey, :voter_id => voter)
c = Contact.new
c.survey_id = survey
c.voter_id = voter
c.save
end
Obviously this requires a select and a insert query to create 1 potential contact. When I am adding potentially thousands of contacts at once.
Right now I'm using Resque to allow this run in the background and away from the ui thread. What can I do to speed this up, and make it more efficient?
You can do the following:
Contact.where(survey_id: survey,voter_id: voter).first_or_create
You should add first a database index to force this condition at the lowest level as possible:
add_index :contacts, [:voter_id, :survey_id], unique: true
Then you should add an uniqueness validation at an ActiveRecord level:
validates_uniqueness_of :voter_id, scope: [:survey_id]
Then contact.save will return false if a contact exists for a specified voter and survey.
UPDATE: If you create the index, then the uniqueness validation will run pretty fast.
See if those links can help you.
Those links are for rails 4.0.2, but you can change in the api docks
From the apidock: first_or_create, find_or_create_by
From the Rails Guide: find-or-create-by
It would be better if you let MySQL to handle it.
Create a migration and add a composite unique key to survey_id, voter_id
add_index :contact, [:survey_id, :voter_id], :unique=> true
Now
Contact.create(:survey_id=>survey, :voter_id=>voter_id)
Will create new record only if there is no duplicates.