Using existing tables and its relations in rails 3.2 application? - ruby-on-rails

I have an existing application with lot of data and about 20 tables, how can I use them directly.
My database.yml file points to the MysQL database. Something like magic model generator.

you can do the following
connection = ActiveRecord::Base.connection()
results = connection.execute("#{your_sql_query_here}")
results.each do |row|
puts row[0]
end
However I'd recommend you to associate them in a more coherent fashion.

You would create a model for each one of them which is not much when you only have about 20.
In the model you would associate the table to them using the
set_table_name :name_of_your_table
Keep in mind the model name just have to be relavent enough to the table, because of the explicit set_table_name method they do not have to follow strict convention.
And to set relationships you would use the class_name just like this
has_many :fruits, :class_name => "CrazyFruit"
It might sound tedious but its the right way

Related

ActiveRecord: Read Only Model based on Query

I have worked with Postgres views in the past to model derived representation of data.
Overwriting ActiveRecord's readonly? helps with implementing that.
Unfortunately when used to heavily views have the downside of making schema changes very hard.
So I wonder if there is a way to assign an arbitrary query to a model and treat it like a table?
EDIT: Sorry I should have been more clear:
What I'd like is to do something like "Give me all users left joined with their comments" through a model called "UserWithComments". I know that this particular example could easily be achieved by using ActiveRecords DSL via User.includes(:comments) or similar.
If the query gets more complex the ActiveRecord DSL hits its limits so I could just create a view with my SQL and then use a readonly model that references the view. There are circumstances where creating a view is not feasible so I was wondering way to have a model called UserWithComments which is not linked to a table or view but instead has some kind of config option with an SQL like select <attributes> from users u left join comments c on c.user_id = u.id
Your question is a bit vague but I have used views this way:
Create your view in the DB. I am using an example called gear_alerts. Then in Rails create a model called gear_alert.rb:
class GearAlert < ApplicationRecord
self.primary_key = :id
belongs_to :category
belongs_to :user
has_many :user_tags, through: :user
scope :active, -> { where(active: true) }
scope :deactivated, -> {where(active: false) }
def readonly?
true
end
end
as you can see, since views present themselves as tables normal ActiveRecord stores, you can treat it as such in a read-only context. I like views for when I am using complex table joins that have to be manipulated heavily before going to the presentation layer. Leaving it all for the browser to render can really slow things down. Maintaining views can be a pain. So I highly recommend the Scenic gem https://github.com/scenic-views/scenic as it lets you easily modify views from the rails migration tool while also giving you history of how the view has been modified over time.

Rails: Creating models from existing tables?

I have tables already created from a different project. Their names are formatted like aaa_bbb_ccc_ddd (all non plural and some parts aren't a convention word). I have successfully created a schema from the database by reading this. But now I have to make the actual models. I've looked at RMRE, but they enforce the ActiveRecord convention on my tables and change their names, which I don't want to do because other apps depend on those tables.
What is the best way to automatically create models and a schema from existing tables?
just a theory, not sure how this would work in real app:
create models named as ActiveRecord convention requires, for example for table aaa_bbb_ccc_ddd you'll create a model AaaBbb and map this model to your table:
class AaaBbb < ActiveRecord::Base
self.table_name = "aaa_bbb_ccc_ddd"
end
or a more human example:
class AdminUser < ActiveRecord::Base
self.table_name = "my_wonderfull_admin_users"
end
Now you'll have AaaBbb as resource in routes meaning you'll have a url like:
.../aaa_bbb/...
and if you want to use the table name name in url I guess you could rewrite the route:
get 'aaa_bbb_ccc_ddd/:id', "aaa_bbb#show", as: "aaa_bbb"
again, just a theory that might help you out. I haven't worked with such cases yet but would've start from this.
edit
to automate model creation from database:
https://github.com/bosko/rmre
but I think this will create models by rails convention with wierd names that you'll have to use as resource in your app.
A good template that I found on SO in case you want to use a model name different from table name:
class YourIdealModelName < ActiveRecord::Base
self.table_name = 'actual_table_name'
self.primary_key = 'ID'
belongs_to :other_ideal_model,
:foreign_key => 'foreign_key_on_other_table'
has_many :some_other_ideal_models,
:foreign_key => 'foreign_key_on_this_table',
:primary_key => 'primary_key_on_other_table'
end
Just switch from Rails to Django and make
your life happier and also make your work normal:
$ python manage.py inspectdb my_table_without_existing_model > some_new_model.py
That's enough. Two seconds of work :)

Model Inheritance in Ruby on Rails 3

Please help a newbie to choose the best way to implement inheritance in RoR3. I have:
-Person (address fields, birthdate, etc.)
-Player, inherits from Person (position, shoe_size, etc.)
-Goalkeeper, inherits from Player (other specific fields related to this role)
I think that Single Table Inheritance is a bad solution, because there will be a lot of null fields in the table created. What is the best way to do this? Use polymorphic associations (with has_one?)? Use belongs_to/has_one (but then how to show in the Player views the fields of Person too?)? Don't implement inheritance? Other solutions?
While I think STI is probably the approach I would use for this, one other possibility, if you want to avoid a lot of NULL attributes, is to add a column other_attributes to your Person model that will store a Hash of attributes. To do this, add a text column to the people table:
def self.up
add_column :people, :other_attributes, :text
end
Then make sure the attribute is serialized in the model. And you may want to write a wrapper to make sure it's initialized as an empty Hash when you use it:
class Person < ActiveRecord::Base
serialize :other_attributes
...
def other_attributes
write_attribute(:other_attributes, {}) unless read_attribute(:other_attributes)
read_attribute(:other_attributes)
end
end
Then you can use the attribute as follows:
p = Person.new(...)
p.other_attributes #=> {}
pl = Player.new(...)
pl.other_attributes["position"] = "forward"
pl.other_attributes #=> {"position" => "forward"}
One caveat with this approach is that you should use strings as keys when retrieving data from other_attributes, as the keys will always be strings when the Hash is retrieved from the database.
I suggest STI. An alternative solution is to use a document store like mongodb, or use the activerecord store http://api.rubyonrails.org/classes/ActiveRecord/Store.html. If you have a postgress database look at his HStore column http://rubygems.org/gems/activerecord-postgres-hstore.
Another option is PostgreSQL table inheritance. http://www.postgresql.org/docs/8.1/static/ddl-inherit.html

how to force a has_many to use a specific :foreign_key?

I'm having trouble with a model not honoring the :foreign_key policy.
Character model has the following fields:
name:string
level:int
realm:string
realm_id:integer
class Character < ActiveRecord::Base
belongs_to :realm
end
My Realms model looks like this:
class Realm < ActiveRecord::Base
has_many :characters, :foreign_key => "realm_id"
end
However, it seems like it's forcing the character model to use the :realm column as the foreign_key rather than :realm_id. I don't have any clue as to why or how to fix it. Is there any other way to make it ignore the :realm field and go for the :realm_id without having to change the name of the column?
[Edit for clarity]
The character model does have a realm_id:integer field. I have tried not having the foreign_key but the results with both is identical.
ruby-1.9.2-p136 :012 > c = Character.new
=> #
ruby-1.9.2-p136 :013 > c.realm = "Sargeras"
ActiveRecord::AssociationTypeMismatch: Realm(#2154038240) expected, got String(#2151988680)
Despite even having the foreign_key, it just refuses to let go of the realm column.
[Edit 2]
The realm column will just take over due to the has_many and belongs_to association. There is no way so far to break this, so the solution is to either remove the column (the approach i will take), or rename it to something different.
Did you make sure that your Character table has a realm_id column? Please make sure of that, and then get rid of foreign_key => 'realm_id, it is not necessary at all. Your program should work if you get both these things done.
You should not need the :foreign_key part here at all, since you're following the standard Rails naming convention, the realm_id column should be inferred from the model name.
EDIT
I see. I don't think you can have a column and an association by the same name in one model. The easiest solution would probably be to rename the "realm" column to "realmname" or something like that.
belongs_to :realm creates (among other things) methods called realm and realm= used as getters and setters. That means the method_missing magic that ActiveRecord uses to expose database columns as attributes is never triggered when you do Character#realm, as that method isn't in fact missing.
If you want to avoid renaming the realm column in your database, you could create attribute accessors for the field manually under another name:
class Character < ActiveRecord::Base
def realm_name
self['realm']
end
def realm_name=(value)
self['realm'] = value
end
end
This way you'll still have the realm column in your database and be able to access the attribute in Ruby, albeit under a different name. This isn't a great idea, though, as you'll be duplicating the realm name as both Character#realm_name and Character.realm.name.
I would ditch the realm column and instead make sure I use Realm objects when importing from the datasource:
character.realm = Realm.find_by_name('Sargeras')
That way you'd only realm data available where it makes sense; in the Realm model.

Eager loading of polymorphic associations in ActiveRecord

This is my first time using Rails and I was wondering if it's possible to load a has one polymorphic association in one SQL query? The models and associations between them are basic enough: An Asset model/table can refer to a content (either an Image, Text, or Audio) through a polymorphic association, i.e.
class Asset < ActiveRecord::Base
:belongs_to :content, :polymorphic => true
end
and the Image, Text, Audio are defined like this:
class Image < ActiveRecord::Base
:has_one :asset, :as => :content
end
When I try to load an Image, say like this:
Image.first(
:conditions => {:id => id},
:include => :asset
)
It specifies two queries, one to retrieve the Image and another to retrieve the Asset (FYI, this happens also if I specify a :joins). Based on my understanding, ActiveRecord does this because it doesn't know there's a one-to-one association between Image and Asset. Is there a way to force a join and retrieve the 2 objects in one go? I've also tried using join with a custom select, but I end up having to create the ActiveRecord models manually and their associations.
Does ActiveRecord provide a way to do this?
After digging through the Rails source I've discovered that you can force a join by referencing a table other than the current model in either the select, conditions or order clauses.
So, if I specify an order on the Asset table:
Image.first(
:conditions => {:id => id},
:include => :asset,
:order => "asset.id"
)
The resulting SQL will use a left outer join and everything in one statement. I would have preferred an inner join, but I guess this will do for now.
I ran up against this issue myself. ActiveRecord leans more toward the end of making it easy for Rubyists (who may not even be all too familiar with SQL) to interface with the database, than it does with optimized database calls. You might have to interact with the database at a lower level (e.g. DBI) to improve your performance. Using ActiveRecord will definitely affect how you design your schema.
The desire for SQL efficiency got me thinking about using other ORMs. I haven't found one to suit my needs. Even those that move more toward transact SQL itself (e.g. Sequel) have a heavy Ruby API. I would be content without a Ruby-like API and just manually writing my T-SQL. The real benefit I'm after with an ORM is the M, mapping the result set (of one or more tables) into the objects.
(This is for Rails 3 syntax.)
MetaWhere is an awesome gem for making complex queries that are outside of ActiveRecord's usual domain easy and ruby-like. (#wayne: It supports outer joins as well.)
https://github.com/ernie/meta_where
Polymorphic joins are a little trickier. Here's how I did mine with MetaWhere:
Image.joins(:asset.type(AssetModel)).includes(:asset)
In fact, I made a convenience method in my polymorphic-association-having class:
def self.join_to(type)
joins(:asset.type(type)).includes(:asset)
end
So it will always query & eager load a particular type of asset. I haven't tried mixing it with multiple types of join in one query. Because polymorphism works using the same foreign key to reference different tables, on the SQL level you have to specify it as separate joins because one join only joins to one table.
This only works with ActiveRecord 3 because you have access to the amazing power of AREL.

Resources