Associations between three models - ruby-on-rails

I have Article model which should have one gallery, and every gallery should have many pictures. Associations between Article-Gallery, Gallery-Picture models work properly, but I have no idea what I'm doing wrong with Article-Picture association. I've attached my code below.
Article.rb model
class Article < ActiveRecord::Base
belongs_to :gallery
has_many :pictures, through: :galleries
end
Gallery.rb model
class Gallery < ActiveRecord::Base
has_many :pictures, dependent: :destroy
has_many :articles
end
Picture.rb model
class Picture < ActiveRecord::Base
belongs_to :gallery
has_many :articles, through: :galleries
end
Schema.rb
ActiveRecord::Schema.define(version: 20150829181617) do
create_table "articles", force: :cascade do |t|
t.string "title"
t.text "content"
t.integer "author_id"
t.integer "language_id"
t.integer "category_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "gallery_id"
end
add_index "articles", ["gallery_id"], name: "index_articles_on_gallery_id"
create_table "galleries", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "pictures", force: :cascade do |t|
t.string "image"
t.integer "gallery_id"
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end

In Rails 4 you can certainly state that an Article:
belongs_to :gallery
has_many :pictures, :through => :gallery
... and that a picture ...
belongs_to :gallery
has_many :articles, :through => :gallery
... allowing you to do both:
#picture.articles
... and ...
#article.galleries
... with both of those being executed as a single query joining through the galleries table.

David's answer is right, belongs_to does work for through associations in Rails 4.
class Article < ActiveRecord::Base
belongs_to :gallery
has_many :pictures, through: :gallery # not :galleries
end
The thing to remember is that the through part of a has_one or has_many is referring to an association, not another model. This is important when you're doing trickier associations.

Related

Unable to count records for associated model NoMethodError

I tried to multiple ways to get the count of all job applications for the job model, that an employer has. When it comes to one job in particular (job.job_applications.count), it works. You can actually count the applications for that job in question. When you try to sum all job applications for all jobs. I may have overlooked something while setting up the relationships. I receive the error below:
line of code that breaks: <%= current_employer.jobs.job_applications.count %>
undefined method job_applications' for #<Job::ActiveRecord_Associations_CollectionProxy:0x0000000011066998>
The code that I wrote beforehand, is below:
schema
job
create_table "jobs", force: :cascade do |t|
t.string "job_title"
t.text "required_exp"
t.text "job_description"
t.text "job_requirements"
t.bigint "employer_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "slug"
t.bigint "visit_id"
t.index ["employer_id"], name: "index_jobs_on_employer_id"
t.index ["slug"], name: "index_jobs_on_slug", unique: true
end
job applications
create_table "job_applications", force: :cascade do |t|
t.bigint "user_id"
t.string "user_resume_link"
t.string "user_contact_time"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "user_first_name"
t.string "user_last_name"
t.bigint "job_id"
t.index ["job_id"], name: "index_job_applications_on_job_id"
t.index ["user_id"], name: "index_job_applications_on_user_id"
end
job.rb
class Job < ApplicationRecord
belongs_to :employer
has_many :job_applications, dependent: :destroy
has_one :job_category
end
job_application.rb
class JobApplication < ApplicationRecord
belongs_to :user
belongs_to :job
end
employer.rb
class Employer < ApplicationRecord
has_many :jobs, :dependent => :destroy
end
user.rb
class User < ApplicationRecord
has_many :job_applications, dependent: :destroy
has_many :jobs, :through => :job_applications
end
class Employer < ApplicationRecord
has_many :jobs, :dependent => :destroy
has_many :job_applications, through: :jobs
end
And then just call current_employer.job_applications.count

ActiveRecord -- user has_many events and belongs to those events as member

I want to set up a relationship where users can start events and then also join them as members. I figured this uses a many to many relationship, but it also has a simple belongs_to relationship from Event to User.
class User < ActiveRecord::Base
has_secure_password
has_many :events # => Owning
has_many :events, through: :members # => Joining
end
class Event < ActiveRecord::Base
belongs_to :user # => event owner
has_many :users, through: :members # => joining users
end
class Member < ActiveRecord::Base
belongs_to :user
belongs_to :event
end
But I'm having trouble getting queries like Event.first.members to work... I can't figure out what's preventing me from using these methods. Am I thinking of these relationships the wrong way? My Schema.rb:
ActiveRecord::Schema.define(version: 20161205220807) do
create_table "events", force: :cascade do |t|
t.string "title"
t.string "location"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "events", ["user_id"], name: "index_events_on_user_id"
create_table "members", force: :cascade do |t|
t.integer "user_id"
t.integer "event_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "members", ["event_id"], name: "index_members_on_event_id"
add_index "members", ["user_id"], name: "index_members_on_user_id"
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.integer "age"
t.string "password_digest"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
Updated to fix my mistake
class User < ActiveRecord::Base
has_secure_password
has_many :members
has_many :events, through: :members
end
class Event < ActiveRecord::Base
#belongs_to :user # => event owner
has_many :members
has_many :users, through: :members
class Member < ActiveRecord::Base
belongs_to :user
belongs_to :event
end
If this is giving you a stack level too deep error im guessing the issue is elsewhere. Your logs might have more info.

Rails 4, specify custom foreign key in has_many through join table?

I would like to specify a custom foreign key in the join table for a has_many through association in my rails 4 app. Please see below, what I have so far...
When I execute user.team_memberships.create in the rails console, I receive this error:
ActiveRecord::UnknownAttributeError: unknown attribute 'user_id' for TeamMembership.
users.rb
class User < ActiveRecord::Base
has_many :team_memberships
has_many :teams, through: :team_memberships
teams.rb
class Team < ActiveRecord::Base
has_many :team_memberships
has_many :members, through: :team_memberships
team_membership.rb
class TeamMembership < ActiveRecord::Base
belongs_to :member, class_name: 'User', foreign_key: 'member_id'
belongs_to :team
schema.rb
create_table "team_memberships", force: :cascade do |t|
t.integer "member_id"
t.integer "team_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "team_memberships", ["member_id"], name: "index_team_memberships_on_member_id", using: :btree
add_index "team_memberships", ["team_id"], name: "index_team_memberships_on_team_id", using: :btree
create_table "teams", force: :cascade do |t|
t.string "name"
t.integer "team_snap_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name"
t.integer "teamsnap_uid"
t.string "teamsnap_access_token"
end
You'll need to add foreign_key: 'member_id' to the has_many declaration on User. You can also remove the foreign_key option on the belongs_to in TeamMembership - Rails will infer this automatically.
user.rb
class User < ActiveRecord::Base
has_many :team_memberships, foreign_key: 'member_id'
has_many :teams, through: :team_memberships
team_membership.rb
class TeamMembership < ActiveRecord::Base
belongs_to :member, class_name: 'User'
belongs_to :team
You need to specify foreign key in both models
class User < ActiveRecord::Base
has_many :team_memberships, foreign_key: 'member_id'
has_many :teams, through: :team_memberships
end
and
class TeamMembership < ActiveRecord::Base
belongs_to :member, class_name: 'User', foreign_key: 'member_id'
belongs_to :team
end
if you do not add foreign key in user model then you will not be able to create team membership for a user similarly if you will not specify foreign key in team membership then you will not able to get user of a team membership.

Has many associations error when not using default column name

I cannot make good associations when the foreign key has not the default name.
I would like to access to all subjects which belongs_to one participant (foreign key = questioner_id).
It raise me an error
p = Participant.first
p.subjects
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: subject_participants.participant_id: SELECT "participants".* FROM "participants" INNER JOIN "subject_participants" ON "participants"."id" = "subject_participants"."subject_id" WHERE "subject_participants"."participant_id" = ?
Why does it looks for subject_participants.participant_id ? It's just a has_many association, I don't think that subject_participants table should be called in this case...
interested_id and questioner_id are from the same model but not the same role. One has to go through subject_participants table and the other has to go directly in subjects table
My models :
participant.rb
class Participant < ActiveRecord::Base
has_many :subjects, foreign_key: "questioner_id", class_name: "Participant" #questioner
has_many :subjects, through: :subject_participants, foreign_key: "interested", class_name: "Participant" #interested
has_many :subject_participants
has_many :conference_participants
has_many :conferences, through: :conference_participants
end
subject.rb
class Subject < ActiveRecord::Base
validates_presence_of :title, :questioner, :conference, :description
has_many :subject_participants
has_many :interested, through: :subject_participants, :class_name => "Participant" #interested
belongs_to :questioner, :class_name => "Participant"
belongs_to :conference
end
subject_participant.rb
class SubjectParticipant < ActiveRecord::Base
validates_presence_of :interested_id, :subject_id
belongs_to :interested, :class_name => "Participant"
belongs_to :subject
end
schema.rb
create_table "participants", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
end
add_index "participants", ["email"], name: "index_participants_on_email", unique: true
add_index "participants", ["reset_password_token"], name: "index_participants_on_reset_password_token", unique: true
create_table "subject_participants", force: :cascade do |t|
t.integer "interested_id"
t.integer "subject_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "subjects", force: :cascade do |t|
t.string "title", null: false
t.text "description"
t.integer "questioner_id", null: false
t.integer "conference_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Change your participant.rb to
class Participant < ActiveRecord::Base
.....
has_many :subject_participants,class_name: "SubjectParticipant", foreign_key: "interested_id"
end
You make me find the solution, thanks for help :
participant.rb
class Participant < ActiveRecord::Base
has_many :subject_participants, class_name: "SubjectParticipant", foreign_key: "interested_id"
has_many :subjects_interested_in, through: :subject_participants, :source => "subject"
has_many :subjects, foreign_key: "questioner_id"
has_many :conference_participants
has_many :conferences, through: :conference_participants
end
subject.rb
class Subject < ActiveRecord::Base
validates_presence_of :title, :questioner, :conference, :description
has_many :subject_participants
has_many :interested, through: :subject_participants #interested
belongs_to :questioner, class_name: "Participant"
belongs_to :conference
end

How do I write this has_many_to_many dependency?

I am trying to create a has many to many relationship between a Goal model. A goal can have dependent goals and a goal can have others goals that rely on it.
So far I have come up with the below, but it does not seem to be working.
class Goal < ActiveRecord::Base
belongs_to :goal_status
belongs_to :goal_type
has_many :users, through: :user_goals
has_many :user_goals
has_many :dependers, class_name: 'GoalDependency', foreign_key: :dependee_id
has_many :dependees, class_name: 'GoalDependency', foreign_key: :depender_id
has_many :dependencies, through: :dependees
has_many :depending, through: :dependers
validates_presence_of :goal_status_id, :goal_type_id
end
class GoalDependency < ActiveRecord::Base
belongs_to :dependee, class_name: 'Goal', foreign_key: 'dependee_id'
belongs_to :depender, class_name: 'Goal', foreign_key: 'depender_id'
end
Schema
create_table "goal_dependencies", force: :cascade do |t|
t.integer "dependee_id"
t.integer "depender_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "goals", force: :cascade do |t|
t.integer "goal_status_id"
t.integer "goal_type_id"
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
I am getting the error
Could not find the source association(s) "dependency" or :dependencies in model GoalDependency. Try 'has_many :dependencies, :through => :dependees, :source => <name>'. Is it one of dependee or depender?
I have tried putting in a couple of different values as the source, but nothing is working. I am not really familiar that much with using source.
I would guess this is possible in rails. Any ideas?
After taking #Pavan's advice I changed the language around and managed to get it working that way. See the code below.
class Goal < ActiveRecord::Base
belongs_to :goal_status
belongs_to :goal_type
has_many :users, through: :user_goals
has_many :user_goals
has_many :parent_goals, class_name: 'GoalDependency', foreign_key: :parent_id
has_many :child_goals, class_name: 'GoalDependency', foreign_key: :child_id
has_many :children, through: :child_goals
has_many :parents, through: :parent_goals
validates_presence_of :goal_status_id, :goal_type_id
end
class GoalDependency < ActiveRecord::Base
belongs_to :parent, class_name: 'Goal', foreign_key: 'parent_id'
belongs_to :child, class_name: 'Goal', foreign_key: 'child_id'
end
Schema
create_table "goals", force: :cascade do |t|
t.integer "goal_status_id"
t.integer "goal_type_id"
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "goal_dependencies", force: :cascade do |t|
t.integer "parent_id"
t.integer "child_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end

Resources