2 references to same model without has_and_belongs_to_many - ruby-on-rails

class Message < ActiveRecord::Base
belongs_to :conversation
belongs_to :user
end
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 :conversations
has_many :conversations, class_name: "Conversation", foreign_key: :user2_id
end
class Conversation < ActiveRecord::Base
belongs_to :user
belongs_to :user2, class_name: "User", foreign_key: :user2_id
has_many :messages
end
spec:
require 'rails_helper'
RSpec.describe Message, type: :model do
it "should be available to 2 users" do
u = User.create(email: 'x#y.com', password: '8888888888')
u2 = User.create(email: 'z#w.com', password: '8888888888')
c = Conversation.create(user_id: u.id, user2_id: u2.id)
expect(u2.conversations.count).to eq 1
expect(u.conversations.count).to eq 1
end
end
It's this line:
expect(u.conversations.count).to eq 1
that fails.
Presumably because of my second has_many
But if I remove it, then expect(u2.conversations.count).to eq 1 fails.
Yes, I explicitly want only 2 users in a conversation. I was trying to avoid doing HABTM.
How do I make this work?

has_many :conversations
has_many :conversations, class_name: "Conversation", foreign_key: :user2_id
You can only have 1 has_many of a set name. Your first has_many is being overwritten by the second as the ruby class loads. I can't think of a better name but you should name one of them different, maybe something to demonstrate the person initiating the conversation.

You could use a has_many :through with a join model. Here is an explanation of how to set that up: http://www.tweetegy.com/2011/02/setting-join-table-attribute-has_many-through-association-in-rails-activerecord/.

Related

Rails associations within same model depending on attribute

I am building a rails app with devise and cancancan and I am trying to create associations within the same model. I have a User model, a role model and an appointments model. The user can have the role of Doctor or Patient. I want to create an association so a Patient can create appointments with the Doctor. I managed to create the associations but I do not know how to make the patient only create appointments with a doctor and only in that way.
My models are like this:
User model
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
belongs_to :role, optional: true
validates :name, :DOB, presence: true
has_many :doctor_user_appointments, class_name: 'Appointment', foreign_key: 'doctor_user_id', dependent: :destroy
has_many :patient_user_appointments, class_name: 'Appointment', foreign_key: 'patient_user_id', dependent: :destroy
before_save :assign_role
scope :doctor_user, -> {where("role_id = ?", 1)
scope :patient_user, -> {where("role_id = ?", 2)
def admin?
role.name == 'Admin'
end
def doctor?
role.name == 'Doctor'
end
def patient?
role.name == 'Patient'
end
def assign_role
self.role = Role.find_by name: 'Patient' if role.nil?
end
end
Role model
class Role < ApplicationRecord
has_many :users, dependent: :restrict_with_exception
end
Appointment model
class Appointment < ApplicationRecord
belongs_to :doctor_user, class_name: 'User'
belongs_to :patient_user, class_name: 'User'
end
With these models, I can create appointments between doctor_user and patient_user, both users, but the associations do not distinguish who has the role "doctor" and who has the role "patient". I have tried with scopes but it is not working.
What I basically want is that doctor_user could only be a user with role "doctor" or user with role_id=1 and a patient_user only could be a user with role_id=2 or role "patient"
Any help would be welcome since I am quite stuck
Thanks in advance
Simply updating with the following lines to your Appointment controller will do the job:
belongs_to :doctor_user, -> { doctor_user }, class_name: 'User'
belongs_to :patient_user, -> { patient_user }, class_name: 'User'
I have fixed the issue by adding these lines to the Appointment model
belongs_to :doctor_user, -> {where("role_id = ?", 1)}, class_name: 'User'
belongs_to :patient_user, -> {where("role_id = ?", 2)}, class_name: 'User'

Querying for values from several models

I have a Course and Lesson models. Course has several lessons. I want to find all the lessons for currently logged in student to generate kind of timetable.
I have a method that returns all the courses that this student is studying. Now I want to get all lessons from all those courses in #courses into #lessons, something like:
def index
#courses = current_student.find_courses
#lessons = #courses.lessons
end
Is it possible to do it somehow simple on one line?
The find_courses method is implemented as following:
def find_courses
Course.where("id IN (?)", StudentAssignment.select("course_id").where('student_id == (?)', self.id))
end
The Models:
class Student < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :student_assignments
has_many :courses, :through => :student_assignments
....
class Lesson < ApplicationRecord
belongs_to :room
belongs_to :teacher
belongs_to :course
....
class Course < ApplicationRecord
has_many :lessons, dependent: :destroy
has_many :teacher_assignments
has_many :teachers, :through => :teacher_assignments
has_many :student_assignments
has_many :students, :through => :student_assignments
...
class Student < ApplicationRecord
has_many :courses
def active_lessions
Lession.joins(course: :students).where(students: {id: self.id})
end
end
In this way you can directly get all active lesssions for current_user
current_student.active_lessions
Try:
#lessons = #courses.flat_map(&:lessons)
It takes each course in #courses list and gets the list of lessons for that course.

Belongs_to alias

I want to get the username of comment author like this
comment.commenter
models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :commenter
belongs_to :commentable, polymorphic: true
end
models/user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates :username, presence: true, uniqueness: true
has_many :comments, as: :commenter
end
When i try to create Comment directly to db using this line of code:
Comment.create(commentable_type: 'Pokemon', commentable_id: 1, content: 'great', commenter: 1)
It throws this error
NameError: uninitialized constant Comment::Commenter
from /var/lib/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/inheritance.rb:158:in `compute_type'
I've read somewhere as: is used only for polymorphic assocciations so that might be the case of my error but couldn't figure out how to get around this problem
I don't think as: is what you are looking for. I think your issue is similar to the issue in belongs_to with :class_name option fails
Try
# user.rb
has_many :comments, foreign_key: "commenter_id"
# comment.rb
belongs_to :commenter, class_name: "User", foreign_key: "commenter_id"
Let's explain a bit before going to the solution, if you write this code:
belongs_to :commentable, polymorphic: true
It implicitly means:
belongs_to :commentable, foreign_key: 'commentable_id', foreign_type: 'commentable_type', polymorphic: true
And
has_many :comments, as: :commentable
It specifies the syntax of polymorphic, that also means the foreign_key is commentable_id and foreign_type is commentable_type so if you want to change commentable to commenter, it is possible, you can do like this:
class Comment < ActiveRecord::Base
belongs_to :commenter, foreign_key: 'commentable_id', foreign_type: 'commentable_type', polymorphic: true
end
class User < ActiveRecord::Base
has_many :comments, as: commentable
end
That 's it!
For detail, please go through has_many and belongs_to documentation

User has_many association not working (error:Could not find the association :user_categories in model Category)

I have three models and here they are when I try to create a has_many. I basically want my users (using devise) to have many categories. And categories to have many users.
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable
has_many :user_categories
has_many :categories, through: :user_categories
acts_as_messageable
def mailboxer_email(object)
email
end
end
userCategory.rb
class UserCategory < ActiveRecord::Base
belongs_to :user
belongs_to :category
accepts_nested_attributes_for :categories
end
Category.rb
class Category < ActiveRecord::Base
has_many :user_categories
has_many :user, through: :user_categories
validates :name, presence: true, length: {minimum: 3, maximum: 25}
validates_uniqueness_of :name
end
when I run category.users << user I get this error:
ActiveRecord::HasManyThroughAssociationNotFoundError: Could not find the association :user_categories in model Category
I can't say for sure what the problem could be, but a few things I could point out:
UserCategory's accepts_nested_attributes_for, does that mean the you want to be able to dynamically create categories?
Category has_many :users, through: :user_categories, not user
You need to follow the Rails file naming conventions, user.rb, user_category.rb and category.rb
These may not be the problem/solution, but I believe they're in the way of resolving the problem.

Rails 4: has_many :through errors

Here is my join model:
class CompanyUser < ActiveRecord::Base
belongs_to :company
belongs_to :user
end
My User model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
ROLES = %w[admin requestor requestor_limited shipping_vendor].freeze
attr_accessor :temp_password
has_many :companies_users
...
end
If I run this in the console:
u = User.first
u.companies
This is the error I am getting:
NameError: uninitialized constant User::CompaniesUser
has_many through relationships should be like this:
In app/models/company.rb file,
has_many :company_users
has_many :users, :through => :company_users
In app/models/user.rb file,
has_many :company_users
has_many :companies, :through => :company_users
In app/models/company_user.rb file,
belongs_to :company
belongs_to :user
If you want to delete the dependent records in company_users table when deleting companies/users,
Add, , :dependent => :destroy at the end of has_many relations in Company and User model.
Hope this helps you..
Thanks.!!
it must be
has_many :company_users
"CompanyUser".tableize => "company_users"
The model shall be either:
class CompaniesUser < ActiveRecord::Base
belongs_to :company
belongs_to :user
end
Or has_many declaration sheel be defined explicitly as:
class User < ActiveRecord::Base
has_many :company_users
end

Resources