I have a model named PaypalPayment:
class PaypalPayment < PaymentMethod
belongs_to :order
def provider_class
PaypalPayment
end
def process!
end
end
I generated the following migrations for it:
class CreatePaypalPayments < ActiveRecord::Migration
def change
create_table :paypal_payments do |t|
t.integer :order_id
t.integer :payment_id
t.timestamps
end
end
end
and
class AddDetailsToPaypalPayment < ActiveRecord::Migration
def change
add_column :paypal_payments, :state, :string
add_column :paypal_payments, :amount, :decimal
add_column :paypal_payments, :cc, :string
add_column :paypal_payments, :cm, :string
end
end
After the migration the table looks something like:
development_database=# select * from paypal_payments;
id | order_id | payment_id | created_at | updated_at | state | amount | cc | cm
But when I try to initialize an object of this model, I'm getting the unknown attribute: payment_id.
#paypal_payment = PaypalPayment.new(:payment_id => params[:tx], :state => params[:st], :cc => params[:cc], :cm => params[:cm], :order_id => params[:id])
EDIT: db/schema.rb:
create_table "paypal_payments", :force => true do |t|
t.integer "order_id"
t.integer "payment_id"
t.datetime "created_at"
t.datetime "updated_at"
t.string "state"
t.decimal "amount"
t.string "cc"
t.string "cm"
end
There are different ways to model inheritance in a relational database, Martin Fowler lists the following options:
Single Table Inheritance : all classes are stored in a single table
Class Table Inheritance : all classes have their own table
Concrete Table Inheritance : only concrete classes have a table (e.g. in your example PaymentMethod if being abstract, would not have a table)
Now ActiveRecord only supports STI: single table inheritance.
So if you write
class PaypalPayment < PaymentMethod
ActiveRecord will assume STI and look for a type column, and furthermore, will only look for payment_methods table.
Depending on what you want, in most cases, STI is just perfect. Sometimes I prefer the Class and Concrete Table Inheritance better, but especially for associations this needs a little more householding, since:
e.g. you have different payment-methods, but they are stored in different tables
do you want to access all payment methods at once, you need the "abstract class"
you need an association per possible payment-method
if you have the "abstract class", how do you link to the "real payment method". One way is to include table-name and id of the child as the link.
There are lots of way to solve this, but always harder than using a single table. Also this is stretching the relational datamodel, as depending on the chosen solution, foreign key constraints are not automatically supported. I could go into detail,
but I am not sure if this is relevant, as your example seems a classic case for STI.
If you do want to use Class Table Inheritance or Concrete Table Inheritance, each class has to derive from `ActiveRecord::Base`` and you should include a module (or concern) with the shared behaviour if needed (since ruby does not support multiple inheritance).
I believe you have to add the column "type" to your PaymentMethods table. This will allow it to be inheritable. Without the type column, when you instantiate a PaypalPayment, it thinks it's a PaymentMethod and hence has none of the unique fields of PaypalPayment. However when you add the column "type" to PaymentMethod, then it will store "PaypalPayment" and ActiveRecord knows to make the PaypalPayment methods available. You should probably make a model for PaymentMethod also and make sure it inherits ActiveRecord::Base
def change
add_column :payment_methods, :type, :string
end
Here's some info:
http://www.archonsystems.com/devblog/2011/12/20/rails-single-table-inheritance-with-polymorphic-association/
I'd do this:
Check your Rails Console --
$ rails c
$ payment = PaypalPayment.find(1)
$ payment.column_names #-> should reveal which columns Rails comes back with
Check Rails is picking up the attribute
For testing's sake, just try attr_accessor :payment_id to see if that works. You might not have permitted the attribute in your model
In Rails4, that means using strong params, but in Rails 3, I think it means using attr_accessible like this:
#app/models/paypal_payment.rb
Class PaypalPayment < ActiveRecord::Base
attr_accessible :payment_id #-> tests parameter passing
attr_accessor :payment_id #-> tests virtual attribute assignment
end
I know I'm a bit late the show here, but if anyone is encountering a similar problem with Rails 5.1, in my case I was able to resolve the issue by including the following line in my parent classes
self.abstract_class = true
Related
I'm working on CRM platform.
I would like my users to add, edit and delete custom fields in Client, Contact and Lead objects. Those fields may be plain textfield, list, checkbox, tag etc. Those fields may be required or not. Those fields may have custom validation (that user will define).
Say one company from financials would like to add income to Client object, another would add order configuration to Lead object.
Is there any "enterprise-level" solution (ROR gem) for my problem.
Of cause I know about Custom configuration and config gem, but it doesn't look extensible enough.
Hard question, but this is how I would try to deal with it: I would make all the objects to be derived from a CustomField object, then I would create a one to many relationship between it and a Field model. Something like this:
create_table :field_types do |t|
t.string :name # This would identify the fields: checkbox, plain text, etc
end
create_table :fields do |t|
t.belongs_to :custom_field, null: false, index: true
t.belongs_to :field_type, null: false, index: true
t.string :name
end
class Field < ApplicationRecord
belongs_to :custom_field
belongs_to :field_type
end
class CustomField < ApplicationRecord
has_many :fields
end
This way you could just look into the specified fields on the database and mount it at the view.
Then I would create a table for each type of field that could be used by the users to save the data from the CustomField objects. For instance, I would check the Client field specifier, mount a view with checkboxes A and B. Then, I would get the data from the checkboxes and save each of them at the table Checkboxes with an identifier, so that I could tell that it came from clients.
Depending on what you need to do, another idea that pops to my head is to save the data as a JSON string into the database. This way you could have different fields with different values, all you would need to do is serialize and deserialize to save and load it from the database, respectively.
Sorry if it was a little confusing. Hope it helps.
Assuming your database is relational:
I would suggest to use Entity-Attribute-Value pattern:
https://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value_model.
Here is a gem for it:
https://github.com/iostat/eav_hashes
Also document-oriented database like MongoDB would be an option, if you ever consider changing database. It is schemaless, so you can have different attributes for different instance.
I'm not aware of any out of the box options available, but you might be better off rolling your own on something like this anyway. It will allow you more flexibility, and shouldn't be terrible to implement. In terms of models, I'd probably go with a single-table inheritance table for the fields, probably using a jsonb column for customization options (assuming postgres):
create_table :fields do |t|
t.string :type, null: false # TextField, ListField, etc.
t.jsonb :config, default: {}, null: false
t.belongs_to :contact
end
You can then subclass as necessary for different use-cases:
class Field < ApplicationRecord
belongs_to :contact
end
class TextField < Field
def required=(required)
config[:required] = required
end
end
class CheckboxField < Field
def default_checked=(default_checked)
config[:default_checked] = default_checked
end
end
You can look into something like jsonb_accessor to make for a cleaner interface to the jsonb column.
Likewise, single-table inheritance looks like it may also make sense for the contacts as well, not sure what the base table should be, but maybe something like:
create_table :contacts do |t|
t.string :type, null: false # Contact, Lead, Client
end
class Contact < ApplicationRecord
end
class Lead < Contact
end
Here are some examples I found helpful for custom fields:
http://railscasts.com/episodes/403-dynamic-forms?view=asciicast
And:
https://github.com/lab2023/postgresql_jsonb_ransack_rails_5
https://gist.github.com/ismailakbudak/2ca1feac945999ec3e7d9cf0a373497a
I'm working on an application where a guest will be provided a short Base64 token that they could in turn use to access the edit function of one of several different models via one "search form" on the application homepage.
I have already created the token functionality and included it in the schema for the models I need. My question is, how would one best search for and access the edit function using the access token from the home page?
I'm having a hard time finding a good way to do this and while I'm finding a lot about access tokens, most of it doesn't seem to pertain to my use case.
Rails provides the ability for model classes to be inherited from a parent model class. Then the models can have shared attributes, but also unique ones. In the database all of these model objects are stored in the same table for all classes, so this is called Single Table Inheritance or STI. (Documented here but there are better docs in blog posts out there.)
If you use this approach, then you could search the parent class for all instances to find matching objects/records.
class AccessToken < ActiveRecord::Base
# has attribute access_token, and maybe others
end
class OneAccessibleKind < AccessToken
# may have other attributes
end
class AnotherAccessibleKind < AccessToken
# may have other attributes
end
Your migration would look something like this:
create_table :access_token do |t|
t.string "access_token"
t.string "type"
# add any additional attributes of subclasses
t.timestamps
end
You can then query against the parent class. Note
all_models = AccessToken.where(access_token: 'a-token')
Note that these will all come back as AccessToken objects (i.e. the parent class), but you can inspect the type attribute to see what their base class is.
This may not be the best solution, however, if your classes are mostly different fields because you'll have lots of unused columns. Depending on your backing database (assuming row-oriented SQL) and number of objects this could be a performance problem.
Another option would be to use a one-to-one relationship and have an AccessToken model for each of your other models. Here you can use an STI association.
class AccessToken < ActiveRecord::Base
belongs_to :owner, :polymorphic => true
end
class OneAccessibleKind < ActiveRecord::Base
has_one :access_token, :as => :owner
end
class AnotherAccessibleKind < ActiveRecord::Base
has_one :access_token, :as => :owner
end
With migrations something like this:
create_table :access_token do |t|
t.string :access_token
t.integer :owner_id, null: false
t.string :owner_type, null: false
t.timestamps
end
create_table :one_accessible_kind do |t|
# any attributes for this type
t.timestamps
end
Then you can find an access token and access each owner to get the objects.
AccessToken.where(access_token: 'a-token').map(&:owner)
So I've got two models, State and Acquisition. State has_many Acquisitions. I felt like an autoincrementing integer primary key for 51 records was rather silly. So I altered the model for the State to be the PK (State being the two letter abbreviation; I'm not storing the actual state name anywhere:
class State < ActiveRecord::Base
self.primary_key = "state"
has_many :acquisition_histories
end
The problem is when I created my Acquisition model, it created the foreign key column state_id as an integer. More specifically, the script/generated migration did:
class CreateAcquisitions < ActiveRecord::Migration
def self.up
create_table :acquisitions do |t|
t.date :date
t.string :category
t.text :notes
t.references :state
t.timestamps
end
end
end
I'm assuming that t.references data type sets it to int. The problem is my create method on my Acquisition class is trying to put a state abbreviation into the state_id field on the table acquisitions (and yes, it's called state_id on the database, even though it says :state in the migration script). The method doesn't fail, but it does put a 0 in the state_id field and the records go into the ether.
Though, I agree that this might be more trouble than it's worth considering the extra effort of working against the defaults elsewhere, just in case you actually want to do what you've asked:
Create states migration:
class CreateStatesTable < ActiveRecord::Migration
def change
create_table :states, id: false do |t|
t.string :state, limit: 2
t.string :name
t.index :state, unique: true
end
end
end
states model:
class State < ActiveRecord::Base
self.primary_key = :state
end
Note that before Rails 3.2, this was set_primary_key = :state instead of self.primary_key= see: http://guides.rubyonrails.org/3_2_release_notes.html#active-record-deprecations
if you find yourself here... leave as quickly as you can and go to:
Using Rails, how can I set my primary key to not be an integer-typed column?
In Rails 5.1 you can specify the type of the primary key at creation:
create_table :states, id: :string do |t|
# ...
end
From the documentation:
A Symbol can be used to specify the type of the generated primary key column.
I'm working on a project that uses UUIDs as primary keys, and honestly, I don't recommend it unless you're certain you absolutely need it. There are a ton of Rails plugins out there that will not work unmodified with a database that uses strings as primary keys.
Note that mkirk's answer creates a faux primary key. This explains why ActiveRecord needs to be told what the primary key is. Inspecting the table reveals
Table "public.acquisitions"
Column | Type | Modifiers
--------+----------------------+-----------
state | character varying(2) |
name | character varying |
Indexes:
"index_acquisitions_on_state" UNIQUE, btree (state)
In practice this works as expected so nothing wrong there, but it could be nicer.
We can keep the id column and change its type to string*. The migration looks like
class CreateAcquisitionsTable < ActiveRecord::Migration
def change
create_table :acquisitions do |t|
t.string :name
end
change_column :acquisitions, :id, :string, limit: 2
end
end
Inspecting the table reveals that you have an actual primary key with all the goodies such as the unique key constraint (no unique index needed), not null constraint, and auto-incrementing key.
Table "public.acquisitions"
Column | Type | Modifiers
--------+----------------------+---------------------------------------------------
id | character varying(2) | not null default nextval('acquisitions_id_seq'::regclass)
name | character varying |
Indexes:
"acquisitions_pkey" PRIMARY KEY, btree (id)
And you won't need to explicitly tell ActiveRecord what the primary is.
You'll want to consider setting a default id if none is provided.
class MyModel < ActiveRecord::Base
before_create do
self.id = SecureRandom.uuid unless self.id
end
end
* Disclaimer: you should not change the default primary key unless you have good reason to
You want to follow the Rails conventions. The extra primary key is not an issue in any way. Just use it.
I had a bit of experience with string used as primary keys and it's a pain in the ***. Remember that by default if you want to pass an object with the default :controller/:action/:id pattern, the :id will be a string and this will probably lead to routing problems if some ids get weirdly formatted ;)
class CreateAcquisitions < ActiveRecord::Migration
def self.up
create_table :acquisitions, :id => false do |t|
t.date :date
t.string :category
t.text :notes
t.references :state
t.timestamps
end
end
end
Rails works best when you don't fight against the defaults. What harm does it do to have an integer primary key on your state table?
Unless you're stuck with a legacy schema that you have no control over, I'd advise you to stick to the Rails defaults—convention over configuration, right?—and concentrate on the important parts of your app, such as the UI and the business logic.
There are two ways to write a polymorphic migration in Rails. Generally, I've done this:
class CreateFeatures < ActiveRecord::Migration
def change
create_table :features do |t|
t.integer :featureable_id
t.string :featurable_type
t.timestamps
end
end
end
However, we can also do this:
class CreateFeatures < ActiveRecord::Migration
def change
create_table :features do |t|
t.references :featureable, :polymorphic => true
t.timestamps
end
end
end
The two are, for all practical purposes, identical. My question: Is one better than another? Is one better for future maintainability?
This would likely be an issue only if one of two things changed:
The polymorphic abstraction version (Version #2) goes away or the syntax changes
The method of working a polymorphic relationship (using id and type) changes- unlikely
Just wondering if there's a preference, or if it's "Meh, doesn't really matter either way"
For an all rails app, where you are generating all tables via migrations, there is no difference functionally.
Here is the code for references:
def references(*args)
options = args.extract_options!
polymorphic = options.delete(:polymorphic)
args.each do |col|
#base.add_column(#table_name, "#{col}_id", :integer, options)
#base.add_column(#table_name, "#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
end
end
This is all well and good but if your foreign keys on the referenced table are not _id, method one is the only choice.
references just saves you one line of code...
Why can't I do something like this:
class CreateModels < ActiveRecord::Migration
def self.up
create_table :fruit do |t|
t.array :apples
end
end
end
Is there some other way to make an array ("apples) be an attribute of an instance of the Fruit class?
In Rails 4 and using PostgreSQL you can actually use an array type in the DB:
Migration:
class CreateSomething < ActiveRecord::Migration
def change
create_table :something do |t|
t.string :some_array, array: true, default: []
t.timestamps
end
end
end
Check out the Rails guide on associations (pay particular attention to has_many).
You can use any column type supported by your database (use t.column instead of t.type), although if portability across DBs is a concern, I believe it's recommended to stick to the types explicitly supported by activerecord.
It seems kind of funny for fruit to have_many apples, but maybe that is just an example? (I would expect apples to be a subclass of fruit).
You may use serialize. But if an Apple is going to be an AR object, use associations.