Liondancer
Liondancer

Reputation: 16469

One to Many relationship in Rails Models

Im learning Ruby on Rails and I am practicing by making a small website. I am trying to make one to many relationship for a few models but I am having some trouble.

Currently I am stuck. I have a Users, ExpensePictures, IncomePictures models.

User model

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :rememberable, :validatable

  has_many :expense_picture, :income_picture
end

ExpensePicture model

class ExpensePicture < ActiveRecord::Base
  belongs_to :user
end

IncomePicture model

class IncomePicture < ActiveRecord::Base
  belongs_to :user
end

I'm stuck on how to actually make these relationships.

I want Users to have a one to many relationship with IncomePicture and ExpensePicture

I'm also not sure how to handle image files inside the model

ex:

User
    - user_id (primary key)
    - user_name

  ExpensePicture
    - picture_id (primary key)
    - user_id (foreign key)
    - ExpensePictureFile

  IncomePicture
    - picture_id (primary key)
    - user_id (foreign key)
    - IncomePictureFile

Upvotes: 3

Views: 6641

Answers (4)

Nesha Zoric
Nesha Zoric

Reputation: 6620

one-to-many association indicates that each instance of the model A can have zero or more instances of another model B and model B belongs to only one model A.

Example of how your models should be:

class User < ApplicationRecord  
  has_many :stories
end

class Story < ApplicationRecord
  belongs_to :user
end

Notice that has_many is always followed by a plural word: has_many :stories. It is also very important to correctly decide which model will have has_many and which will have belongs_to. The second model (Stories) contains one reference to the first model in form of a foreign key.

The second model doesn't know about the first model relation to it, it is not aware if the first model has reference to more than one of them or just to one.

You can read more about rails associations in this article.

Upvotes: 1

Richard Peck
Richard Peck

Reputation: 76774

Associations

What you're looking at is something called ActiveRecord Associations - the backend relational database (ORM - object-relational mapping) system

ActiveRecord is the "glue" which binds your Ruby classes (models) together, allowing you to pull data from a variety of different databases on your system. Any association inside Rails is governed by the ActiveRecord system - meaning if you can set it up correctly, you'll be able to get the associations you want

--

One-to-Many

The one-to-many association is one of the most common with Rails -

enter image description here

It works very simply:

#app/models/user.rb
Class User < ActiveRecord::Base
   has_many :expense_pictures
   has_many :income_pictures
end

#app/models/expense_picture.rb
Class ExpensePicture < ActiveRecord::Base
   belongs_to :user
end

#app/models/income_picture.rb
Class IncomePicture < ActiveRecord::Base
   belongs_to :user
end

This will allow you to call:

@user = User.find params[:id]
@user.income_pictures #-> collection of income pictures :)

This should be what you're looking for, with relevant documentation to help you

--

STI

A bonus for you: Single Table Inheritance

STI's are an advanced feature (so I wouldn't expect you to learn it immediately), but they will certainly help you with your current project. Basically, they allow you to define "parent" / "child" models which inherit from a single table (have a single pictures table, rather than multiple income_pictures and expense_pictures tables):

#app/models/picture.rb
Class Picture < ActiveRecord::Base
   belongs_to :user
end

#app/models/user.rb
Class User < ActiveRecord::Base
   has_many :expense_pictures
   has_many :income_pictures
end 

#app/models/expense_picture.rb
Class ExpensePicture < Picture
end

#app/models/income_picture.rb
Class IncomePicture < Picture
end

Why is this important? Simply, it allows you to just use a single table for both of these models (the pictures table will have a column type to identify the model which saved it)

This is a far DRYer way to accomplish your goal

Upvotes: 5

Tiago Farias
Tiago Farias

Reputation: 3407

As long as to handle images inside your models use Paperclip or Carrierwave (newer gem). Both very simple and easy to do the job.

Also take a look at this railscast about Paperclip and this one about carrierwave, very enlightening.

Besides, what's wrong with your relationship between the models ? Is it not working ? Cause it makes sense. Just use the plurals in your has_many declarations and I think this is it.

Upvotes: 3

Benjamin Bouchet
Benjamin Bouchet

Reputation: 13181

You must separate the declarations, and you must declare them plural versions (with a 's') like this

class User < ActiveRecord::Base
  ...

  has_many :expense_pictures
  has_many :income_pictures
end

It's useful to go through the guide

Upvotes: 1

Related Questions