Rails model to handle composite records - ruby

The first form of database normalisation is to hold potentially null fields in a second table, and join these in when they are referred to. The SQL would look something like this.
SELECT A.*, DA.* FROM ANIMALS A
INNER JOIN DOG_ATTRIBUTES DA ON DA.ANIMAL_ID = A.ID
This is pretty common for large databases to use, so only a dog would have the dog-specific attributes. Now I know this could be implemented as a has_one relationship, e.g.
class Dog < Animal
has_one :dog_attribute
end
If it was a read-only model, I could implement the above SQL as a view, and just refer to the DOGS view in my Dog model.
Is there a way I can treat the composite Dog object (with attributes from the ANIMALS table and the DOG_ATTRIBUTES table) as a single model, placing attributes of both into forms, saving them as one command and retrieving the attributes of both?

You're looking for Multiple-Table Inheritance (MTI). Rails by default only supports single-table inheritance, but you can install ActiveRecord::ActsAs for this capability.
Simulates multiple-table-inheritance (MTI) for ActiveRecord models. By
default, ActiveRecord only supports single-table inheritance (STI).
MTI gives you the benefits of STI but without having to place dozens
of empty fields into a single table.`

Related

Possible to use `one_to_many_through` associations in Sequel ORM?

I have a case where one model is related 2 other ones. I am trying to correctly setup the model relationships between these 3 models.
A simplified example... The first 2 tables are clients and invoices:
db.create_table(:clients) do
primary_key :id
String :name
end
db.create_table(:invoices) do
primary_key :id
String :description
Integer :balance
end
A third table, called files, contains records for files which can be related to either clients or invoices:
db.create_table(:files) do
primary_key :id
String :name
String :path
String :type # [image, pdf, word, excel]
end
There are 2 joiner tables to connect files to clients and invoices:
db.create_table(:clients_files) do
Integer :client_id
Integer :file_id
end
db.create_table(:files_invoices) do
Integer :invoice_id
Integer :file_id
end
The question is, how to correctly set up the relationships in the models, such that each client and invoice can have one or more related files?
I can accomplish this using many_to_many and has_many :through associations, however, this doesn't seem to be the right approach, because a given file can belong to only one customer or invoice, not to many.
I can also do this using polymorphism, but the documentation discourages this approach:
Sequel discourages the use of polymorphic associations, which is the
reason they are not supported by default. All polymorphic associations
can be made non-polymorphic by using additional tables and/or columns
instead of having a column containing the associated class name as a
string.
Polymorphic associations break referential integrity and are
significantly more complex than non-polymorphic associations, so their
use is not recommended unless you are stuck with an existing design
that uses them.
The more correct association would be one_to_many_through or many_to_one_through, but I can't find the right way to do this. Is there a vanilla Sequel way to achieve this, or is there a model plugin that provides this functionality?
With your current schema, you just want to use a many_to_many association to files:
Client.many_to_many :files
Invoice.many_to_many :files
To make sure each file can only have a single client/invoice, you can make file_id the primary key of clients_files and files_invoices (a plain unique constraint/index would also work). Then you can use one_through_one:
File.one_through_one :client
File.one_through_one :invoice
Note that this still allows a File to be associated to both a client and an invoice. If you want to prevent that, you need to change your schema. You could move the client_id and invoice_id foreign keys to the files table (or use a single join table with both keys), and have a check constraint that checks that only one of them is set.
Note that the main reason to avoid polymorphic keys (in addition to complexity), is that it allows the database to enforce referential integrity. With your current join tables, you aren't creating foreign keys, just integer fields, so you aren't enforcing referential integrity.

Find Model name from Table name (Sequel ORM)

In a ruby script I am running a loop in which I am dynamically getting a table name from a list of tables and have to perform some sort of CRUD operation (mainly insertion) on it.
I am using Sequel Orm and have created models for the various tables.
How do I find the name of the Model for each table so that I can perform the insertion?
tables=["table1","table2",'table3",...]
tables.each do |t|
#perform insertion on t
#how to find the model name for table t?
end
I can use a hash to store the model names for each table or can follow a pattern like converting the first character of each table to uppercase or something like that.
Is there a better way to do this?
What you are asking is not possible in the general case without a brute force search, and even then it is ambiguous, for the simple reason that the following is valid:
class Foo < Sequel::Model(:table1); end
class Bar < Sequel::Model(:table1); end
Basically, each model has a related dataset (usually just a simple SELECT * FROM table). However, other models can use the same or similar dataset. So going from model to table is simple, but table to model is not.
If you've created your own models, the easiest way to handle what you want is to use a hash:
ModelMap = {}
ModelMap["table1"] = Model1
ModelMap["table2"] = Model2
Then you can just do:
ModelMap[t]
inside that each block to get the model class.
One way to get the model name from the table name, as long as pluralization conventions have been followed between your models and tables, is something like:
table_name = :users
table_name.to_s.classify
However it doesn't look like that's what you're trying to do. You need to insert new or update existing reords. Since you already have the table name, you may wish to consider doing something like:
tables=["table1","table2",'table3",...]
tables.each do |t|
DB[t.to_sym].insert(...)
end
However you may wish to consider that Jeremy Evans, whose answer is above, is the creator of the Sequel gem and if he didn't recommend this as a solution then there may be a good reason.

Rails -- Single Table Inheritance -- conceptual question about model relationships

I wanted to know if it is possible to have STI where the subclasses each relate to an unrelated model in different ways.
In other words, say B < A and C < A, and I implement single table inheritance on table A (e.g. a type column where you can have B or C to indicate a subclass).
Now let's say we have a separate model called Xyzzy.
Is it possible to implement a has_and_belongs_to_many relationship between C and Xyzzy while implementing a has_one relationship between B and Xyzzy?
How should I do this in the context of STI? Or would STI simply not affect this?
There is nothing particular to do to make this happen. Simply create your relationships.
Indeed, even if B and C have access to some columns they don't care about, well... they simply don't care about.

Doctrine ORM: How to define a 1-n realtionship with a n-n connecting table

So, this is a bit complicated: I have two tables, say cats and dogs.
They are in a many-to-many relationship (could be called friendships or whatever), so that Doctrine automatically creates a table cats_dogs for me with the appropriate fields. (that is rowid, cat_id, dog_id per default.)
Now, imagine I have a third table, award, where I want to award one of these friendships. Here I therefore need a field that references one row in cats_dogs. However, since this table does not really exist between my models, (Doctrine handles it for me) what would be the most elegant solution for this?
In the end, I want in my award model two fields, a cat and a dog, who need to be in a friendship.
I am using the annotation driver.
What stops you from manually creating the m:n table instead of having doctrine do it for you?
The Doctrine aims is to map objects from an E/R schema and to make easier the access to object connections. Therefore I believe that the table cats_dogs automatically provided by Doctrine is necessary as it is. It is concise and hits its purposes, i.e. it provides a list of all dogs of a cat or, vice versa, all the cats of a dog.
Thus, I can conclude that it is preferable to create a third entity (besides Cat and Dog) named Award which provides a one-to-one relationship with Cat and another one-to-one relationship with Dog. Making it consistent with the cats_dogs table is only up to you, and is not a Doctrine task by default. E.g., you can use some cascade persist option.
I believe that this is the most effective solution with Doctrine.
As a final remark, consider that each table should map a specific relationship between one or more entities, and in fact the table cats_dogs represents the friendship relationships, while the table Award will represent the awarded relationship relationship between two friends.

use sequence object for multiple tables to ensure querability on join

I am using Oracle. I have two tables:
Cat (cat_ID, cat_name, cat_age, cat_strength)
Dog (dog_ID, dog_name, dog_age)
Sometimes I need to get all pets into one query result. I was thinking of creating an animal_seq sequence that can be used by both Cat and Dog tables so that they never have duplicate IDs across tables and then when joined can be easily searched/queried whatever.
Is this bad practice? If so, why? Are there better ways to design the tables (eg just one Animal table, or multiple inheritence). Personally, I try to avoid inheritence due to the performance issues of joins.
Having a single sequence is totally acceptable and safe. Oracle ensures multiple reads to a sequence always returns a unique value so you shouldn't have any problems with duplicate keys.
If the the schema between CAT and DOG is truly unique and an additional animal entity will also be unique, I would keep separate tables. If you are going to maintain the same information about cats, dogs, monkeys, etc., I would recommend putting them into a single ANIMAL table. You'll have to give more information about the application/database for us to know what to recommend.
With your current design what if you wanted to record birds or rabits or any other animal ?You would have to create tables for each type.
I would say use KISS(Keep it simply stupid) principle and have one table and join to another table called ANIMAL_TYPE( animal_type_id,Animal_type_name) that way you can make the sure ids are not duplicate and you can tend should you want to record other animal types.
I think you are looking for a base table, animal. Then you have two sub classes cat and dog. Such a design will help you when you add information such as "owner" of that animal, or "animal observation" or whatever the purpose of your application is.
table animal(
animal_id
,animal_type <-- Discriminator column with for example C for cat, D for dog
,name
,age
,primary key(animal_id)
)
table cat(
animal_id
,cat_strength
,primary key(animal_id)
,foreign key(animal_id) references animal(animal_id)
)
table dog(
animal_id
,dog specific attributes here
,primary key(animal_id)
,foreign key(animal_id) references animal(animal_id)
)
As you can see, I've moved up common attributes to the base table animal, while keeping the specific attributes for the subclasses in the sub class tables.

Resources