Where do indexes go when using STI? - ruby-on-rails

I am using Rails and postgres.
I have a couple of models using STI and i was wondering where i should put indexes on the tables and why?
For example lets say i have the following setup:
class Comment < AR; end
class MovieComment < Comment; end
class MagazineComment < Comment; end
# Fake Comment Table
id:integer
title:string
body:text
type:string
Thanks!

On the type field, if you want only one of MovieComment or MagazineComment. If you don't do that, you won't need the index here. I'm not sure if AR does use type every time, but just to make sure.
Because the id field is a primary key, an index should already be there.
If you want to query by both type and id make sure you have a combined index.
On the other fields: Depends what you query on, but I suppose you want to retrieve these only.

In general, you need indices in the columns that you will use when performing queries.
If you do MovieComment.find(10), the database will use the index in the id field that Rails will add automatically for you. Same if you do Comment.find(30): Rails will retrieve the commend with id 30 using the index, then it will read the type column and it will return a MovieComment or a MagazineComment.
If you are going to add a feature to search by title, for instance, you will have to create an index in this column as well. In this case, probably a :fulltext index.
An Index in the type column would make a query like MagazineComment.all faster because it is equivalent to Comment.where(type: 'MagazineComment').all, but it is probably not worth it.

Related

Deal with a column name 'hash' in a legacy database

I have to access a legacy database from within a Rails 5.2 project. Unfortunately I can not change any table column names and the table contains a column with the name hash which doesn't work with ActiveRecord (is will throw an error because of hash which is an existing method). I don't need that column but I can neither rename nor delete it either.
Is there a way to tell ActiveRecord to not use the hash field of a given table?
You can use the ignored_columns method that was added to Ruby on Rails in version 5.0 to ignore columns from the database. Quote from the docs:
ignored_columns=(columns)
Sets the columns names the model should ignore. Ignored columns won't have attribute accessors defined, and won't be referenced in SQL queries.
Just add the following to your model:
class MyModel < ApplicationRecord
self.ignored_columns = %w(hash)
end

Rails single table inheritance with lowercase type names

I'm working up an app that interfaces with a legacy database which has a type column used for single table inheritance. This database is still used by an existing PHP application so I have to work with what is there. I'm trying to set up some models for this and one of the key tables is set up with an STI scheme and a type column, however, all of the types in that column are entirely lowercase.
In my testing so far, rails works fine with the type column if I change the value to match the class name (for example, Claimant instead of claimant). However I don't want to go changing those values in the production database even though it would probably be ok, nor do I want to have to go in and modify the legacy app to save the names differently...
In order to fix this I have two questions...
1) Is there anyway I can configure the model to recognize that type = 'claimant' maps to class = 'Claimant'?
2) failing that, is there a way I can tell rails to not use STI on this table even though it has a type column?
I've done some googling and haven't come up with much yet...
I haven't tried this in an STI setting, but when using a legacy table I was able to use the method "set_table_name" on the ActiveRecord model.
class Claimant < ActiveRecord::Base
set_table_name 'claimant'
end
Or with a custom method:
def table_name
'claimant'
end
Apologies I haven't got an STI table handy to test this on, but thought it might help solve your problem.
In answer to the second part of your question, I believe you can disable Rails looking at the type column, by just specifying a non-existant column name.
class Claimant < ActiveRecord::Base
inheritance_column = :_type_column_disabled
end

Rails gem/plugin for dynamic custom fields in model

Is there any gem/plugin for ruby on rails which gives the ability to define custom fields in a model at runtime with no need to change the model itself for every different field.
I'm looking for something like Redmine acts_as_customizable plugin which is packaged as a gem usable in the rails way, i.e.
gem 'gemname'
rails g something
rails db:migrate
class Model < ActiveRecord::Base
acts_as_something
end
Here are the CustomField and the CustomValue classes used in Redmine.
Edit:
Since my question is not clear I add a brief use case which explains my need better:
I want users to be able to design their own forms, and collect data
submitted on those forms. An important decision is the design of how
these custom dynamic records are stored and accessed.
Taken from here, in this article approach the problem with different ideas, but they all have drawbacks. For this reason I'm asking if the issue has been approached in some gem with no need to rethink the whole problem.
I'm not aware of a gem that does this, but serialize works quite well and it's a built-in. You get a NoSQL-ish document store backed by JSON/YAML.
If you allow user to create a custom form, you can pass nested arrays et cetera directly into the attribute. However, if you need to validate the structure, you're on your own.
I'm afraid it could be tricky and complicated to do it in ActiveRecoand (generally in standard relational database). Take a look at http://mongoid.org/docs/documents/dynamic.html - this mechanism is using nosql feature.
You can also may try the following trick:
1/ Serialize a hash with your custom fields in the database column, for example { :foo => 'bar', :fiz => 'biz' }
2/ After load a record from database do some metaprogramming and define corresponding methods on the record's singleton class, for instance (assume that custom fields are stored and serialized in custom_fields column):
after_initialize :define_custom_methods
# ..or other the most convinient callback
def define_custom_methods
# this trick will open record's singleton class
singleton_class = (class << self; self; end)
# iterate through custom values and define dynamic methods
custom_fields.each_with_key do |key, value|
singleton_class.send(:define_method, key) do
value
end
end
end
Since rails 3.2 you can use store method. Just include following in your model:
store :properties, accessors: [:property1, :property2, :property3...]
You only need to change your model once (to add properties field to db table). You can add more properties later without altering the schema.
The way this works is by serializing properties hash into YAML and saving it into database. It it suitable for most cases, but not if you'd like to use these values in db queries later.
I don't know a gem, but this can be accomplished be creating a table called custom_fields with a name column and possibly a datatype column if you wanted to restrict fields by datatype.
Then you create a join table for a custom field to your desired table and a value and do whatever validations you want.

Rails Single Table Inheritance without "type" column

I'm porting some functionality to Rails, and I'm working with an existing table which is for comments.
Basically, there are two types of comments - profile comments (photo_id column is null) and photo comments (photo_id column is set to photo's ID)
I got single table inheritance working just fine by adding a type field to the table, but I'm wondering if there's a way to get my single table inheritance working without the type field. According to the Rails API documentation, "If you don‘t have a type column defined in your table, single-table inheritance won‘t be triggered. In that case, it‘ll work just like normal subclasses with no special magic for differentiating between them or reloading the right type with find."
I'm wondering if there's a way that I can customize my models to determine type based on photo_id being nil or having an integer value, rather than using the database column (which I'd rather not add if I don't have to.) Any ideas?
If comments models doesn't differ much, I wouldn't bother with single table inheritance at all. Just add:
# to Comment model
belongs_to :photo
belongs_to :profile
# to Profile model
has_many :comments
# to Photo model
has_many :comments
Then:
#photo.comments # will return comments associated with photos
#profile.comments # will return comments associated with profiles
There may be problem if you had both photo_id and profile_id set (I suppose it may happen when you comment a photo that is associated with profile), so you can change in Profile model:
has_many :comments, :conditions => "photo_id is not null"
Another approach (I think better) it to you polymorphic associations but you will need to modify you sql tables.
I suspect you cannot do this trivially. However, one possibility is to trick active record into using a view rather than a table, and write some database functions to set this magic attribute based on which id is set.
However, in the end, I suspect it would be far, far easier to just add the column.

Rails: define column names to model

I have an existing database(Postgresql). How can i create models from it? How can i pass column names for Rails? As if something like this:
Person:
Name :table_name_for_name_attribute
Surname :table_name_for_surname_attribute
PersonalCode :table_name_for_perconal_code_attribute
Unfortunately, my database is not following Rails convention, because not all tables are named in English. Any ideas how can i map it?
UPDATE reverse_scaffold script generates only model, but i need controller and view forms also.
You can define a table name not matching the model's using the table_name method.
class MyModel < ActiveRecord::Base
def self.table_name
'my_table_name'
end
end
Change the value of 'my_table_name' to your effective table name.
For generating controllers and views with automatic methods to create, update, delete and view database objects, you should create a scaffold. There's some pretty good documentation on the rails guides about that.
In your model, you'll need to tell ActiveRecord what the table name and primary key field column is named if they don't follow the conventions.
class MyLegacyTable < ActiveRecord::Base
self.table_name = "some_name"
self.primary_key = "some_name_primary_key_field"
end
Once you've done that, ActiveRecord knows the some_name_primary_key_field as id, which makes life much easier.
Rails doesn't need to know the column names it figures them out when it connects to the database.
As others have said you can specify a table name in the model.
As for generating controllers/views, you're pretty much own your own. script/generate scaffold is deprecated. It still works as far as creating things, but you need to pass all column names and times on the command line.
Instead have a look at the ActiveScaffold Plugin, it has a similar end result. But is much more robust and easier to adapt to model changes.

Resources