I have a models like below
# Base model
class RecordBase < ActiveRecord::Base
self.table_name = 'records'
has_many :record_images, as: assetable, dependent: :destroy
fileuploads :record_images
end
# Inherited model
class SpecificRecord < RecordBase
end
# Image model
class RecordImage < ActiveRecord::Base
include Uploader::Asset
belongs_to :assetable, polymorphic: true
end
I'm using https://github.com/superp/rails-uploader for upload files. I have record of RecordImage model in database with assetable_id corresponding to SpecificRecord record and assetable_type 'SpecificRecord' but when I try SpecificRecord.find(some_id).record_images in console, rails builds sql query with assetable_type 'RecordBase' and hence finds nothing. How to specify which assetable_type should be in sql query?
Update
That's issue about rails-uploader gem, in version '0.2.8' gem redefined .base_class and saves data to db with assetable_type of child class, but in version '0.4.5' assets saves to db with assetable_type of base class.
Related
Here is how I use array in a rails 5 model
in migration
t.text :diagnoses, array: true, default: []
in model
class Patient < ApplicationRecord
serialize :diagnoses, Array
end
in my seed method I am doing it like
patient = Patient.create(first_name: 'John', last_name: 'Smith', admission: a)
patient.diagnoses = [1, 2]
patient.save!
It give an error as
ActiveRecord::SerializationTypeMismatch: can't dump `diagnoses`: was supposed to be a Array, but was a Integer. -- 0
Thanks for any help!
A while ago, I encountered this exact issue. I found the following workaround:
In your migration file:
t.text :diagnoses, array: true
Then in model:
class Patient < ApplicationRecord
serialize :diagnoses
after_initialize do |patient|
patient.diagnoses= [] if patient.diagnoses == nil
end
end
The after_initialize callback will be called whenever an Active Record object is instantiated, either by directly using new or when a record is loaded from the database.
I would seriously consider actually using a relational database properly instead.
# since diagnosis is highly irregular we most likely need to configure rails
# to pluralize it correctly
# config/initializers/inflections.rb
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.irregular 'diagnosis', 'diagnoses'
end
class Patient < ApplicationRecord
has_many :patient_diagnoses
has_many :diagnoses, through: patient_diagnoses
end
# this table provides data normalization
class Diagnosis < ApplicationRecord
has_many :patient_diagnoses
has_many :patients, through: patient_diagnoses
end
# this is just a join table
class PatientDiagnosis < ApplicationRecord
belongs_to :patient
belongs_to :diagnosis
end
This lets you use the foreign keys to ensure referential integrity and lets you use ActiveRecord Associations instead of just cobbling together something wonky. There are very few actual advantages of using an array type here.
If you still want to use your array column you should not use ActiveRecord::AttributeMethods::Serialization. Its used with plain old varchar / text columns to store YAML strings which are serialized/unserialized in Rails. It is a vestige from the dark days before we had native JSON/array types and really does not have any use today besides in legacy applications.
I have a very special cases. I understand maybe db design is not very awesome, but I cannot change that.
class Employer < ApplicationRecord
has_many :contract_employers
has_many :contracts, through: :contract_employers
has_many :crm_contacts, through: :contract_employers
# typical join table, with key: contract_id and employer_id
class ContractEmployer < ApplicationRecord
belongs_to :contract
belongs_to :employer
has_many :crm_contacts
# CrmContact table has key: contract_employer_id
class CrmContact < ApplicationRecord
belongs_to :contract_employer
has_one :employer, through: :contract_employer
Given
employer = Employer.create
I have no issue to run
employer.contracts.create
However, if I try to run
employer.crm_contacts.create
It raise error
ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection: Cannot modify association 'Employer#crm_contacts' because the source reflection class 'CrmContact' is associated to 'ContractEmployer' via :has_many.
I checked the rails source code, basically it states very clearly
# Construct attributes for :through pointing to owner and associate. This is used by the
# methods which create and delete records on the association.
#
# We only support indirectly modifying through associations which have a belongs_to source.
# This is the "has_many :tags, through: :taggings" situation, where the join model
# typically has a belongs_to on both side. In other words, associations which could also
# be represented as has_and_belongs_to_many associations.
#
# We do not support creating/deleting records on the association where the source has
# some other type, because this opens up a whole can of worms, and in basically any
# situation it is more natural for the user to just create or modify their join records
# directly as required.
So only typical join table supports model.associations.create? Any suggestion for my user case?
Take my case for example, even rail is willing to do the job. How could employer.crm_contacts.create create middle table record ContractEmployer? Yes, it knows employer.id, but it has no clue what contract.id is, right?
Rails can not create middle table record in this case, but you can.
And I am completely agree with this (comments in rails source code /activerecord/lib/active_record/associations/through_association.rb):
in basically any situation it is more natural for the user to just
create or modify their join records directly as required
I don't see a problem here.
class Employer < ApplicationRecord
# ...
def create_crm_contact
ActiveRecord::Base.transaction do
contract = contracts.create # will create both `contract` and associated `contract_employer`
# find the `contract_employer` that has been just created
contract_employer = contract_employers.find_by(contract_id: contract.id)
contract_employer.crm_contacts.create
end
end
Say I have this model and associated schema defined.
class Memory < ActiveRecord::Base
belongs_to :memory_slot
end
class MemorySlot < ActiveRecord::Base
has_many :memories
end
Now typically it let me can access memory slots of Memory via #memory.memory_slot.name. But I want to access it via different method like #memory.supporting_memory_slot.name. What is the best way I can do that?
You won't need any new migration, you can use the previous memory_slot_id, and still can change the name like following:
class Memory < ActiveRecord::Base
belongs_to :supporting_memory_slot, class_name: 'MemorySlot', foreign_key: 'memory_slot_id'
end
class MemorySlot < ActiveRecord::Base
has_many :memories
end
This way, if you had any records saved previously, they will work in the current scenario as well. But if you generate a new migration, the old records saved will not be accessible, because they were used using the foreign_key as memory_slot_id.
If you can change your model association like this
class Memory < ActiveRecord::Base
belongs_to :supporting_memory_slot, :class_name => 'MemorySlot', :foreign_key => 'supporting_memory_slot_id'
end
then you can do something like this
#memory.supporting_memory_slot.name
Note: In this case,you must generate a new migration to add supporting_memory_slot_id to your memories table
I'm working on building an app to keep track of product designs, and I'm having some trouble with my associations. Basically I have a model (Assembly) which needs to have polymorphic association, but also needs to be able to belong to itself.
To illustrate, I have three models: Product, Assembly, and Part.
A Product can have many Assemblies.
An Assembly can have many Parts AND Assemblies.
An Assembly belongs to a Product OR an Assembly.
A Part belongs to an Assembly.
My model definitions are currently like this:
product.rb
class Product < ActiveRecord::Base
belongs_to :product_family
has_many :assemblies, as: :assemblable
end
assembly.rb
class Assembly < ActiveRecord::Base
belongs_to :assemblable, polymorphic: true
has_many :parts
has_many :subassemblies, as: :assemblable
end
part.rb
class Part < ActiveRecord::Base
belongs_to :assembly
belongs_to :product_family
end
What I would like to be able to do is, given an assembly called "top_assy":
top_assy.subassemblies.create
However, when I try this, I get the following error:
NameError: uninitialized constant Assembly::Subassembly
I'm clearly doing something wrong here - what am I missing? I have already tried adding 'class_name: "Assembly"' as an argument to the 'has_many :subassemblies' command.
Thanks in advance!
has_many :subassemblies, as: :assemblable
by
has_many :subassemblies, as: :assemblable, class_name: 'Assembly'
Carlos's solution works because now rails knows which class to query, as follows:
Before specifying :class_name:
When calling .subassemblies method, rails queries a supposed 'Subassembly' model class to match the 'assemblable_id' column in that class. However, 'Subassembly' model class is not defined (it doesn't make sense to define it anyway) here and hence the error.
After specifying :class_name:
Because the class 'Assembly' was specified as :class_name, now rails knows it is to query the 'Assembly' model class and match the 'assemblable_id' column.
Demonstation of flow:
# :class_name has been specified to be 'Assembly'
ex_asm = Assembly.new # an example assembly
ex_asm.subassemblies # flow:
# 1. Rails checks the :subassemblies association
# 2.a. There it is specified to query the class 'Assembly'
# 2.b. and it is to match the "id" column of ex_asm
# 2.c. with the 'assemblable_id' column of the Assembly table
# 3 Rails returns the assemblies matching criteria (2) as
# :subassemblies of ex_asm.
I don't know why this works, but I had the same problem and solve as it:
In Assembly class replace
has_many :subassemblies, as: :assemblable
by
has_many :subassemblies, as: :assemblable, class_name: 'Assembly'
=====================================================================
Edit: explanation of solution
Before specifying :class_name:
When calling .subassemblies method, rails queries a supposed 'Subassembly' model class to match the 'assemblable_id' column in that class. However, 'Subassembly' model class is not defined (it doesn't make sense to define it anyway) here and hence the error.
After specifying :class_name:
Because the class 'Assembly' was specified as :class_name, now rails knows it is to query the 'Assembly' model class and match the 'assemblable_id' column.
Demonstation of flow:
# :class_name has been specified to be 'Assembly'
ex_asm = Assembly.new # an example assembly
ex_asm.subassemblies # flow:
# 1. Rails checks the :subassemblies association
# 2.a. There it is specified to query the class 'Assembly'
# 2.b. and it is to match the "id" column of ex_asm
# 2.c. with the 'assemblable_id' column of the Assembly table
# 3 Rails returns the assemblies matching criteria (2) as
# :subassemblies of ex_asm.
you can try this
product.rb
class Product < ActiveRecord::Base
belongs_to :product_family
has_many :assemblies
end
assembly.rb
class Assembly < ActiveRecord::Base
attr_accessible :top_assembly_id
has_many :sub_assemblies, :class_name => "Assembly", :foreign_key => "top_assembly_id"
belongs_to :top_assembley, :class_name => "Assembly"
has_many :parts
end
part.rb
class Part < ActiveRecord::Base
belongs_to :assembly
belongs_to :product_family
end
and now you can referer
top_assembley.sub_assemblies.create
Im very new to Ruby on Rails 3 and ActiveRecord and seem to have been thrown in at the deep end at work. Im struggling to get to grips with querying data from multiple tables using joins.
A lot of the examples Ive seen either seem to be based on much simpler queries or use < rails 3 syntax.
Given that I know the business_unit_group_id and have the following associations how would I query a list of all related Items and ItemSellingPrices?
class BusinessUnitGroup < ActiveRecord::Base
has_many :business_unit_group_items
end
class BusinessUnitGroupItem < ActiveRecord::Base
belongs_to :business_unit_group
belongs_to :item
belongs_to :item_selling_price
end
class Item < ActiveRecord::Base
has_many :business_unit_group_items
end
class ItemSellingPrice < ActiveRecord::Base
has_many :business_unit_group_items
end
I'm confused as to whether I need to explicity specify any joins in the query since the associations are in place.
Basically, you do not need to specify the joins:
# This gives you all the BusinessUnitGroupItems for that BusinessUnitGroup
BusinessUnitGroup.find(id).business_unit_group_items
# BusinessUnitGroupItem seems to be a rich join table so you might
# be iterested in the items directly:
class BusinessUnitGroup < ActiveRecord::Base
has_many :items through => :business_unit_group_items
# and/or
has_many :item_selling_prices, through => :business_unit_group_items
...
end
# Then this gives you the items and prices for that BusinessUnitGroup:
BusinessUnitGroup.find(id).items
BusinessUnitGroup.find(id).item_selling_prices
# If you want to iterate over all items and their prices within one
# BusinessUnitGroup, try this:
group = BusinessUnitGroup.include(
:business_unit_group_item => [:items, :item_selling_prices]
).find(id)
# which preloads the items and item prices so while iterating,
# no more database queries occur