rails model relationship and migration - ruby-on-rails

I have some problem trying to understand when building a rails app with several models and relation ships between them...
If I take a basic example like a model Group, a model User and a model Car
class Group < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
belongs_to :group
has_many :cars
end
class Car < ActiveRecord::Base
belongs_to :user
end
Will those relation ship statements automatically create the following functions:
group.users
user.group
user.cars
car.user
It seems that we sometimes need to have to create "references" in migration (like adding a reference toward User in Car table) but is this always required ?
In this case, what is the difference of creating the migration and of adding the relationship statement in the models ? I sometimes have the feeling this is used for the same purpose.
Thanks a lot for your help,
Regards,
Luc

The association declarations are there for Rails only. You have to define the foreign keys (references) in the database, so that Rails can properly save the data.
Remember, despite all the magic, it's still backed by a relational database, so good practices there will pay off in the long run.

Related

Should I mock AR associations?

I'm trying to test my Order AR model in "isolation", but because it lives into a complex "context", I need to create a lot of associated AR models before.
My models are:
class Order < ApplicationRecord
belongs_to :registration #it's required!!!
end
class Registration < ApplicationRecord
belongs_to :event #it's required!!!
end
class Event < ApplicationRecord
has_many :registrations
belongs_to :account #An account belongs_to an organization
#This model a couple of required associations also
end
Is some way to "mock" the associations of my Order model??? Should I?
Thanks in advance!
I think, that with proper factory_girl setup, there will be absolutely no issues to easily setup context, that you need. During my experience I haven't faced any cases when associations were stubbed.
From my point of view, if you have some complex method inside your Order model, then you should move it into separate entity (service object, separate library, decorator). Then you can easily test this logic, without any need to setup associations, objects, etc.
I don't remember the last time I really needed to test my modal in isolation after I start making correct design of my application. I suspect, that your issue is also like a bell for you about your design. I treat models only as a set of validations(tested inside rails), associations(tested inside rails) and trivial methods, so usually there is no need to test your models

Should i use a hash or a new class for achievements in Rails?

I'm practicing ruby on rails and I'm trying to add achievements to users. I was wondering if anyone could explain the benefits/detriments to using a hash over a new class.
For example, should my object have a "has_many" relationship with an "achievement" object, or should there be a "achievement" hash and why? I'm mostly concerned with database speed implications.
should my object have a has_many relationship with an "achievement" object, or should there be a "achievement" hash and why?
I'd most certainly recommend a has_many relationship based on database backends - it gives you the ability to build the associative data as you require (instead of messing around with custom methods).
You must also realize that ActiveRecord will build a hash for you, right?
The only difference is that ActiveRecord will populate the hash with data from your db, whilst I believe you'll be talking about a hash of static data (which I've not massive experience with).
--
Since this question doesn't have many answers, this is how I'd do it:
#app/models/achievement.rb
class Achievement < ActiveRecord::Base
## you could attach this to MongoDB or some other file-based storage system
has_many :awards
has_many :users, through : :awards
end
#app/models/award.rb
class Award < ActiveRecord::Base
belongs_to :user
belongs_to :achievement
end
#app/models/user.rb
class User < ActiveRecord::Base
has_many :awards
has_many :achievements, through: :award
end
This is a standard has_many :through relationship. I imagine you already know about it, so I'll not bore you with details; however, you have to remember that this type of setup is a standard in Rails -- it will give you the functionality you want without any of the customization your ideas will probably require.
It will give you the ability to call the following:
#awards = Award.joins(:achievements).where(user_id: #current_user.id)
This will take one DB call to bring back all the achievements for a single user, all related.
The data will be encapsulated in classes -- and will basically be a series of hashes, populated from the db.
The Achievement model could easily be converted to use static data.

Rails 2 tables, 1 model

I am relatively new to ruby/rails and I have the following question:
I am working on a scheduling app and have a model named Classes and another named ClassEntries. The relationship between them is that each user can have multiple class entries per semester, each relating to one class. Each record in the Classes table belongs to a specific University. A User can have multiple entries in the ClassEntries table for 1 semester (typically 5). Their schedule is comprised of all their ClassEntries with the same semester ID.
I am not sure whether I should have a third model called Schedule that brings together the info in the ClassEntries and Classes models for the user at hand. I originally wrote this functionality in PHP and I simply used a MySQL JOIN to gather the necessary information. In Rails it seems that there should be a better way to accomplish this.
What would be the best way of going about this in Rails?
Many thanks
So, what you are looking for is pretty much associations in Rails.
You would have the following:
def User < ActiveRecord::Base
has_many :course_entries
has_many :courses, :through => :class_entries
end
def CourseEntry < ActiveRecord::Base
belongs_to :user
belongs_to :course
end
def Course < ActiveRecord::Base
has_many :course_entries
has_many :users, :through => :class_entries
end
With those associations set up, Rails would allow you to do such things like
some_user.courses or some_course.users and it will make the joins through CourseEntry for you.
Let me know if this helps. If you need me to go more in depth let me know.

Many-to-one bidirectional relation in Rails

I'm begininng with Rails and I'm getting kind of confused with the relations.
The problem is quite simple, I have a Station class representing a train station, and a Shop class. I'd simply want the shop to have a station, representing the closest train station, so I guess it would be a many-to-one relation.
Without any ORM I'd just add a foreign key of the station in the shop table. After looking up about rails relations, I ended up with
class Shop < ActiveRecord::Base
belongs_to :station
end
class Station < ActiveRecord::Base
has_many :shop
end
As properly speaking, the shop does not really BELONG to a station I'm finding this kind of strange, so I'd like to know if this is the right way to proceed or if I'm getting confused.
Thank you in advance.
This is the right way to proceed. "Belongs to" simply means "has a foreign key to" - it doesn't necessarily mean that this is a sensible way of describing the relationship in real terms.
As posted, the code won't quite work - you need to pluralize the has_many side; i.e.:
class Station < ActiveRecord::Base
has_many :shops
end
You can test the relationship actually works by firing up the rails console ('rails c') from your application folder and the experimenting with creating objects. Assuming you've created the corresponding tables, you should be able to do things like:
station = Station.create
shop = Shop.create
shop.station = station
station.shops
station.shops.build
...etc
belongs_to and has_many do not describe either the ownership or the scope or the life cycles for the objects they relate. They just describe the references (the keys) between the objects.
Such references can have their life cycle tied with :dependent, :autosave, etc. options.
Other options such as :read_only reduce privileges of edition from reference to another.
Ownership is a concept that you have to define yourself. For instance: a Post in a forum can "belong" to different users with different privileges. Who is the owner? The admin? The last editor? The one that created the post? For such behaviors, extra definition and mechanics are needed. I recommand you take a look at the CanCan gem for this ( https://github.com/ryanb/cancan ).
class Station < ActiveRecord::Base
has_many :shops, :dependent => "nullify"
end
if your station gets deleted still the shops will be their

Declaring associations in Ruby on Rails

Hopefully I don't get flamed for this one too bad - I've tried my very hardest to find an answer to no avail.
I was wondering if someone could help me figure out how to properly declare associations in Ruby on Rails (3). At the moment, I have 3 models:
#room.rb
class Room < ActiveRecord::Base
has_many :check_ins
end
#check_in.rb
class CheckIn < ActiveRecord::Base
belongs_to :user
belongs_to :room
end
#user.rb
class User < ActiveRecord::Base
has_one :check_in
end
So far, I haven't done any migrations to add foreign_key columns to any of my tables (does Rails do this for you?).
I'm confused about why the command CheckIn.first.user returns nil whereas the command User.first.check_in returns SQLite3::SQLException: no such column. The same happens with respect to CheckIn.first.room and Room.first.check_ins, respectively. What would I need to do in order to have User.first.check_in return the CheckIn object associated with the first user and Room.first.check_ins return the set of CheckIns associated with the first Room?
Any help would be GREATLY appreciated.
Charlie
How did you originally generate these models? Did you just make the model files, or did you use rails' model generator (which also generates a migration for you)?
#room.rb
class Room < ActiveRecord::Base
has_many :check_ins
end
#check_in.rb
class CheckIn < ActiveRecord::Base
belongs_to :user
belongs_to :room
end
#user.rb
class User < ActiveRecord::Base
has_one :check_in
has_one :room, :through => :check_in
end
In db/migrations/34612525162_something_something.rb You need to make sure you have migrations that set these tables up for you. The easiest way for you to do it would be to run this command in a console, modify the commands to use the fields you want, the *_id fields are required for your associations to work:
rails generate model user name:string email:string otherfield:integer
rails generate model check_in user_id:integer room_id:integer
rails generate model room number:integer
Note that these will also generate model files for you, since you already have these 3 model files it'll ask you if you want to overwrite them, or skip the files. You can skip them and it should be just fine. If you already had the migrations for part of the data in these models then you can just add the user_id and room_id fields to the check_in model by running this generator instead:
rails generate migration AddIdsToCheckIn user_id:integer room_id:integer
For rails 2.3.x replace rails generate with script/generate. Next you can inspect your migration(s) by opening the files up in db/migrate.rb and modify them there if you need to. Finally, run the migrations:
rake db:migrate
And it should work out for you. Note that I added a has_one, :through => relationship to User - this is so that you can do #user.room without having to make 3 chains: #user.check_in.room
You need to add the migrations yourself (or if you aren't live, you can modify an existing migration). Basically, you need to give the belongs_to side of the relationship a foreign key.
There is absolutely no reason to get flamed don't worry :) It seems here that you are trying to do a one to many association, but you're actually doing a many to many one.
If a user has one check in, it means that he/she has one room. So, you could just have :
user has_one room
room belongs to user
and room has a user_id.
Hope that helps :)

Resources