I am trying to set up a polymorphic association for a parent that has multiple different types of children. What I find in the documentation is about the inverse problem: children with different types of parents.
I tried the code below, but that always results in productables of type 'Store', while I want them to be 'Book' or 'DVD'.
class App.Product extends Batman.Model
#belongsTo 'productable', {polymorphic: true}
class App.Book extends Product
class App.DVD extends Product
class App.Store extends Batman.Model
#hasMany 'products', {as: 'productable'}
Thanks.
Yes, you're right. Usually, polymorphism is for a case like this:
class App.Product extends Batman.Model
#belongsTo 'productable', polymorphic: true
class App.Store extends Batman.Model
#hasMany 'products', as: 'productable', polymorphic: true
class App.VendingMachine extends Batman.Model
#hasMany 'products', as: 'productable', polymorphic: true
Now, a Product will store both productable_id and productable_type. productable_type will be either Store or VendingMachine.
But... you want to have children of different types available from a single accessor? I don't know that batman.js supports this out of the box, but here's a way you might be able to get it done:
class App.Store extends Batman.Model
#hasMany 'products' # setup the relationships individually
#hasMany 'books'
#hasMany 'dvds'
#accessor 'allProducts', -> # then create a custom accessor for all of them
products = #get('products')
books = #get('books')
dvds = #get('dvds')
products.merge(books, dvds) # returns a new Batman.Set
Actually, the docs say not to use Set::merge inside accessors, but rather to use Batman.SetUnion. If this approach seems like it would work, you might want to look into that!
Then, if you need to send class names back in JSON, you can use #encode on your child models, for example:
class App.Product extends Batman.Model
#resourceName: 'product'
#encode 'product_type',
encode: (value, key, builtJSON, record) ->
builtJSON.product_type = Batman.helpers.camelize(record.constructor.resourceName)
Hope that helps!
Edit:
you could shorthand the relation and accessor, if you think you'll have a lot of them:
class App.Store extends Batman.Model
#productTypes: [
'products'
'books'
'dvds'
]
for productType in #productTypes
#hasMany productType
#accessor 'allProducts', ->
allProducts = new Batman.Set
productSets = (#get(pt) for pt in #constructor.productTypes)
allProducts.merge(productSets...)
Might be worth a shot anyways!
Related
Here are the two models I have:
class Parent
many :children
key name
end
class Children
belongs_to :parent
key price
key description
end
I want to find all the children that have a parent with a particular name let's say test.
I could this with
Children.all.select{|a| a.parent.name == "test"}
But I would like to define a scope in the children class is that possible using where clause?
I tried with
scope :parent_name, where("parent.name" => "test")
But it does not work.
So I am wondering if there is a better way of doing this. I have a parent class that has three 1 to 1 relationships, children.
class Parent
has_one :child_one
has_one :child_two
has_one :child_three
...
end
Then within each child object I state belongs_to :parent, now ChildOne has attribute1, attribute2, ..., attribute30 etc.
I am using a gem that uses yaml to build calculations, but it can only access the table or model of the Parent class. It means that all the attributes i need from the ChildOne class I'll have to pull like this
def calculation_one
cal_one = self.child_one.attribute1
end
and onward. This would mean that I would have a model that's freakin fat just linking children attributes. Is there a better way of doing this?
Update
What i am looking for is a way to basically attr_accessor a subclass?
class Parent
attr_accessor :child_one, :attribute1
end
person = Parent.new
person.attribute1 = "awesome"
person.attribute1 # => "awesome"
I think what you're looking for is the Delegate module in rails : you can call delegate to let a related model respond to a method call like that :
class Parent < ActiveRecord::Base
has_one :child_one
delegate :attribute1, to: :child_one
end
Full documentation : http://apidock.com/rails/Module/delegate
I am trying to set up the polymorphic association in Batman as documented here. I am using LocalStorage and keep getting the following message:
Related model undefined for polymorphic association not found.
My model is a follows:
class App1.Model extends Batman.Model
#persist Batman.LocalStorage
class App1.Superpower extends App1.Model
#resourceName: 'superpower'
#encode 'id', 'name'
#belongsTo 'superpowerable', polymorphic: true
class App1.Hero extends App1.Model
#resourceName: 'hero'
#encode 'id', 'name'
#hasMany 'superpowers', as: 'superpowerable', polymorphic: true
class App1.Villain extends App1.Model
#resourceName: 'villain'
#encode 'id', 'name'
#hasMany 'superpowers', as: 'superpowerable', polymorphic: true
and the code I use to instantiate all:
superman = new App1.Hero(name: "Superman")
superman.save()
super_strength = new App1.Superpower(name: "Super Strength")
super_strength.save() # -> gives "Related model undefined for polymorphic association not found."
invincibility = new App1.Superpower(name: "Invincibility")
invincibility.save() # -> gives "Related model undefined for polymorphic association not found."
superman.get('superpowers').add super_strength
superman.get('superpowers').add invincibility
superman.save()
super_strength.save()
invincibility.save()
console.log superman.toJSON()
console.log super_strength.toJSON()
console.log invincibility.toJSON()
According to this question it depends on the namespace, but they are all the same in my code, so I'm really wondering what is going wrong here.
Thanks in advance
Uh oh... I wrote that example so if it doesn't work, I'm to blame :)
Just to be sure, did you call App1.run() at the beginning of that script? I ran into that issue while testing last week -- gotta call run to load associations!
Edit:
Oh, it looks like a matter of using the API just right. You have to be explicit with the #{label}_type and #{label}_id of the related models. Batman.js will load the associations from JSON just fine, but you'll have to specify them when initializing a new record, for example:
super_strength = new App1.Superpower
name: "Super Strength"
superpowerable_id: superman.get('id'),
superpowerable_type: 'Hero'
I put up a JSFiddle where it's working: http://jsfiddle.net/2atLZ/2/
I'll go back to the docs and add a note about this! Long term, would be great if the API just accepted #{label} then extracted #{label}_id and #{label}_type... but for now, it's not so.!
I'm running into an issue trying to implement polymorphic associations on a Batman model. I'm getting this error in the console:
Related model undefined for polymorphic association not found.
I'm having a hard time tracking down where I am going wrong. Where should I look to find the missing piece?
My models look something like this:
class Admin.Product.PopularCollectables extends Batman.Model
#belongsTo 'collectable', polymorphic: true
class Admin.Item extends Batman.Model
#hasOne 'popular_collectable', as: 'collectable'
When Batman loads a related model in an association, it checks a namespace. By default, the namespace is Batman.currentApp (which is your app after you call MyApp.run()), but you can also pass a namespace when you declare the association:
class Admin.Item extends Batman.Model
#hasOne 'popular_collectable', as: 'collectable', namespace: Admin.Product
That way, Batman will look for PopularCollectable on Admin.Product instead of on Admin.
I was able to workaround this issue by embedding the belongs_to side of a has_many relation inside the parent Batman Model instance when it is sent to the client from Rails:
format.json {render :json => #post, :include => {:comments => {:include => :comments}}}
I have
class Car < ActiveRecord::Base; end
class Porsche < Car
has_many :page_items, :as=>:itemable, :dependent=>:destroy
end
I have to mention that I'm using a single table called cars which has a field type.
class PageItem<ActiveRecord::Base
belongs_to :itemable, :polymorphic=>true
end
When I do
a = PageItem.new
a.itemable = Porsche.new
a.itemable
#<Porsche id: nil, type: "Porsche", name: nil, ..etc>
a.itemable_type
=> "Car"
And it should be
a.itemable_type
=> "Porsche"
Do anybody have an idea about this?
Update
According to bor1s's answer probably this is the right behaviour. So if is it, then my question is how can I set it to Porsche implicitly?
This is because Porshe is virtual model, it is not really exist in in database, it just has type field to know that it is Porshe. Maybe you should get rid of STI and use say type column to save Car model with type of Porshe and use scope to find porshes. I hope I helped you alittle.
I think if you add type field to the Car table, you'll be able to get Porsche type using :
PageItem.new(:itemable => Porsche.new).itemable.type