Apparently, include and select can't be used simultaneously on a Rails find query, and this has been repeatedly marked as wontfix:
http://dev.rubyonrails.org/ticket/7147
http://dev.rubyonrails.org/ticket/5371
This strikes me as very inconvenient, because the times I'd want to use include are exactly the same times I'd want to use select - when every bit of performance counts.
Is there any way to work around this and manually generate a combined include-with-select using find_by_sql, or any other method? The trouble is, I'm not aware of any way to emulate the functionality of include, where it instantiates models in memory to hold the included associated models, such that I can enter model1.associated_models and have it not hit the database again.
Have you considered creating model for database view? For example:
Create database view, with your complicated SQL query:
CREATE VIEW production_plan_items AS
SELECT * FROM [...]
INNER JOIN [...];
Create model for this view:
# app/view_model.rb
class ViewModel < ActiveRecord::Base
self.abstract_class = true
def readonly?
true
end
def before_destroy
raise ActiveRecord::ReadOnlyRecord
end
end
# app/models/logical/production_plan_item.rb
module Logical
class ProductionPlanItem < ::ViewModel
end
end
Use as always, but remember that these records are READ ONLY!
Logical::ProductionPlanItem.where( ... )
If performance still be an issue in the future, you can quite easily convert DB views to materialized views using triggers and stored procedures. This will give your application enormous speed boost, and you don't have to change even one line of Rails code.
Enterprise Rails is highly recommended reading:
http://www.amazon.com/Enterprise-Rails-Dan-Chak/dp/0596515200/ref=sr_1_1?ie=UTF8&qid=1293140116&sr=8-1
Related
I'm trying to delete all the organizations that no longer have any users.
Using the below code, I can find all the records I wish to delete:
Organization.includes(:users)
.where(users: { id: nil })
.references(:users)
When I add delete_all, I get the same error I would get if I didn't include references:
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "users"
I could probably write the solution in pure SQL, but I don't understand why Rails isn't keeping the reference to users when I add the delete_all statement.
Here are some more details:
Organization:
has_many :users
User:
belongs_to :organization
I've found the includes useful only for eager loading (and it can rarely handle my cases), and when coupled with references it generates something completely insane (aliasing every single field with something like tN_rM) even though it actually does a LEFT OUTER JOIN... Which could help if it didn't vanish once delete_all appears!
I've found that it's much clearer and simpler just to use exists. It's Arel (and there's no point in avoiding it, its under the hood of ActiveRecord anyway), but it's such a tiny portion that it's barely noticeable:
Organization.where(
User.where('users.organization_id = organizations.id').exists.not
)
Or, if this string of SQL doesn't look nice to you, use a bit more Arel, so it gets noticeable:
Organization.where(
User.where(organization_id: Organization.arel_table[:id]).exists.not
) # I tend to extract these ^^^^^^^^^^^^^^^^^^^^^^^ into local variables
That handles chaining .delete_all on top just fine, since it's not (syntactically) a join, even though it's effectively equivalent to one.
The magic behind this
SQL has an EXISTS operator that is similar in functionality to a join, except for inability of selecting fields from a joined table. It forms a valid boolean expression which can be negated and thrown into WHERE-conditions.
In the "SQL-free" form I'm using an expression "column of a table", which turns out to be usable in Rails' hash-conditions. It's an accidental discovery, one of the few uses of Arel that does not make code too bulky.
I'm not sure how you plan to implement this in the MVC framework, but it seems clean to do the organization purge via model action. Whenever a user is deleted, check to see of the organization has any remaining members.
in the User.rb
class User < ActiveRecord::Base
before_destroy :close_user
...
def user_organization
Organization.where(user_id: id)
end
private
def close_user
unless user_organization.users.any?
user_organization.destroy
end
end
end
Added To apply callback delete solution to users being member of many organizations
If the user has multiple organizations
class User < ActiveRecord::Base
before_destroy :close_user
...
def user_organizations
Organization.where(user_id: id)
end
private
def close_user
user_organization.find_each do |organization|
unless organization.users.any?
organization.destroy
end
end
end
Caveat: this is not tested, didn't fail syntax. I don't have the data to test it fully but I think it will work. But it means running this action after every user delete, which is a system architecture decision. If it's an option, it might be worth a try.
I'm looking for a way to add a *_updated_at columns for several columns in a record.
It would work similarly to normal Rails timestamps, ie based on convention. If there's a DB column called author_updated_at, the class would automagically update it whenever author attribute changes.
Not too hard to write, but thought I'd ask here in case anyone has done it before or there's a gem around. I'd also be interested to know if there are any performance issues with this approach, though I think it should be negligible if using before_save. There's no extra reads or writes needed.
You might want to try paper_trail - it tracks changes to a model but in a slightly different fashion - instead of adding extra columns for each attribute to track it uses a table to store versions of the model. This gives you a full blown versioning system without doing much work.
You can easily revert to previous versions and track who created a revision etc.
But if all you need is a light-weight solution to store a timestamp for the changes to a limited set of attributes you can use model callbacks to set the *_updated_at columns.
module AttributeTimestamps
extend ActiveSupport::Concern
included do
after_validation(on: :update) do
# Use ActiveModel::Dirty to get the changed attributes
self.previous_changes.each_with_index do |attr|
setter = "#{attr}_updated_at="
self.call(setter, self.updated_at) if self.respond_to? setter
end
end
end
end
class MyModel < ActiveRecord::Base
include AttributeTimestamps
end
I solved with a different version of #max solution.
previous_changes wasn't working for me. I'm using Rails 4.2.
module AttributeTimestampable
extend ActiveSupport::Concern
included do
after_save do
self.changed.each do |attr|
timestamp = "#{attr}_updated_at"
update_column(timestamp, updated_at) if self.respond_to? timestamp
end
end
end
end
I have a relational database table that holds a product lookup. This table powers multiple systems, only one of which is a Rails app. In the Rails app, I want to use the product lookup as an ActiveRecord class with instance members with the product code - for example, the key code field is a 4-digit alphanumeric. It would be nice to be able to refer to instances by the code like this: ProductCode.01A3. I don't want to simply declare them in the Rails code, of course, because the DB is the system of record for multiple systems. Also, how would Ruby react to a non-existent product code? If ProductCode.ABCD doesn't exist, does it just silently return a nil, and I'd need nil checks everywhere? And then there's the issue of releasing a new ProductCode into production. Updating the table would require reloading the class instance variables.
Thoughts? Can this be done? Should this be done? I've searched for a library but maybe my Google-fu isn't that good.
Lookup tables are a great tool to reduce the number of database queries and I often use them in long worker processes. I wouldn't recommend using them in a webapp unless the data is often accessed rarely changes and is expensive to get.
My implementation for you problem would look like this:
class ProductCode
#product_codes = {}
class << self
attr_accessor :product_codes
end
def self.get(code)
#product_codes[code.to_s]
end
def self.cache_all
# whatever you do here
#product_codes = {'01A3' => 42}
end
end
ProductCodes.cache_all
ProductCodes.get('01A3')
my sql DB contains tables "jobs" and "job_categories."
"job_categories" associates job category strings (i.e. "Software Development") with an integer number (i.e. 7).
I need these associations saved into variables in my job controller for various query functions. How can I use rails to dynamically link changes to the job_categories table to variables in my jobs controller? I've worked with RoR for a few weeks now but am still a little fuzzy on how everything interacts. Thank you!
There's one big gotcha with what you're trying to do, but first I'll answer your question as asked.
Create class-level accessors in your JobsController, then write an Observer on the JobCategory class that makes the appropriate changes to the JobsController after save and destroy events.
class JobsController < ActionController::Base
##categories = JobCategory.find(:all)
cattr_accessor :categories
# ...
end
class JobCategoryObserver < ActiveRecord::Observer
def after_save(category)
JobsController.categories[category.name] = category.id
end
def after_destroy(category)
JobsController.categories.delete(category.name)
end
end
You'll need additional logic that removes the old name if you allow for name changes. The methods in ActiveRecord::Dirty will help with that.
So, the gotcha. The problem with an approach like this is that typically you have more than one process serving requests. You can make a change to the job_categories table, but that change only is updated in one process. The others are now stale.
Your job_categories table is likely to be small. If it's accessed with any frequency, it'll be cached in memory, either by the OS or the database server. If you query it enough, the results of that query may even be cached by the database. If you aren't querying it very often, then you shouldn't be bothering with trying to cache inside JobsController anyway.
If you absolutely must cache in memory, you're better off going with memcached. Then you get a single cache that all your Rails processes work against and no stale data.
I'm getting some objects from an external library and I need to store those objects in a database. Is there a way to create the tables and relationships starting from the objects, or I have to dig into them and create migrations and models by hand?
Thanks!
Roberto
Even if you could dynamically create tables on the fly like that (not saying that you can). I wouldn't want to do that. There is so much potential for error there.
I would create the migrations by hand and have the tables and fields pre-created and fill them in with rows as needed.
Note: This is a TERRIBLE hack and you'll be ironing out the bugs for years to come, but it is however pretty easy:
This relies on rails ActiveSupport, and ActiveRecord already being loaded
Say you get a random object from a third party library which has 2 instance variables - it's class might look like this:
class Animal
attr_accessor :name, :number_of_legs
end
a = SomeThirdPartyLibrary.get_animal
You can use reflection to figure out it's name, and columns:
table_name = a.class.to_s.tableize
column_names = a.instance_variables.map{ |n| n[1..-1] } # remove the #
column_types = a.instance_variables.map{ |n| a.instance_variable_get(n).class
}.map{ |c| sql_type_for_class(c) } # go write sql_type_for_class please
Then you can use ActiveRecord migrations to create your table, like this:
ActiveRecord::Migration.class_eval do
create_table table_name do |t|
column_names.zip(column_types).each do |colname, coltype|
t.column colname, coltype
end
end
end
Then you can finally declare an activerecord class which will then interface with the just-created table.
# Note we declare a module so the new classes don't conflict with the existing ones
module GeneratedClasses; end
eval "class GeneratedClasses::#{a.class} < ActiveRecord::Base; end"
Presto!
Now you can do this:
a = GeneratedClasses::Animal.new
a.update_attributes whatever
a.save
PS: Don't do this!
Apart from being awful, if your rails app restarts it will lose all concept of the Generated Classes, so you'll need to devise some mechanism of persisting those too.
I have this exact situation. I have to read data external to the application and the performance hit is so big, that I store locally. I have gone with a solution where I have, over time, developed a schema and migrations by hand, that work with the data, and allow me to persist the data to the tables. I have developed a caching scheme that works for my data and the performance has increased significantly.
All that to say, I did everything by hand and I don't regret it. I can have confidence that my database is stable and that I am not re-creating db tables on the fly. Because of that, I have no concern about the stability of my application.
Depending on what you're trying to do with the objects, you can store objects directly into the database by serializing them.
Try looking at some ORM solutions. Or store as XML.