Single Table Inheritance using ActiveRecord. Since we can use #test = Employee.all and find all the employees created. How does rails do this? Since we only use a User Table. How does it know about employees and retrieve only employees? Rails Magic? Explanation anyone? Thank you in advance.
Base Class : Person (inherits ActiveRecord)
Sub-Class: Employee, Supervisor, Manager (each inherit Person)
So my Person table needs to have a _type and _id field to make the table polymorphic.
My next question is how do I get Employee Associated to the Person table and when you save an employee, how do you get it to actually put in Employee in the person_type field?
To indicate to Ruby on Rails that the
users table needs to support Single
Table Inheritance you need to add a
column named ‘type’ to the users
table. Here is my users table
definition:
CREATE TABLE users ( id INT NOT
NULL AUTO_INCREMENT, user
VARCHAR(15) NOT NULL UNIQUE, pass
VARCHAR(40) NOT NULL, type
VARCHAR(20) NOT NULL, PRIMARY KEY (id) );
In the column named type you
should store the name of the class,
the class type, that should be used
for each user. To mark an certain user
as an admin set his type to
‘Administrator’. By setting a user’s
type to ‘Administrator’ you are giving
him full administrator privileges as
defined in your Administrator model
class.
http://juixe.com/techknow/index.php/2006/06/03/rails-single-table-inheritance/
Single table inheritance uses a type column on the table to indicate the type of the object. ActiveRecord knows that your Employee class is using single table inheritance (it has no matching table and the users/people table has a type column).
So when you ask for Employee.all it knows to looks for all entries in the users/people table where type == 'Employee'.
If you look at the logs the SQL will be displayed for these queries and you'll see the 'magic' happening.
Related
I have one table called countries which has a field i.e ID with primary key and the other table is customers which has a field called primary_country. I want to join both the tables in cakephp way, but always it is taking customers.country_id which is not present in the table. I am using primary_country because the table also has alternate_country field. so i can't use country_id. Please provide me any solution so that I can fetch the country names on the basis of primary_country and alternate_country.
You'll need to modify your table object with the foreign key, as mentionned here:
https://book.cakephp.org/3.0/en/orm/associations.html
It could be something like that, in your case :
class CustomersTable extends Table
{
public function initialize(array $config)
{
$this->belongsTo('Countries')
->setForeignKey('primary_country');
}
}
But i must say that if your model is still under conception, you may want to make an third table customer_countries and make an 'hasMany' relation between 'customers' and 'countries'. Limiting records for one customer to only two countries, and ordering them between 'primary' and 'alternate' is part of you business logic.
I am having a domain class arHistory as follows:
package ars
import ars.AccessRequest
import gra.Users
class ArHistory {
Long id =2340
Users updatedby
Date updatedon
String requeststatus
static hasMany=[accessrequests:AccessRequest]
static constraints = {
requeststatus(blank:false, nullable:false)
}
Now after I run the application the GORM create tables ar_history and ar_history_access_request (the join table for one to many relationship)
The join table above has only 2 foreign keys, the table itself has no primary key id
I wanted to know 3 things,
1) do I need to have a primary key id for the join table
2) if yes how do I create the id (do I create it manually through mysql)
3) whats the advantage of having hasMany() instead of having a class variable AccessRequest defined in ArHistory, is it just normalised data?
Regards
Priyank
You don't need primary key in join table, since you don't allow duplicates with hasMany. Read the documentation of hasMany: "Grails will automatically inject a property of type java.util.Set into the domain class based on the hasMany setting". Thus, primary key is not needed.
What do you mean by third question? hasMany allows you to add many AccessRequest objects to a collection.
I have the following nested model relationship:
Countries (id, name)
Provinces (id, country_id, name)
Cities (id, province_id, name)
I have validates_uniqueness_of constraint on the name fields for each model in the relationship and a unique index on the name columns in the database.
I want to swap a new object created with the same name as an existing record at some point before it's validated. In other words, if a user attempts to add a city, province, country combination that has already been added, I want to country model to return a reference to the corresponding existing model records instead of failing validation before save.
I'm having trouble using the model callbacks (after_initialize, before_validation, etc.) and I wasn't able to get Country.find_or_initialize_by_name to work with the nested models... any suggestions?
What you are trying to do sounds pretty hard and will probably require you to know a lot of the internal implementation details of ActiveRecord::Base.
Instead, could you do something like this?
#country = Country.find_or_initialize_by_name(params[:name])
...
#country.save
EDIT:
ActiveRecord has find_or_create_by_XXX and find_or_initialize_by_XXX functions built in, so there is no need to add a function to the model. For more info see the "Dynamic attribute-based finders" section of http://api.rubyonrails.org/classes/ActiveRecord/Base.html
I have a user table, and a teacher that I newly created. The teacher is sub class of user, so, I use scaffold generator to generate the teacher table, than, I modify the model to do teacher is subclass of user. After all that, I did a db:migrate. Then, I go to
http://localhost:3000/teachers/new
It shows an error:
undefined method `teacherSalary' for #<Teacher:0x103331900>
So, my question is what did I do wrong? I want to create a page for doing user register, the user can ONLY be a teacher / student. But I can't add a teacher record ... ... Moreover, I go to
http://localhost:3000/users/new
I want to have a combo box that allow user register their user to be a "teacher" or a "student". But everything seems not work like I expected. What I need to do? Thank you very very much for your help.
Within your database you should have a single table called users. This table should have a string column which by default is called type. If you use another name for this column then you will have to set the inheritance column name manually using self.inheritance_column = "column_name"
Within your application you have three models, User, Student and Teacher. User inherits from ActiveRecord::Base as usual, Student and Teacher both inherit from User.
You should then be able to instantiate new Teacher and Student objects. Internally this works by writing the model name to the type field on the user tables and then when you use Student.find it adds a clause to the SQL to only return rows where the type = 'Student'
You can add shared behaviour to the User class, e.g. validations etc then add additional behaviour to the inherited classes.
A fuller description of how STI works can be found in Martin Fowlers Book(Patterns of Enterprise Application Architecture).
I found this definition really handy:
STI means one table contains the data of more than one model, usually differentiated by the "type" column. ("users" table contains data for the models "Teacher", ""Pupil", "Employee", "Assistant", etc.)
Keeps similar models in the same table instead of creating new ones.
A Polymorphic Association means that one model can be associated with more than one other model(Comment can belong to post, image, file, user_type...)
To prevent foreign key conflicts, the association is reperesented with the *_id and *_type columns instead of only *_id.
For what you have here , I am not sure if STI is the best way go . STI should generally be used when there is a OO like inheritance and the Models have the same Attribute but different behaviour . In your case Teacher and Student can sure have a few shared attributed , but they are also bound to have different ones as well .
You might want to experiment with a polymorphic association as well .
I have these two domains Car and Driver which have many-to-many relationship. This association is defined in table tblCarsDrivers which has, not surprisingly, primary keys of both the tables BUT additionally also has another boolean field deleted. Herein lies the problem. When I find/get query on domain Car, I am fetched all related drivers irrespective of their deleted status in tblCarsDrivers, which is expected.
I need to put a clause/constraint to
exclude the deleted associations from the
list of fetched records.
PS: I tried using an association domain CarDriver in joinTable name but that seems not to work. Apparently it expects only table names, not maps.
PPS: I know its unnatural to have any other fields besides the mapping keys in mapping table but this is how I got it and it cant be changed.
Car domain is defined as such -
class Car {
Integer id
String name
static hasMany = [drivers:Driver]
static mapping = {
table 'tblCars'
version false
drivers joinTable:[name: 'tblCarsDrivers',column:'driverid',key:'carid']
}
}
Thanks!
I know its unnatural to have any other
fields besides the mapping keys in
mapping table but this is how I got it
and it cant be changed.
This is not at all unusual. If you want to store properties about the relationship, this is the obvious solution. You should reinstate your association domain CarDriver which has a deleted property in addition to a relationship to Car and Driver, and you should then be able to write a query which excludes the deleted drivers.
A comprehensive example of how to define such a mapping is provided here.