Ok, so this is driving me crazy. I have read the Associations article and example and been trying to work this out for the las three days and I'm tired of this making me feel dumb, so...
How does one set up associations with DataMapper?
(I am using DM with Sinatra with SQLite3. Everything word fine for single tables with multiple values etc. It's when I start to try to associate them that I start getting errors.)
Let's say I have an Orchard full of Apple Trees. Each Tree has many Apples. Each Apple has many Seeds. Therefore each tree has many Seeds through its Apples
require 'sinatra'
require 'datamapper'
DataMapper::setup(:default, "sqlite3://#{Dir.pwd}/orchard.db")
# Trees in the orchard.
class Tree
include DataMapper::Resource
property :id, Serial
has n, :apples
has n, :seeds, :through => :apples
end
# Apples on a Tree.
class Apple
include DataMapper::Resource
property :id, Serial
belongs_to :tree
belongs_to :seed
end
# Seeds in an Apple
class Seed
include DataMapper::Resource
property :id, Serial
has n, :apple
has n, :tree, :through => apple
end
DataMapper.finalize.auto_upgrade!
Is that correct? I keep getting various errors when I try to run this. Mostly along the lines of invalid association or cannot create column NULL with value NULL etc. What am I not understanding about this relationship?
Further, Once I have a working model how do I go about getting information from it?
If there are 3 trees:
Tree.all.count
=> 3
If there are 2 apples:
Apple.all
=>[#<Apple @id=1>, #<Apple @id=2>]
Ok cool. But how many Apples does Tree #2 have? How many Seeds does Tree #4 have? How many Apples in total? How many Seeds in total?
Any help would be greatly appreciated.
Your model seems a bit confused:
Let's say I have an Orchard full of Apple Trees. Each Tree has many Apples. Each Apple has many Seeds. Therefore each tree has many Seeds through its Apples.
So a tree has many apples, an apple belongs to a tree and has many seeds, and a seed belongs to an apple (and ultimately a single tree).
We can almost (but not quite) take that language as it is and use it to create the associations. After a little translation to get the syntax right we get this:
# Trees in the orchard.
class Tree
include DataMapper::Resource
property :id, Serial
has n, :apples # "a tree has many apples"
has n, :seeds, :through => :apples
end
# Apples on a Tree.
class Apple
include DataMapper::Resource
property :id, Serial
belongs_to :tree # "an apple belongs to a tree..."
has n, :seeds # "...and has many seeds"
end
# Seeds in an Apple
class Seed
include DataMapper::Resource
property :id, Serial
belongs_to :apple # "and a seed belongs to an apple"
end
In your code you have seeds having multiple apples and trees, which doesn't really make any sense.
As for querying:
But how many Apples does Tree #2 have?
Assuming you mean the Tree
with id == 2
:
tree_2 = Tree.get(2)
apples_of_tree_2 = tree_2.apples # this gives an array of apples
count_of_apples_of_tree_2 = tree_2.apples.count
How many Seeds does Tree #4 have?
The association we added to the Tree
model has n, :seeds, :through => :apples
means we have a seeds
method available in Tree
objects.
Tree.get(4).seeds.count
How many Apples in total? How many Seeds in total?
Simply:
Apple.count # note singular not plural (it's a class method on Apple)
Seed.count
Try loading this new model into irb (you might need to delete your orchard.db
file when you change the model), and then playing around with some of the queries and creation methods, hopefully that'll give you a better idea of what's going on.
(See the section "Adding To Associations" on the Associations page.)
To add an existing Apple
to a Tree
:
a_tree.apples << an_apple
Note that a Tree
isn't associated with a single Apple
but a collection (it has n
Apples
), so the method created is apples
(i.e. it's pluralized), and there's no method apple
which is why you're seeing the no method error.
You can also create a new Apple
associated with a Tree
directly:
a_tree.apples.new #created but not saved to database
a_tree.apples.create #created and saved to database
You can also create the association the other way round, from the Apple
side:
an_other_apple = Apple.new
an_other_apple.tree = a_tree
but you need to be careful doing it this way, as the new apple won't show up in the a_tree
s collection of Apples
(a_tree.apples
won't include an_other_apple
). In order for it to appear you need to save the apple, and then call reload
on the Tree
:
an_other_apple.save
a_tree.reload
You need to watch out with this, as you can end up with an Apple
that appears to be in two Tree
s at the same time if you're not careful.
I think it's a pluralization issue - don't you hate that?
# Seeds in an Apple
class Seed
include DataMapper::Resource
property :id, Serial
has n, :apples # here
has n, :tree, :through => :apples # and here
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With