Foreign reference in data mapper - ruby-datamapper

Say I have two data mapper classes like this:
class Family
include DataMapper::Resource
property :id, Serial
property :nice, Boolean
end
Class Person
include DataMapper::Resource
property :id, Serial
belongs_to :family
end
If I want to get all the people belonging to a certain family family, I can use this:
people=Person.all(:family=>family)
But what if I want to get all people who belong to a family which have the nice attribute? In SQL I could do
SELECT * FROM persons, families WHERE persons.family_id=families.id AND families.nice
Is there a nice way to do this in data mapper without dropping to the underlying dataset?

You need to specify that a Family has several Persons. See the documentation on has n and belongs_to.
class Family
include DataMapper::Resource
property :id, Serial
property :nice, Boolean
# add this:
has n, :people
end
Then you can use this:
Family.all(:nice => true).people
The SQL generated is actually a subquery rather than a join:
SELECT "id", "family_id" FROM "people" WHERE "family_id" IN (SELECT "id" FROM "families" WHERE "nice" = 't') ORDER BY "id"

Related

Combining data from two tables in rails

I have two models, one belongs to the other. They look like this:
class LittleClass < ActiveRecord::Base
has_many :little_class_sessions
end
and
class LittleClassSession < ActiveRecord::Base
belongs_to :little_class
end
LittleClassSession has a column called little_class_id. I want to get all LittleClassSession but also have the associated LittleClass returned to me in the same hash.
Is there some way to do this that's built into Rails? Or is there a clean way to do this?
And is this something that I build into the LittleClass or LittleClassSession model with scope?
When you query ActiveRecord you will get an array of ActiveRecord:Relation. It is a specific entity which starts your query. You can of course join dependent tables (as in your example with one-to-many relation). But you will still need to go over those dependent relations to build whatever object you need.
Here is a sketch of what I mean (assume we search for all little class sessions with specific little class id):
class_sessions = LittleClassSession.includes(:little_class).where(:little_classes => {:id => 1})
class_sessions.each do |relation|
test_hash = relation.attributes.merge!({:little_class => relation.little_class.attributes});
puts test_hash
end
test_hash will include all the attributes of the little class session as well as attributes of the little class under corresponding key.

Save model id as foreign key through has_one / belongs_to

I'll briefly explain my situation: I have a model called "tax" which belongs_to a "user" and of which a "user" has_one.
In my users table I have a column called "tax_id" which I want to store the id of the tax model when a user creates one.
Currently, in my tax model the create function looks something like this:
class Tax < ActiveRecord:Base
belongs_to :user
tax = Tax.new(income: income, taxes: taxes, rrsp: rrsp)
tax.save
and then in the taxes_controller file, the create function looks like this:
def create
#tax = Tax.new(secure_params)
#tax.user = User.find(current_user.id)
if #tax.save
redirect_to show_tax_path(current_user.tax)
else
render :new
end
end
(secure_params) being the strong parameters for the field inputs set in a private definition.
Now, someone mentioned that I may have better luck if I use build but unfortunately I couldn't get it to work at all, something to do with how I'm using current_user (devise). Currently, my setup works just fine, other than saving the tax model id in the user model column "tax_id" like I said.
I'm wondering if I perhaps need to add a foreign ID key to either the belongs_to or has_one statement, even though I was under the impression that the "link" should be made automatically as long as the column was named "[model]_id"
try using
user.build_tax
I think this might help you out.
The build syntax for has_many association:
user.taxes.build
The build syntax for has_one association:
user.build_tax # this will work
user.tax.build # this will throw error

DataMapper- can I avoid intermediate tables?

I am a total beginner at DataMapper, and have two models:
class ThirdPartyAccount
include DataMapper::Resource
property :access_token, String, :length => 500
belongs_to :user
end
class User
include DataMapper::Resource
property :id, Serial
property :first_name, String
has n, :third_party_accounts, :through => Resource
end
Looking at the SQL logs, it appears to create two tables- users, third_party_accounts and third_party_account_users to join the two. It doesn't appear that the last table is needed- surely the third_party_account table just needs to use it's user_id field to map directly to the user table? Have I accidentally created a many-to-many relationship here?
It's due to this line:
has n, :third_party_accounts, :through => Resource
:through => Resource tells DataMapper to that it's a "has-and-belongs-to-many" relation (each 3rd party account belongs to multiple users and each user has multiple 3rd party accounts), which requires an intermediate table. If this is just a has-many relation (each user has many 3rd party accounts, but each account only belongs to one user), you should just use:
Class User
...
has n, :third_party_accounts
end
See http://datamapper.org/docs/associations.html for more info.

Rails merging tables

I have two tables: Members and Addresses. How can I merge these two tables together in my model, so that I could combine all their columns together? For example, Members has a column named position and Addresses has a column named street. How could I make it so that I could have Position and Address in the same virtual table. Is there a merge function for this?
Not sure why you need to create a separate model, you can use the basic Ruby constructs for this:
I'll assume Member has a column called address_id, that is a member belongs_to an Address. This will automatically associate the joins for you.
So all you need to do is something like this:
class Address < ActiveRecord::Base
end
class Member < ActiveRecord::Base
belongs_to :address
end
member = Member.create(:address => Address.find(123))
If you want to get the address and just the position field then go:
member = Member.find(456)
position = member.address.position
or perhaps:
Member.joins(:address).select("members.position, addresses.street")
Not sure what you are trying to do, but if it is something very specific, you could also try creating a View in your database and then making a model for it, treating it like a regular table.

Ruby on Rails/Activerecord mySQL modeling

This is a pretty simple question really, but let's say I'm creating a model for Person. Person obviously has first name, last name, age, etc. But the person also has contact info consisting of things like address line 1, address line 2, city, state, zip, country, phone 1, phone 2, etc...
Does it make more sense to create a model for Person and then list that contact information as tables in the model or to also create, say, a ContactInfo (or Address, etc) model, then associate the Person to ContactInfo through an association (Person has_one ContactInfo/Person has_one Address/Address belongs_to Person, etc)?
Which of these is a better approach and what are the benefits/drawbacks to each method?
edit: in re to j..
So with this approach, would I have to then create an Addressable model?
script/generate model Addressable
class Addressable < ActiveRecord::Base
#stuff here?
end
or is this unnecessary?
Also, would i need to add this line to the create_users.rb:
t.references :addressable, :polymorphic => true
I feel like I'm missing something, but I'm not sure what. I appreciate the help a ton, btw! Thanks!
I'd create separated tables/models for address, phone and stuff like this and would make them polymorphic. Like this:
class Address < ActiveRecord::Base
belongs_to :addressable, :polymorphic => true
end
class Person < ActiveRecord::Base
has_one :address, :as => :addressable
end
I believe this is the best way because later you may need to add, for example, a Company model and it'll be easy to make it addressable.
Edit
Using the address as example, you'd need an Address model, not Addressable.
And you'll have to add
t.references :addressable, :polymorphic => true
or
t.belongs_to :addressable, :polymorphic => true
to your create_addresses migration, so you'll have the addressable_id and addressable_type in the addresses table.
Let me know if you have any other doubts :]
the answer above makes sense, but think about how many fields you need and how many records you have to manage. creating a table for each additional field may be too much effort.
another approach could be something more flexible: create a table (say, person_details) with 3 fields: person_id:integer, field_name:string, field_data:string, then the model:
class PersonDetail < ActiveRecord::Base
belongs_to :person
end
this way you can add whatever additional field you need: phone1..phoneN, address1..addressN, and so on.
another similar approach is to pre-determine fields names, to avoid different labels during inserts:
class PersonDetail < ActiveRecord::Base
belongs_to :person
FIELD_NAMES => { 'Address' => 1, 'Phone' => 2)
end
in this case you'll declare the field_name as integer (because it stores only the value of the hash, not a string).