I am a beginner to rails framework. I have a fundamental question.
I am trying to define some models and their association referring the popular rails guides. My association looks like below.
class Person < ActiveRecord::Base
has_one :head
end
class Head < ActiveRecord::Base
belongs_to :person
end
Here, I need to add the foreign_key (Persons's primary key) in the table 'head'.
Now, if I need to get the 'head' of a 'person', rails need to scan through the head table and match the person_id.
The straight forward way I would think is to add the foreign key in 'person' table. Then I can directly refer the 'head' from 'person' with it's ID.
It appears that rails convention is not performance friendly. Am I missing something here?
When you create the migration to add the column containing the foreign key, it is highly recommended to add an index on this column. This way, the database will efficiently find the Head from the person_id (as efficiently then a search by its id).
add_index :heads, :person_id
If it's a one-to-one association, you can even add the unique option (unless your application accepts conjoined twins :-) ):
add_index :heads, :person_id, :unique => true
I suggest you to have a look to this 2 articles on where to use indexes:
http://tomafro.net/2009/08/using-indexes-in-rails-index-your-associations
http://tomafro.net/2009/08/using-indexes-in-rails-choosing-additional-indexes
Re: It appears that rails convention is not performance friendly. Am I missing something here?
Yes. Rails is designed for production sites. So it includes many performance features. As #Baldrick says, the answer to your specific concern is to always add an index for foreign key fields.
Adding an index for each foreign key field is needed, for performance reasons, for any SQL dbms application, no matter the language. Note that the index is added to the database, not to the MVC layers (Rails).
Rails itself includes additional performance features including sql results caching, optional fragment and page caching, and more.
Rails 3.2 includes the slow query features. These enable Rails to automatically show you the queries which are slow. You can then focus on fixing them as appropriate.
Related
Is there a way in Rails to manipulate database fields and corresponding accessor methods without the „nifty generators” ?
I want users, insofar they are privileged, to be able to manipulate the database structure, that is, at least, to add or delete columns. The privileged user should have the possibility to „Add new” some columns.
Say I have an Object/Table artist and it should “dynamically” receive columns as "date of birth", "has played with", "copies sold"
Not sure if it's a dup. It takes a preliminary decision whether Rails discourages from letting the user do this to begin with or or not. (if that's the case => certainly some noSQL solution)
In pure ruby at least it is easy to dynamically add an attribute to an existing Model/Class like this
Test.class_eval do; attr_accessor "new_attribute"; end
and
Test.new.new_attribute = 2
would return => 2 as expected
In order to create or manipulate a customized input mask / model: can I not manually go the same way the generators go and manually call ActiveRecord::Migration methods like add_column as well as create getter/setter-methods for ORM ?
If yes or no, in both cases, which are they to begin with?
Thanks!
I am not aware of any elegant way to allow an Object to dynamically create new columns. This would not be a good application design and would lead massive inefficiency in your database.
You can achieve a similar type of functionality you seek using ActiveRecord associations in Rails. Here's a simple example for your Artist model using a related Attributes table.
class Artist < ActiveRecord::Base
has_many :attributes
end
class Attribute < ActiveRecord::Base
belongs_to :artist
end
With this association, you can allow your Artist class to create/edit/destroy Attributes. ActiveRecord will use foreign keys in the database to keep track of the relationship between the two models.
If that doesn't work for you, your next best option is to look into NoSQL databases, such as MongoDB, which allow for much more flexibility in the schema. With NoSQL, you can facilitate the insertion of data without a predefined schema.
This is most likely a noob question since people use this gem and a lot of people love it, but I don't get the purpose. I'm looking at a project and its been used here many times in places such as t.references :foreign_key_table_name , :foreign_key => true, add_foreign_key :table :foreign_key_table_name, :options, and in a create t.foreign_key :foreign_key_table_name. Hope those weren't confusing since they're out of context.
But I don't get how this is different from what rails does built in with t.references :foreign_key_table_name or from me just adding t.integer :foreign_key_table_name_id? does it simply make it more readable by making clear that this is a 'foreign key'? I could just add a comment instead of a gem if thats the case... The only advantage I see is that you can move options such as :dependent into the migration instead of having it in the model, but who cares?
Some database engines support legit foreign key constraints: if someone tries to save a Child with a parent_id of 5, but there's no Parent with id 5, then the database itself (not Rails) will reject the record if there's a foreign key constraint linking children.parent_id and parents.id.
A foreign key can also specify what happens if the parent is deleted: in MySQL, for example, we can delete or nullify the dependent records, like how Rails does with :dependent, or even just straight-up reject the deletion and throw an error instead.
Since not all database engines offer this functionality, Rails offers to emulate it with :dependent, and it's nice to have it on the software level so that dependent child records can fire their destroy callbacks when the parent is deleted. Since the feature is engine-independent and therefore pretty much schema-independent, Rails doesn't handle the creation/deletion of foreign keys. That's where foreigner comes in: if your engine supports foreign key constraints, and you want that extra confident in your data integrity, foreigner can help with that.
Resurrecting an old question here, but…
Having rails enforce the relationship is fine, within rails itself.
However, if your project grows to have code that also accesses these tables from other languages, that will not have the benefit of rails enforcing the relations. These foreign key constraints are baked into the SQL tables themselves, so can protect non-rails code.
This will also protect you if you need to perform datafixes or otherwise manipulate your data via native SQL.
Another reason is that some documentation tools for SQL look at foreign keys on the DB, so it is cool to have a gem that generates them. Rails 4 added the ability to define foreign keys in the same migration that creates the table with:
t.references :something, foreign_key: true
And the generators will do this for you if you use the references type. Rails adds an index on something_id by default when using foreign_key like this
Using Rails 2.3.5.
In acts_as_audited, the schema definition defines an index:
add_index :audits, [:auditable_id,
:auditable_type], :name =>
'auditable_index'
It seems to me that the index should be:
add_index :audits, [:auditable_type,
:auditable_id], :name =>
'auditable_index'
In general, in a polymorphic association, we might sometimes want to search by the type only, but hardly ever search by the ID without the type?
Or is this a lazy way to allow a search by auditable_id when you are only using the plugin to audit one table?
Or is there another reason to do the indexing this way?
The answer just occurred to me.
Some databases don't support multi-field indexing,and those databases will index only the first field. If you go with my alternate indexing, then you'd get data clustered by class name, on which the database will have to do a sequential scan to find a particular ID. That's bound to be slower than searching for IDs and then checking the class name.
Another reason, I've found, is that SQL optimizers tend to not figure out to use an index when the first field in the index is not specific enough, apparently.
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.
I think there are a lot of places where my design may be screwing this up. I have very limited experience with Rails though. This is happening in Rails 2.3.2 with Postgres 8.3.
We've got two tables in our DB. One called "survey" and one called "survey_timepoint". A survey can have multiple time points so in the survey_timepoint table there is a column called "survey_id" with an fk constraint on it.
I also think I should mention that the tables were not created with a rails migration although they do follow the rails naming conventions. I suspect AR isn't anticipating a constraint on that column and it doesn't know how to handle the situation.
In my rails models I have:
has_many :survey_timepoint
and
belongs_to :survey
If I do something like:
s = Survey.new
s.survey_timepoint.push SurveyTimepoint.new
s.save!
I get:
ActiveRecord::StatementInvalid: PGError: ERROR: insert or update on table "survey_timepoints" violates foreign key constraint "survey_timepoints_fk"
DETAIL: Key (survey_id)=(59) is not present in table "surveys"
I'm assuming that if I delete that fk constraint on survey_timepoint.survey_id it'll work ok. It seems like I shouldn't have too though. Am I going to be stuck creating and saving each of the objects separately and wrapping the whole process in a transaction? It seems rather un-railsy. Apologies for any necessary information that I may have omitted.
You might want to check the SQL commands being sent. It looks like it is adding the survey_timepoint record before the survey record. Note that you are already dealing with two database changes — the survey and the survey_timepoint — so you should be using a transaction.
You can fix the immediate problem by doing s.save! before adding the timepoint (and then calling it again). My knowledge of Rails functionality is not deep enough to know if there is a more "railsy" way of doing this then wrapping it in a transaction.
I just experimented and found that this works with MySQL:
s = Survey.new()
s.survey_timepoints << SurveyTimepoint.new # Note "survey_timepoints" (plural)
s.save!
I think it would work equally well with PostgreSQL.
It does two inserts, first the Survey, then the timepoint, and wraps them in a transaction.
You can also do it all on one line:
Survey.create!({:name=>'New Survey', :survey_timepoints => [SurveyTimepoint.new]})
Incidentally, for ActiveRecord to work right you have to make sure of your singulars and plurals. (If you want to break the expected forms, you'll need to tell AR you're doing that -- a whole other topic.)
Your tables should be:
surveys
-------
# ...
survey_timepoints
-----------------
survey_id
# ...
And in your models you'd have:
class Survey < ActiveRecord::Base
has_many :survey_timepoints
# etc...
end
class SurveyTimepoint < ActiveRecord::Base
belongs_to :survey
end