how to join an associated table with a has_one association - ruby-on-rails

In my Rails app, I only require users to enter email and name upon signup, but then give them the option to provide fuller contact details for their profile. Therefore, I have a User.rb model that has an association with Contact.rb, namely,
User.rb
has_one :contact
Contact.rb
belongs_to :user
Contact.rb has the predictable fields you might expect such as address, postal code etc, but it also stores the province_id for a relation with the Province.rb model, so
Contact.rb
attr_accessible :address, :city, :mobile, :postalcode, :province_id, :user_id
belongs_to :user
belongs_to :province
Province.rb
has_many :contacts
I did it that way (rather than storing the name of the province as a "string" on contact.rb) so that I could more easily (so I thought) categorize users by province.
In the show action of one of the artists_controller, I do the following to check whether the user is trying to sort by province and then call an artists_by_province method that does a search
if params[:province_id]
province = params[:province_id]
province = province.to_i #convert string to integer
#artistsbyprovince = User.artists_by_province(province)
else
#artists = User.where(:sculptor => true)
end
This is the method on the User.rb model that it calls if a province id is passed in
scope :artists_by_province, lambda {|province|
joins(:contact).
where( contact: {province_id: province},
users: {sculptor: true})
}
However it gives me this error:
Could not find table 'contact'
If I make contacts plural
scope :artists_by_province, lambda {|province|
joins(:contacts).
where( contacts: {province_id: province},
users: {sculptor: true})
}
This error
Association named 'contacts' was not found; perhaps you misspelled it?
Can anyone tell me what I'm doing wrong when I'm making this query?
Update: I changed some of the details after posting because my copy and paste had some problems with it
P.S. ignore the fact that I'm searching for a 'sculptor.' I changed the names of the user types for the question.
from schema.rb
create_table "contacts", :force => true do |t|
t.string "firm"
t.string "address"
t.string "city"
t.string "postalcode"
t.string "mobile"
t.string "office"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "province_id"
end

The problem was fixed by using contact (singular) in the join and contacts (plural) in the where clause. I'm guessing 'contact' (singular) reflects the has_one association between User.rb and Contact.rb, whereas 'contacts' is used in the where clause to represent the name of the table, which is always plural.
User.rb
has_one :contact
scope :artists_by_province, lambda {|province|
joins(:contact).
where( contacts: {province_id: province},
users: {sculptor: true})
}

Can you try the following?
scope :artists_by_province, lambda {|province|
joins(contact: {province: { id: province}}).
where(sculptor: true)
}

Related

In a Rails Model, will belongs_to cause a rollback if not defined?

Model:
class UserPosition < ApplicationRecord
belongs_to :user
belongs_to :job_title
end
UserPosition's schema:
t.integer :user_id
t.integer :company_id
t.integer :industry_id
t.integer :department_id
t.integer :job_title_id
t.string :job_title_custom
user_positions_controller.rb
def create
#user_position = UserPosition.find_or_create_by(user_id: current_user.id)
#user_position.update_attributes({
:industry_id => params[:industry_id],
:department_id => params[:department_id],
:job_title_id => params[:job_title_id],
:job_title_custom => params[:job_title_custom]
})
I need UserPosition to either create a record with:
user_id
job_title_custom
OR
t.integer :user_id
t.integer :company_id
t.integer :industry_id
t.integer :department_id
t.integer :job_title_id
Currently, if I try to create a UserPosition with just user_id & job_title_custom
It doesn't work, the logs show ROLLBACK the error message is:
#messages={:job_title=>["must exist"]}
What am I doing wrong here? I think it could be because job_title has a relationship defined in the model but the Rails Guide says that they are optional, so I'm not sure.
Turns out this is a new Rails 5 behavior.
"In Rails 5, whenever we define a belongs_to association, it is required to have the associated record present by default after this change.
It triggers validation error if associated record is not present."
"In Rails 4.x world To add validation on belongs_to association, we need to add option required: true ."
"Opting out of this default behavior in Rails 5. We can pass optional: true to the belongs_to association which would remove this validation check."
Full Answer: http://blog.bigbinary.com/2016/02/15/rails-5-makes-belong-to-association-required-by-default.html

Limit association based on rich relation attributes

first question for me here! Im trying to assign 'key companies' to my users. These are found in a many-to-many rich join table. On this table there are attributes like new, key, active and so forth. I want to assign companies to users in a long list and for that Im using SimpleForm.
Everything is working excepts that I want to filter out and limit the association relation based on the attributes on the rich relation. I have company relations for each user but not all of them are akey-relation or a new-relation for example. I only want the association being key to show up and not touch the other ones. I also want to set the attribute active to true when Im assigning these companies to the users. My code looks like this now:
user.rb
class User < ActiveRecord::Base
has_many :company_user_relationships
has_many :companies, through: :company_user_relationships
company.rb
class Company < ActiveRecord::Base
has_many :company_user_relationships
has_many :users, through: :company_user_relationships
schema.rb
create_table "company_user_relationships", force: true do |t|
t.integer "company_id"
t.integer "user_id"
t.boolean "key"
t.boolean "active"
t.datetime "last_contacted"
t.string "status_comment"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "status"
t.boolean "new"
end
users_controller.rb
def assign_key_companies
User.update(params[:users].keys, params[:users].values)
redirect_to(:back)
end
view
= form_for :user_companies,
url: assign_key_companies_users_path,
html: {:method => :put} do |f|
- users.each do |user|
= simple_fields_for "users[]", user do |u|
tr
td.col-md-4
= "#{user.first_name} #{user.last_name}"
td.col-md-8
= u.association :companies, label: false, collection: #key_company_candidates,
input_html: {data: {placeholder: " Assign key companies"}, class: 'chosen-select'}
= submit_tag "Save key companies", class: "btn btn-success pull-right"
I basically want to only show user.companies.where(key: true) and the SQLCOMMIT to always put the key-field to true when updating the record.
How can i filter out to only affect the associations I want?
I can think of two ways.
First to filter it at the association level
class User < ActiveRecord::Base
has_many :company_user_relationships, -> { where("company_user_relationships.key" => true) }
Or a where
user.companies.where("company_user_relationships.key" => true)
When you call user.companies it actually doing the join table among all three tables, so you could specify the condition like my example.

Trouble with self referential model in Rails

I have a model named User and I want to be able to self reference other users as a Contact. In more detail, I want a uni-directional relationship from users to other users, and I want to be able to reference an owned user of one user as a 'contact'. ALSO, i want to have information associated with the relationship, so I will be adding fields to the usercontact relation (I just edited this sentence in).
I attempted to do this while using the answer to this question as a guide.
Here is the User model:
user.rb
class User < ActiveRecord::Base
attr_accessible(:company, :email, :first_name, :last_name,
:phone_number, :position)
has_many(:user_contacts, :foreign_key => :user_id,
:dependent => :destroy)
has_many(:reverse_user_contacts, :class_name => :UserContact,
:foreign_key => :contact_id, :dependent => :destroy)
has_many :contacts, :through => :user_contacts, :source => :contact
end
I also created the model UserContact as a part of connecting contacts to users:
usercontact.rb
class UserContact < ActiveRecord::Base
belongs_to :user, :class_name => :User
belongs_to :contact, :class_name => :User
end
Here is the create_users.rb migration file i used:
create_users.rb
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :phone_number
t.string :email
t.string :company
t.string :position
t.timestamps
end
end
end
And here is the create_users_contacts.rb migration:
create_users_contacts.rb
class CreateUsersContacts < ActiveRecord::Migration
def up
create_table :users_contacts, :force => true do |t|
t.integer :user_id, :null => false
t.integer :contact_id, :null => false
t.boolean :update, :null => false, :default => false
end
# Ensure that each user can only have a unique contact once
add_index :users_contacts, [:user_id, :contact_id], :unique => true
end
def down
remove_index :users_contacts, :column => [:user_id, :contact_id]
drop_table :users_contacts
end
end
However, for reasons unknown to me, I believe something has gone awry in the linking since on my users index page, I have a column using <td><%= user.contacts.count %></td>, but I get this error from the line when I attempt to load the page:
uninitialized constant User::UserContact
I think the issue may be something to do with the fact that I want to name users associated with another user as contacts, because I cannot find other examples where that is done, and as far as I can tell I am doing everything properly otherwise (similarly to other examples).
The closest similar problem that I found was outlined and solved in this question. The issue was incorrect naming of his connecting model, however I double checked my naming and it does not have that asker's problem.
Any help is appreciated, let me know if any other files or information is necessary to diagnose why this is occurring.
EDIT
After changing usercontact.rb to user_contact.rb, I am now getting this error:
PG::Error: ERROR: relation "user_contacts" does not exist
LINE 1: SELECT COUNT(*) FROM "users" INNER JOIN "user_contacts" ON "...
^
: SELECT COUNT(*) FROM "users" INNER JOIN "user_contacts" ON "users"."id" = "user_contacts"."contact_id" WHERE "user_contacts"."user_id" = 1
EDIT TWO
The issue was that my linking table, users_contacts, was misnamed, and should have been user_contacts! so I fixed it, and now it appears to work!!
You need to rename your usercontact.rb to user_contact.rb
This is naming convention rails autoload works with.

User models with different roles but discrete data?

I'm working on a Rails app where Users can have multiple roles, e.g. Manager, Employee, Admin, etc.
STI doesn't work in this situation, due to multiple roles, but I'd still like to keep role-related data in different tables if at all possible.
So for right now, my schema looks something like this:
create_table :roles do |t|
t.string :name
t.timestamps
end
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email, :default => "", :null => false
t.timestamps
end
create_table :roles_users, :id => false do |t|
t.references :role, :user
end
And my User/Role models both have has_and_belongs_to_many relationships with each other.
So if, for example, I need the Manager to have_many Employees, is that possible with this setup? Is it possible for a User with the Manager role to have a Manager-specific attribute like secret_manager_information? Or do I need to re-think my approach?
Seeing as how Managers need to keep track of Employees (and in general other roles may need to keep track of other special data), I'd say that each role is different enough that they should get their own tables (assuming that you don't have too many roles).
For example, I would create a Manager and an Employee model:
class Manager
attr_accessible :user_id
has_many :employees
end
class Employee
attr_accessible :user_id, :manager_id
belongs_to :manager
end
Any user that is a Manager will have a record in the Manager table with user_id = user.id.
Any user that is an Employee will have a record in the Employee table with user_id = user.id and manager_id = (the id of the corresponding manager record)

Rails + model.rb issues -- rails console error message -- "Object doesn't support #inspect"

EDIT3:
To wit, DO NOT put migration code in your model.rb files!!!
EDIT2: THE QUESTION (?) :
does ANY migration code belong in a model.rb file?
EDIT: Just mentioning extra (system/config/etc) information that I need to share in order to get a good answer to this question from someone (even if it's not you) would be greatly appreciated. (1-ups for good tips on stack overflow optimization strategies)
First of all, here is the command prompt activity:
C:\Users\davo\Desktop\RailsProjects\simple_cms>rails c
Loading development environment (Rails 3.2.3)
irb(main):001:0> subject = Subject.find(1)
←[1m←[36mSubject Load (1.0ms)←[0m ←[1mSELECT `subjects`.* FROM `subjects` WHERE `subjects`.`id` = 1
LIMIT 1←[0m
=> #<Subject id: 1, name: "Initial Subject", position: 1, visible: true, created_at:"2012-05-18 01:00:26", updated_at: "2012-05-18 01:11:21">
irb(main):002:0> subject.pages
(Object doesn't support #inspect)
The basic schema is that we have two models here, page.rb and subject.rb. Subject is the parent of Page, as you will see. Here are the two models.
Guide to viewing this code: I think all that is relevant to this problem in these two models are the has_many and belongs_to tags. And I admit, I feel like there should be some foreign keys here. Should there be foreign keys here? Or is that wrong too?
subject.rb
class Subject < ActiveRecord::Base
# attr_accessible :title, :body
has_many :pages
scope :visible, where(:visible => true)
scope :invisible, where(:visible => false)
scope :search, lambda {|query| where(["name LIKE ?", "%#{query}%"])}
end
page.rb
class Page < ActiveRecord::Base
has_many :sections
belongs_to :subject
# attr_accessible :title, :body
create_table "Pages" do |t|
t.string "name"
t.string "permalink"
t.integer "position"
t.boolean "visible?"
end
end
I'm really new at this, so please forgive me if I didn't give you some piece of information that you need. Please let let know what extra information you need, I'm not sure where the error is coming from but I know this is a model (M....VC) issue. 95% on that one.
You have a migration in your model.
create_table "Pages" do |t|
t.string "name"
t.string "permalink"
t.integer "position"
t.boolean "visible?"
end
Should be in ./db/migrate/{timestamp}_create_pages.rb. This file was generated for you if you did rails g model page
You also need a subject_id column to store the relation to subject
class CreatePages < ActiveRecord::Migration
def change
create_table :pages do |t|
t.integer :subject_id
t.string :name
t.string :permalink
t.integer :position
t.boolean :visible?
t.timestamps
end
end
end

Resources