Creating User Groups on Rails - ruby-on-rails

I'm creating a rails application in which a user can create a group, add contacts, add a contact to that group and subsequently broadcast information out to the users to a group they have created.
I'm at the third stage where I'm now trying to allow the logged in user to add a contact to the group.
I have three models for many to many relationships:
class UserGroups < ApplicationRecord
belongs_to :user
belongs_to :group
end
class Group < ApplicationRecord
belongs_to :user
has_many: :user_groups
has_many: :users, through: :user_groups
validates :title, presence: true
end
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_one_attached :avatar
has_many :groups, dependent: :destroy
has_many :posts, dependent: :destroy
has_many :user_groups
has_many :users, through: :user_groups
before_create :set_circleid
has_many :contactships, dependent: :destroy
has_many :contacts, -> { where contactships: { status: :accepted }}, through: :contactships
has_many :requested_contacts, -> { where contactships: { status: :requested }}, through: :contactships, source: :contact
has_many :pending_contacts, -> { where contactships: { status: :pending }}, through: :contactships, source: :contact
has_many :blocked_contacts, -> { where contactships: { status: :blocked }}, through: :contactships, source: :contact
has_many :contactships_inverse, class_name: 'Contactship', foreign_key: :contact_id
has_many :contacts_inverse, through: :contactships_inverse, source: :user
def all_contacts
contacts + contacts_inverse
end
def has_contactship?(contact)
#return true if the user is a contact
return true if self == contact
contactships.map(&:contact_id).include?(contact.id)
end
def requested_contacts_with?(contact)
return false if self == contact
#we are going to map requested contacts with list of users to see if they include contact_id
requested_contacts.map(&:id).include?(contact.id)
end
def pending_contacts_with?(contact)
return false if self == contact
pending_contacts.map(&:id).include?(contact.id)
end
def contacts_with?(contact)
return false if self == contact
contacts.map(&:id).include?(contact.id)
end
def contact_request(contact)
#unless the contact is not equal to self and contactship does not already exist
unless self == contact || Contactship.where(user: self, contact: contact).exists?
#transaction means that if one fails they both are rolled back
transaction do
#for user to another user (sent request)
Contactship.create(user: self, contact: contact, status: :pending)
#from another user to user (recieve request)
Contactship.create(user: contact, contact: self, status: :requested)
end
end
def accept_request(contact)
transaction do
Contactship.find_by(user: self, contact: contact, status: [:requested])&.accepted!
Contactship.find_by(user: contact, contact: self, status: [:pending])&.accepted!
end
end
def reject_request(contact)
transaction do
Contactship.find_by(user: self, contact: contact)&.destroy!
Contactship.find_by(user: contact, contact: self)&.destroy!
end
end
end
And a method within my group controller (not sure what to do here):
#for adding a user to a group?
def add_user
#search for the group?
#group = Group.find(params[:id])
#add a user to that group via user_groups? How?
end
schema.rb:
ActiveRecord::Schema.define(version: 2020_06_22_142356) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "active_storage_attachments", force: :cascade do |t|
t.string "name", null: false
t.string "record_type", null: false
t.bigint "record_id", null: false
t.bigint "blob_id", null: false
t.datetime "created_at", null: false
t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
end
create_table "active_storage_blobs", force: :cascade do |t|
t.string "key", null: false
t.string "filename", null: false
t.string "content_type"
t.text "metadata"
t.bigint "byte_size", null: false
t.string "checksum", null: false
t.datetime "created_at", null: false
t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
end
create_table "groups", force: :cascade do |t|
t.string "title"
t.bigint "user_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["user_id"], name: "index_groups_on_user_id"
end
create_table "contactships", force: :cascade do |t|
t.bigint "user_id"
t.bigint "contact_id"
t.integer "status", limit: 2, default: 0
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["contact_id"], name: "index_contactships_on_contact_id"
t.index ["user_id"], name: "index_contactships_on_user_id"
end
create_table "posts", force: :cascade do |t|
t.string "description"
t.integer "user_id"
t.string "thought"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "user_groups", force: :cascade do |t|
t.bigint "user_id", null: false
t.bigint "group_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["group_id"], name: "index_user_groups_on_group_id"
t.index ["user_id"], name: "index_user_groups_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "first_name"
t.string "last_name"
t.string "groupid"
t.text "bio"
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", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "groups", "users"
add_foreign_key "comments", "users"
add_foreign_key "user_groups", "groups"
add_foreign_key "user_groups", "users"
end
How would I go about developing the method so I can successfully add a contact to a group in the console? I'm confused particularly about the method to make that happen, especially because the contacts are not their own model but part of the user model.
Thanks!

You could do
group = Group.find(params[:id])
contact = User.find(params[:user_id])
UserGroups.create(user: contact, group: group)
Naming
You already mention the term contacts in your question, maybe consider naming your association like this. You can specify a class_name attribute to let Rails know the name of the your model class if it doesn't match the association name.
belongs_to :owner, class_name: "User"
has_many: :user_groups
has_many: :contacts, through: :user_groups, class_name: "User"
validates :title, presence: true
https://guides.rubyonrails.org/association_basics.html#options-for-belongs-to
has_many through vs. has_and_belongs_to
You should think also if you really need the UserGroups model or use a has_and_belongs_to association, see from the Rails guide
The simplest rule of thumb is that you should set up a has_many :through relationship if you need to work with the relationship model as an independent entity. If you don't need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship (though you'll need to remember to create the joining table in the database).
https://guides.rubyonrails.org/association_basics.html#choosing-between-has-many-through-and-has-and-belongs-to-many

Related

ActiveRecord How to combine two conditions on a where with a join

I have two tables with a join table in between:
Series
create_table "series", force: :cascade do |t|
t.string "title", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
class Series < ApplicationRecord
include PgSearch::Model
has_many :makers
has_many :creators, through: :makers
has_many :sources, dependent: :destroy
has_many :entries, through: :sources
validates :title, presence: true
pg_search_scope :search_by_title, against: {
title: 'A',
title_en: 'B',
title_en_jp: 'C'
}
end
Maker
create_table "makers", force: :cascade do |t|
t.bigint "series_id", null: false
t.bigint "creator_id", null: false
t.bigint "creator_type", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["creator_id"], name: "index_makers_on_creator_id"
t.index ["series_id", "creator_id", "creator_type"], name: "index_makers_on_series_and_creator_and_type", unique: true
t.index ["series_id"], name: "index_makers_on_series_id"
end
# frozen_string_literal: true
class Maker < ApplicationRecord
extend Enumerize
belongs_to :series
belongs_to :creator
validates :series, uniqueness: { scope: %i[creator creator_type] }
enumerize :creator_type, in: {
author: 1,
artist: 2
}, predicates: true, scope: true
end
Creator
create_table "creators", force: :cascade do |t|
t.string "name", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["name"], name: "index_creators_on_name"
end
class Creator < ApplicationRecord
include PgSearch::Model
has_many :makers
has_many :series, through: :makers
pg_search_scope :search_by_title, against: :name
end
Based on the tables above, I wanted to create a where method, which would find Series for specific makers. The issue is for series that have two makers, usually an author and artist.
So while this code works, to find a Series for a specific creator:
def self.find_by_creators(title, creators)
where(title: title)
.joins(makers: :creator)
.where(
makers: {
creator_type: :author,
creators: { name: creators[:authors] }
}
)
end
when trying to add another where, nothing gets returned:
def self.find_by_creators(title, creators)
where(title: title)
.joins(makers: :creator)
.where(
makers: {
creator_type: :author,
creators: { name: creators[:authors] }
}
)
.where(
makers: {
creator_type: :artist,
creators: { name: creators[:artists] }
}
)
end

Rails model where clause for has_many_through relation

I have the following model setup:
class Project < ApplicationRecord
has_many :assignments
has_many :participents, through: :assignments
end
class Participent < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :assignments
has_many :projects, through: :assignments
end
class Client < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :assignments
has_many :projects, through: :assignments
end
class Assignment < ApplicationRecord
# belongs_to :client
belongs_to :participent
end
In my ParticipentsController I want to find projects that I did not participate to. But my code always returns all projects:
me = current_participent #current logged in user
Project.joins(:participents).where.not(participents: {id: me.id})
Can anyone point me to my mistake?
EDIT: me.id is not nil, it's a number object.
I also can reproduce this in rails console. For more context I post my schema.rb:
ActiveRecord::Schema.define(version: 2019_09_03_173831) do
create_table "articles", force: :cascade do |t|
t.string "title"
t.text "text"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "assignments", force: :cascade do |t|
t.integer "client_id"
t.integer "participent_id"
t.integer "project_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["client_id"], name: "index_assignments_on_client_id"
t.index ["participent_id"], name: "index_assignments_on_participent_id"
end
create_table "clients", 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.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["email"], name: "index_clients_on_email", unique: true
t.index ["reset_password_token"], name: "index_clients_on_reset_password_token", unique: true
end
create_table "participents", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, 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.index ["email"], name: "index_participents_on_email", unique: true
t.index ["reset_password_token"], name: "index_participents_on_reset_password_token", unique: true
end
create_table "projects", force: :cascade do |t|
t.string "title"
t.integer "client_id"
t.datetime "published_at"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["client_id"], name: "index_projects_on_client_id"
end
add_foreign_key "projects", "clients"
end
SOLUTION: https://stackoverflow.com/a/58001556/890270

Can we have multiple foreign key in active record schema?

I have an schema that has person table, email table, phone table and address table.
Person model
class Person< ApplicationRecord
has_many :emails, dependent: :destroy
accepts_nested_attributes_for :emails
default_scope { order(created_at: :desc) }
end
Email Model
class Email< ApplicationRecord
belongs_to :Person
has_one :phone, dependent: :destroy
has_many :address, dependent: :destroy
accepts_nested_attributes_for :phone
accepts_nested_attributes_for :address
end
Phone Model
class Phone< ApplicationRecord
belongs_to :Email
end
Address Model
class Address < ApplicationRecord
belongs_to :Email
end
My schema looks like this
ActiveRecord::Schema.define(version: 2019_06_03_231058) do
create_table "emails", id: :string, limit: 36, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
t.boolean "deleted", default: false, null: false
t.datetime "created_at", precision: 3, null: false
t.datetime "updated_at", precision: 3, null: false
t.string "Person_id", limit: 36
t.index ["Person_id", "created_at"], name: "index_email_on_Person_id_and_created_at"
end
create_table "phones", id: :string, limit: 36, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
t.string "phone_id", limit: 36, null: false
t.boolean "deleted", default: false, null: false
t.datetime "created_at", precision: 3, null: false
t.datetime "updated_at", precision: 3, null: false
t.string "email_id", limit: 36
t.string "person_id", limit: 36
t.index ["email_id", "created_at"], name: "index_phone_on_email_id_and_created_at"
t.index ["person_id"], name: "fk_rails_7119a1d90f"
end
create_table "persons", id: :string, limit: 36, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
t.boolean "deleted", default: false, null: false
t.datetime "created_at", precision: 3, null: false
t.datetime "updated_at", precision: 3, null: false
t.index ["created_at"], name: "index_persons_on_created_at"
end
create_table "address", id: :string, limit: 36, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
t.string "address_id", limit: 36, null: false
t.boolean "deleted", default: false, null: false
t.datetime "created_at", precision: 3, null: false
t.datetime "updated_at", precision: 3, null: false
t.string "email_id", limit: 36
t.string "person_id", limit: 36
t.index ["email_id", "created_at"], name: "index_address_on_email_id_and_created_at"
t.index ["person_id"], name: "fk_rails_485c78b376"
end
add_foreign_key "emails", "persons"
add_foreign_key "phones", "emails"
add_foreign_key "phones", "persons"
add_foreign_key "address", "emails"
add_foreign_key "address", "persons"
end
So if you notice in my schema at very end I have 2 foreign keys for phones and address.
when I insert data email_id is placed in person table but not person_id.
I see same issue in address table as well where email_id is placed but not person_id
I am new to rails Active record concept when through online documentation couldn't really solve this issue any help is much appreciated.

ActiveRecord query with multiple joins not recognizing the relations

I am trying to write an ActiveRecord Query that returns all students enrolled in a certain course with the following query:
def self.students_enrolled_in(course_id)
Student
.joins(:enrollments)
.joins(:sections)
.joins(:courses)
.where(sections: { course_id: course_id })
end
the result in the rails console is:
ActiveRecord::ConfigurationError: Can't join 'Student' to association named 'sections'; perhaps you misspelled it?
it seems that the association is made. what am I doing wrong? does the query actually mean that all the join() statements have to relate back to Student, or should ac trace out the relational links?
Professor show page:
<div class="col-md-8">
<h2 class="card-title"><%= #professor.name %></h2>
<% #courses_taught.each do |course| %>
<div class="card mb-4 card-header">
<img class="card-img-top" src="http://placehold.it/750x300" alt="Card image cap">
<h3 class="card-text"><%= course.title %></h3>
</div>
<div class="card-body">
<% course.sections.enrollments.students.each do |student| %>
<p><% student.name %></p>
<% end %>
</div>
<% end %>
</div>
models:
enrollment
class Enrollment < ApplicationRecord
belongs_to :section
belongs_to :student
end
Student:
class Student < ApplicationRecord
has_many :enrollments
end
Professor:
class Section < ApplicationRecord
has_many :enrollments
belongs_to :professor
belongs_to :course
validates_uniqueness_of :professor_id, scope: :course_id
scope :by_professor_id, ->(prof_id) { where('professor_id = ?', prof_id) }
end
Course:
class Course < ApplicationRecord
enum status: { planning: 0, offered: 1 }
scope :offered, -> { where(status: 1) }
scope :planning, -> { where(status: 0) }
belongs_to :department
has_many :sections
has_many :professors, through: :sections
validates :title, :number, :status, :description, presence: true
validates :description, length: { in: 10..500 }
validates :title, :number, uniqueness: { case_sensitive: false }
def self.search(term)
if term
where('title LIKE ?', "%#{term}%").order('title DESC')
else
order('title ASC')
end
end
def self.taught_by(professor_id)
Course
.joins(:sections)
.joins(:professors)
.where(sections: { professor_id: professor_id })
.select('distinct courses.*')
end
end
Schema:
ActiveRecord::Schema.define(version: 20171013201907) do
create_table "courses", force: :cascade do |t|
t.string "title"
t.text "description"
t.string "number"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "status", default: 0
t.integer "department_id"
t.index ["department_id"], name: "index_courses_on_department_id"
end
create_table "departments", force: :cascade do |t|
t.string "name"
t.text "description"
t.text "main_image"
t.text "thumb_image"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "enrollments", force: :cascade do |t|
t.integer "section_id"
t.integer "student_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["section_id"], name: "index_enrollments_on_section_id"
t.index ["student_id"], name: "index_enrollments_on_student_id"
end
create_table "professors", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "status", default: 0
t.integer "department_id"
t.text "bio"
t.index ["department_id"], name: "index_professors_on_department_id"
end
create_table "sections", force: :cascade do |t|
t.integer "number"
t.integer "max_enrollment"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "professor_id"
t.integer "course_id"
t.string "room"
t.index ["course_id"], name: "index_sections_on_course_id"
t.index ["professor_id", "course_id"], name: "index_sections_on_professor_id_and_course_id", unique: true
t.index ["professor_id"], name: "index_sections_on_professor_id"
end
create_table "students", force: :cascade do |t|
t.string "name"
t.decimal "gpa"
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 "name"
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"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "roles"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
end
Another way to do this is to add some more associations to your Student model:
class Student < ApplicationRecord
has_many :enrollments
has_many :sections, through: :enrollments
has_many :courses, through: :sections
scope :enrolled_in_course, -> (course) { joins(:sections).where(course_id: course.id)
end
You can then find all students enrolled in a course with:
Student.enrolled_in_course(course)
You're over-applying .joins. Try starting from the inside out. First, find the course:
Course.find_by(id: course_id)
Then, find all the sections associated with the course. No need to do a joins here:
Section.where(course: Course.find_by(id: course_id))
Now you do your join:
Student.joins(:enrollments).where(enrollments: {section: Section.where(course: Course.find_by(id: course_id))})
I think that ought to do the trick for you. But, untested. So, give it a go and see if it works.
P.S.: Try posting only the most relevant code. It's not so much fun to sort through a bunch of extraneous stuff.

ActiveRecord strange behaviour "PG::UndefinedColumn" for table with two users_id

I'm facing a strange behaviour with ActiveRecord. First here's my database schema:
and here's my schema.rb file:
ActiveRecord::Schema.define(version: 20160612080318) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "conversations", force: :cascade do |t|
t.integer "user1_id"
t.integer "user2_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "conversations", ["user1_id"], name: "index_conversations_on_user1_id", using: :btree
add_index "conversations", ["user2_id"], name: "index_conversations_on_user2_id", using: :btree
create_table "interests", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "matches", force: :cascade do |t|
t.integer "user1_id"
t.integer "user2_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "matches", ["user1_id"], name: "index_matches_on_user1_id", using: :btree
add_index "matches", ["user2_id"], name: "index_matches_on_user2_id", using: :btree
create_table "messages", force: :cascade do |t|
t.text "content"
t.integer "conversation_id"
t.integer "user_id"
t.datetime "read_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "messages", ["conversation_id"], name: "index_messages_on_conversation_id", using: :btree
add_index "messages", ["user_id"], name: "index_messages_on_user_id", using: :btree
create_table "user_interests", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.integer "interest_id"
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 "first_name"
t.string "last_name"
t.integer "age"
t.string "avatar_url"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
add_foreign_key "messages", "conversations"
add_foreign_key "messages", "users"
add_foreign_key "user_interests", "interests"
add_foreign_key "user_interests", "users"
end
To test my models I've added these lines to my seed.rb file:
sarah = User.create!(email: "sarah#happenstance.co", password: "azertyuiop", first_name: "sarah", avatar_url: "http://i.imgur.com/jOGeCVC.jpg")
alexandre = User.create!(email: "alexandre#happenstance.co", password: "azertyuiop", first_name: "Alexandre", avatar_url: "http://i.imgur.com/2jFvkJu.jpg")
happbot = User.create!(email: "happbot#happenstance.co", password: "azertyuiop", first_name: "Happbot", avatar_url: "https://pbs.twimg.com/profile_images/659236694575616000/aF21-Wxz.jpg")
lisa = User.create!(email: "lisa#happenstance.co", password: "azertyuiop", first_name: "Lisa", avatar_url: "https://ph-avatars.imgix.net/175314/original?auto=format&fit=crop&crop=faces&w=220&h=220")
c1 = Conversation.create!(user1: yoann, user2: sarah)
c2 = Conversation.create!(user1: yoann, user2: alexandre)
c3 = Conversation.create!(user1: yoann, user2: happbot)
c4 = Conversation.create!(user1: sarah, user2: alexandre)
c5 = Conversation.create!(user1: sarah, user2: happbot)
c6 = Conversation.create!(user1: alexandre, user2: happbot)
c7 = Conversation.create!(user1: yoann, user2: lisa)
m1 = Message.create!(user: yoann, conversation: c1, content: "Hey")
m2 = Message.create!(user: yoann, conversation: c2, content: "Hello")
m3 = Message.create!(user: yoann, conversation: c3, content: "What's up?")
m4 = Message.create!(user: sarah, conversation: c1, content: "Salut Yoann")
m5 = Message.create!(user: alexandre, conversation: c2, content: "Yo!")
m6 = Message.create!(user: happbot, conversation: c3, content: "Tu peux pas test")
m7 = Message.create!(user: sarah, conversation: c4, content: "Salut alexandre")
m8 = Message.create!(user: sarah, conversation: c5, content: "Salut Happbot")
m9 = Message.create!(user: alexandre, conversation: c6, content: "Hey!")
m10 = Message.create!(user: alexandre, conversation: c4, content: "Hello")
m11 = Message.create!(user: happbot, conversation: c5, content: "Quoi de neuf ?")
m12 = Message.create!(user: happbot, conversation: c6, content: "Tu fais quoi demain ?")
m12 = Message.create!(user: yoann, conversation: c7, content: "Yo!")
i1 = Interest.create!(name: "books")
i2 = Interest.create!(name: "music")
i3 = Interest.create!(name: "coffee")
UserInterest.create!(user: yoann, interest: i1)
UserInterest.create!(user: yoann, interest: i2)
UserInterest.create!(user: yoann, interest: i3)
Match.create!(user1: yoann, user2: lisa)
Everything works well for the first rake db:seed but if I want to add a match and do another rake db:seed I run into this error:
rake aborted!
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column matches.user_id does not exist
LINE 1: SELECT "matches".* FROM "matches" WHERE "matches"."user_id" ...
^
HINT: Perhaps you meant to reference the column "matches.user1_id" or the column "matches.user2_id".
: SELECT "matches".* FROM "matches" WHERE "matches"."user_id" = $1
/Users/yolo/code/happenstance-app/db/seeds.rb:1:in `<top (required)>'
-e:1:in `<main>'
PG::UndefinedColumn: ERROR: column matches.user_id does not exist
LINE 1: SELECT "matches".* FROM "matches" WHERE "matches"."user_id" ...
^
HINT: Perhaps you meant to reference the column "matches.user1_id" or the column "matches.user2_id".
/Users/yolo/code/happenstance-app/db/seeds.rb:1:in `<top (required)>'
-e:1:in `<main>'
Tasks: TOP => db:seed
(See full trace by running task with --trace)
Here's my user.rb file:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :user_interests, dependent: :destroy
has_many :interests, through: :user_interests, dependent: :destroy
has_many :messages, dependent: :destroy
has_many :matches, dependent: :destroy
def conversations
Conversation.includes(:messages)
.where("user1_id = :id OR user2_id = :id", id: id)
.order("messages.created_at DESC")
end
def other_user(conversation)
conversation.users.include?(self) ? conversation.other_user(self) : nil
end
def unread_conversations
conversations.select { |c| c.unread_messages?(self) }
end
def unread_conversations_count
unread_conversations.count
end
def unread_conversations?
unread_conversations_count > 0
end
def one_avatar_url
avatar_url ? avatar_url : "http://placehold.it/64x64"
end
end
my match.rb file:
class Match < ActiveRecord::Base
belongs_to :user1, class_name: "User"
belongs_to :user2, class_name: "User"
end
I've checked many answers but I don't really see what's wrong with my schema or my models.
Thanks a lot for your help!
You have to tell Rails which foreign key to use for this has_many :matches, dependent: :destroy in the User model. By default this is guessed to be the name of this class in lower-case and “_id” suffixed. So a User class that makes a has_many association will use user_id as the default :foreign_key. But you don't have user_id, you have rather user1_id or user2_id.
This is clear from this AR sql.
LINE 1: SELECT "matches".* FROM "matches" WHERE "matches"."user_id" ...
I don't know your usecase, but either one will solve this:
has_many :matches, dependent: :destroy, foreign_key: :user1_id
# or
has_many :matches, dependent: :destroy, foreign_key: :user2_id
You need to add foreign_keys in the Match model so ActiveRecord knows which user goes through which foreign_key.
class Match < ActiveRecord::Base
belongs_to :user1, class_name: "User", foreign_key: "user1_id"
belongs_to :user2, class_name: "User", foreign_key: "user2_id"
end

Resources