I have a rails app with some nested data that I'd like to export as a CSV file
The models look like:
class ContainerRecord < ActiveRecord::Base
has_many :child_records
and
class ChildRecord < ActiveRecord::Base
belongs_to :container_record
I'd like to be able to export a CSV file with each ContainerRecord on a row with its information in the first few columns and a value from each ChildRecord in the remaining columns.
I can't guarantee the number of ChildRecords associated with each ContainerRecord and I don't care if I have a different number of non-null columns for each row.
I've attempted to use FasterCSV, but I get all of the data for the child records shoved into one column rather than a column for each.
Is this something that I can do with FasterCSV? If not, what method can I use to accomplish my goal?
Not sure about FasterCSV but a quick & dirty solution could be:
class ParentClass < AR::Base
has_many :children
def self.csv
all.map do |object|
( object.attributes.values + object.children.map(&:child_field) ).flatten.join(',')
end.join("\n")
end
end
Replacing "child_field" with the field you want to take from your child model of course.
I ended up finding a nice tutorial on csv_builder that let me do exactly what I wanted to do with minimal effort and allowed me to stick a little more closely to the MVC architecture.
Related
I'm building an application where users are part of an Organisation. An organisation has many Lists, which in turn have many ListItems.
Now, I would like for admin users to be able to specify which attributes are available on list items, based on the organisation they belong to (or rather, on the organisation their list belongs to), without having to touch any code.
So far, when defining attributes that are not bound to a specific column in the database, I have used document_serializable, a nifty little gem (based on virtus) which serializes virtual attributes to a JSONB column in the db. I like this approach, because I get all of virtus' goodies (types, coercion, validations, etc.), and because data ends up sitting in a JSONB column, meaning it can be loaded quickly, indexed, and searched through with relative ease.
I would like to keep using this approach when adding these user-defined attributes on the fly. So I'd like to do something like:
class ListItem < ApplicationRecord
belongs_to :list
delegate :organisation, to: :list
organisation.list_attributes.each do |a, t|
attribute a, t
end
end
Where Organisation#list_attributes returns the user-defined hash of attribute names and their associated types, which, for example, might look like:
{
name: String,
age: Integer
}
As you might have guessed, this does not work, because organisation.list_attributes.each actually runs in the context of ListItem, which is an instance of Class, and Class doesn't have an #organisation method. I hope that's worded in a way that makes sense1.
I've tried using after_initialize, but at that point in the object's lifecycle, #attribute is owned by ActiveRecord::AttributeMethods::Read and not DocumentSerializable::ClassMethods, so it's an entirely different method and I can't figure out wether I can still access the one I need, and wether that would even work.
Another alternative would be to find the organisation in question in some explicit way, Organisation#find-style, but I honestly don't know where I should store the information necessary to do so.
So, my question: at the moment of instantiating (initializing or loading2) a record, is there a way I can retrieve a hash stored in a database column of one of its relations? Or am I trying to build this in a completely misguided way, and if so, how else should I go about it?
1 To clarify, if I were to use the hash directly like so:
class ListItem < ApplicationRecord
belongs_to :list
delegate :organisation, to: :list
{
name: String,
age: Integer
}.each do |a, t|
attribute a, t
end
end
it would work, my issue is solely with getting a record's relation at this earlier point in time.
2 My understanding is that Rails runs a model's code whenever a record of that type is created or loaded from the database, meaning the virtual attributes are defined anew every time this happens, which is why I'm asking how to do this in both cases.
at the moment of instantiating (initializing or loading) a record, is
there a way I can retrieve a hash stored in a database column of one
of its relations?
Yes. This is fairly trivial as long as your relations are setup correctly / simply. Lets say we have these three models:
class ListItem < ApplicationRecord
belongs_to :list
end
class List < ApplicationRecord
belongs_to :organisation
has_many :list_items
end
class Organisation < ApplicationRecord
has_many :lists
end
We can instantiate a ListItem and then retrieve data from anyone of its parents.
#list_item = ListItem.find(5) # assume that the proper inherited
foreign_keys exist for this and
its parent
#list = #list_item.list
#hash = #list.organisation.special_hash_of_org
And if we wanted to do this at every instance of a ListItem, we can use Active Record Callbacks like this:
class ListItem < ApplicationRecord
belongs_to :list
# this is called on ListItem.new and whenever we pull from our DB
after_initialize do |list_item|
puts "You have initialized a ListItem!"
list = list_item.list
hash = list.organisation.special_hash_of_org
end
end
But after_initialize feels like a strange usage for this kind of thing. Maybe a helper method would be a better option!
I'm creating an app that it needs to be customizable.
Ej:
I have:
Position scaffold
Category scaffold
Class scaffold
Type Scaffold
Now, the situation is, position table along other attributes, includes a category attr, a class attr and a type attr. The problem I have is that every category, class and type is created and owned by another controller and moedel.
Now, here is where my problem comes to life.
when I tried to create a position (even though I can actually see all categories, classes and types listed on my position view via form.select) I cannot save a position with a category, class nor type.
I tried nested_attributes and don't work quite good in this situation.
What is the appropriate method tho attack this from scratch base on my scaffolds?
Any help will be very much appreciated!
Thanks in advance!
It is a little tough to figure out what you need without seeing your code, but I'll try my best to help.
Keep in mind that Class and Type are reserved words in rails; Columns named _type are used for polymorphic relationships. And Class is used well for ruby classes. So as a general rule of thumb I would stay clear of those terms.
For you Position model:
class Position < ActiveRecord::Base
has_many :categories
has_many :types
has_many :classes
accepts_nested_attributes_for :categories, :types, :classes
end
Then for your other models:
class NameOfClass < ActiveRecord::Base
belongs_to :position
end
You also need to make sure that you add these nested attributes to the whitelist on you controllers. It should include any columns that you want to accept that will update other models. Keep in mind that this has to be the last thing listed.
In your Position Controller
PositionsController < ApplicationController
private
def position_params
params.require(:position).permit(:id, :position_column_a, :another_column,
categories_attributes: [:id, :position_id, :any_other_column_you_want],
types_attributes: [:id, :position_id, :again_any_other_column])
Again, before you do anything I would look into renaming your scaffolds. the best way is to delete them and then regenerate. This will do the trick:
rails d scaffold Type
If you just need a simple dropdown of a list of categories you can try enums. However, it is not good for options that need to be customizable for the user. I typically only used them for statuses.
https://bendyworks.com/blog/rails-enum-sharp-knife
You can also read the API docs: http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
I would like to create a list of model objects that designate a new relationship without having to use raw sql.
Suppose I have the following models:
class MealCombination < ActiveRecord::Base
belongs_to :drink
belongs_to :food
end
class Food < ActiveRecord::Base
has_many :meal_combinations
end
class Drink < ActiveRecord::Base
has_many :meal_combinations
end
I would like to create a list of MealCombination objects that do not presently exist in the database.
Say my query would look something like this:
select distinct DRINK.id, FOOD.id from FOOD, DRINK where DRINK.alchohol_volume > 5 and FOOD.spice_factor > 45;
What is the most efficient way to create the MealCombination objects from this selection?
Iterating through an array returned back from the raw sql seems inefficient. I do not want to persist the objects into the database.
The easiest way to do this would probably be to create a view called "meal_combinations" in your database which will represent your select query you want to do. ActiveRecord should treat the view as a table for the most part, but you should definitely add
def read_only?
true
end
to any class definition that uses a view. The main disadvantage to having a lot of views is that they make database migrations a little more of a pain to manage if you ever want to change columns in the underlying tables.
New to Rails and Ruby and trying to do things correctly.
Here are my models. Everything works fine, but I want to do things the "right" way so to speak.
I have an import process that takes a CSV and tries to either create a new record or update an existing one.
So the process is 1.) parse csv row 2.) find or create record 3.) save record
I have this working perfectly, but the code seems like it could be improved. If ParcelType wasn't involved it would be fine, since I'm creating/retrieving a parcel FROM the Manufacturer, that foreign key is pre-populated for me. But the ParcelType isn't. Anyway to have both Type and Manufacturer pre-populated since I'm using them both in the search?
CSV row can have multiple manufacturers per row (results in 2 almost identical rows, just with diff mfr_id) so that's what the .each is about
manufacturer_id.split(";").each do |mfr_string|
mfr = Manufacturer.find_by_name(mfr_string)
# If it's a mfr we don't care about, don't put it in the db
next if mfr.nil?
# Unique parcel is defined by it's manufacturer, it's type, it's model number, and it's reference_number
parcel = mfr.parcels.of_type('FR').find_or_initialize_by_model_number_and_reference_number(attributes[:model_number], attributes[:reference_number])
parcel.assign_attributes(attributes)
# this line in particular is a bummer. if it finds a parcel and I'm updating, this line is superfulous, only necessary when it's a new parcel
parcel.parcel_type = ParcelType.find_by_code('FR')
parcel.save!
end
class Parcel < ActiveRecord::Base
belongs_to :parcel_type
belongs_to :manufacturer
def self.of_type(type)
joins(:parcel_type).where(:parcel_types => {:code => type.upcase}).readonly(false) unless type.nil?
end
end
class Manufacturer < ActiveRecord::Base
has_many :parcels
end
class ParcelType < ActiveRecord::Base
has_many :parcels
end
It sounds like the new_record? method is what you're looking for.
new_record?() public
Returns true if this object hasn’t been saved yet — that is, a record
for the object doesn’t exist yet; otherwise, returns false.
The following will only execute if the parcel object is indeed a new record:
parcel.parcel_type = ParcelType.find_by_code('FR') if parcel.new_record?
What about 'find_or_create'?
I have wanted to use this from a long time, check these links.
Usage:
http://rubyquicktips.com/post/344181578/find-or-create-an-object-in-one-command
Several attributes:
Rails find_or_create by more than one attribute?
Extra:
How can I pass multiple attributes to find_or_create_by in Rails 3?
I am working on a very large Rails application. We initially did not use much inheritance, but we have had some eye opening experiences from a consultant and are looking to refactor some of our models.
We have the following pattern a lot in our application:
class Project < ActiveRecord::Base
has_many :graph_settings
end
class GraphType < ActiveRecord::Base
has_many :graph_settings
#graph type specific settings (units, labels, etc) stored in DB and very infrequently updated.
end
class GraphSetting < ActiveRecord::Base
belongs_to :graph_type
belongs_to :project
# Project implementation of graph type specific settings (y_min, y_max) also stored in db.
end
This also results in a ton of conditionals in views, helpers and in the GraphSetting model itself. None of this is good.
A simple refactor where we get rid of GraphType in favor of using a structure more like this:
class Graph < ActiveRecord::Base
belongs_to :project
# Generic methods and settings
end
class SpecificGraph < Graph
# Default methods and settings hard coded
# Project implementation specific details stored in db.
end
Now this makes perfect sense to me, eases testing, removes conditionals, and makes later internationalization easier. However we only have 15 to 30 graphs.
We have a very similar model (to complicated to use as an example) with close to probably 100 different 'types', and could potentially double that. They would all have relationships and methods they inheritated, some would need to override more methods then others. It seems like the perfect use, but that many just seems like a lot.
Is 200 STI classes to many? Is there another pattern we should look at?
Thanks for any wisdom and I will answer any questions.
If the differences are just in the behavior of the class, then I assume it shouldn't be a problem, and this is a good candidate for STI. (Mind you, I've never tried this with so many subclasses.)
But, if your 200 STI classes each have some unique attributes, you would need a lot of extra database columns in the master table which would be NULL, 99.5% of the time. This could be very inefficient.
To create something like "multiple table inheritance", what I've done before with success was to use a little metaprogramming to associate other tables for the details unique to each class:
class SpecificGraph < Graph
include SpecificGraphDetail::MTI
end
class SpecificGraphDetail < ActiveRecord::Base
module MTI
def self.included(base)
base.class_eval do
has_one :specific_graph_detail, :foreign_key => 'graph_id', :dependent => :destroy
delegate :extra_column, :extra_column=, :to => :specific_graph_detail
end
end
end
end
The delegation means you can access the associated detail fields as if they were directly on the model instead of going through the specific_graph_detail association, and for all intents and purposes it "looks" like these are just extra columns.
You have to trade off the situations where you need to join these extra detail tables against just having the extra columns in the master table. That will decide whether to use STI or a solution using associated tables, such as my solution above.