Associating multiple existing records - ruby-on-rails

I'm creating an application where one user becomes the account_manager of an account. What I want to do is to add other users to the account. A user can only have one account but an account can have many users.
class Account < ActiveRecord::Base
has_many :users
belongs_to :account_manager, :class_name => 'User', :foreign_key => 'account_manager_id'
end
class User < ActiveRecord::Base
has_one :account
What I'm totally stuck on is having a place where the account manager can either select the user from a dropdown, type in their name, or use some other type of selection. If I try to do this in console each new user I add replaces the last instead of adding to it. here is my schema for accounts:
create_table "accounts", force: :cascade do |t|
t.integer "user_id"
t.integer "account_manager_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
I've tried using collection_select but I think that is only for :though associations. I'm also thinking I probably need a join table but I don't know how to set it up. The thing that is tripping me up most is that I won't be creating new objects, I only want to add existing users to existing accounts. I'm just looking for someone who can talk through this with me.

In your User model:
class User < ActiveRecord::Base
belongs_to :account
end
While in your question, you are writing has_one. You can't write has_many in one model, and has_one in other model. There needs to be belongs_to in one model.
Edit:
The model that belongs_to, always saves the foreign keys in its table. So users would save account_id in it. In order to get all the users of an account, you would simply do:
Account.first.users # As an account `has_many` users

Related

Rails JOINS with a twist

I have been trying and failing for 2 days now :) to get a list of ideas (posts basically) with likes. Order Desc preferably.
I have scaffolded ideas and users which work fine.
Likes (socialization gem) gives me the headache.
I can add likes and retrieve them. And I can also find out how many likes a specific idea has: idea.likers(User).count
and find out whether a user likes a specific idea: user.likes?(idea)
But I can't do agregates because of the non-standard field names which prohibit me from making a JOIN.
create_table "likes", force: :cascade do |t|
t.string "liker_type"
t.integer "liker_id" (this is/should be user_id)
t.string "likeable_type"
t.integer "likeable_id" (this is/should be idea_id)
t.datetime "created_at"
end
add_index "likes", ["likeable_id", "likeable_type"], name: "fk_likeables"
add_index "likes", ["liker_id", "liker_type"], name: "fk_likes"
Models:
like.rb - empty
user.rb - acts_as_liker
idea.rb - acts_as_likeable
Is there a way to join likes and ideas eg somehow matching liker_id to user_id? Or shall I rename the fields in the table (liker_id to user_id and likeable_id to idea_id)...? And also add these:
like.rb
belongs_to :user
belongs_to :idea
idea.rb
has_many :likes, dependent: :destroy
user.rb
has_many :likes, dependent: :destroy
Thanks in advance!
To specify a different column as foreign key which gets used in joins, you could add foreign_key: ... option to belongs_to as follows:
# app/models/like.rb
belongs_to :user, foreign_key: :liker_id
belongs_to :idea, foreign_key: :likeable_id
See referenced documentation on belongs_to.
You can also specify join conditions yourself as follows:
Idea.joins('inner join likes on ideas.id = likes.likeable_id').where(...)

Strange query when calling has_one

Problem: Message class with a has_many :through relationship and a has_one relationship both to the User class. Getting a strange query when I try to use the has_one relationship.
I have a Message class with a has_many relationship to a User through message_memberships. Each instance of Message as well as having users linked to it through the has_many through relationship has a creator who is also a User.
Due to laziness I started to log the creators id in a creator_id column on the Message instance (schema below). Every time a message was created I would add the creators id to the column. Every time I wanted to reference the creator I would call User.find(message.creator_id). I was looking in to creating a link between the creator column and a user but I cannot find the right implementation.
The problem I am having is with referencing the :creator_id column in the has_one relationship. I would assume something like this would work
Message.rb
has_one :creator, -> { where id: :creator_id }, class_name: 'User'
But the query that gets called whenever I look for message.creator is this
SELECT "users".* FROM "users" WHERE "users"."message_id" = ? AND "users"."id" = 'creator_id' LIMIT 1 [[nil, 26]]
I am not entirely sure where the WHERE "users"."message_id" = ?. Without it, it looks like the query would be fine. I have no idea how to stop it from happening.
Schema
create_table "messages", force: true do |t|
t.string "title"
t.string "contents"
t.datetime "created_at"
t.datetime "updated_at"
t.string "token"
t.integer "creator_id"
t.string "link"
end
Any help would be greatly appreciated
Try this setup:
class Message
belongs_to :creator, class_name: '::User', foreign_key: :creator_id
end
class User
has_many :authored_messages, class_name: '::Message', inverse_of: :creator
end

Model style for Ruby on Rails application

So currently my application has a model called Vendors. I want each Vendor to have one or many owners, one or many members, and then people who track or follow the Vendor. I'm trying to decide the best layout for this and I've messed around with a few things. All of the Owners, members, and followers would also be coming from my User model. Therefore I'm thinking of making one association table with booleans as to whether the User is a follower, owner, or member.
For clarification - Owner and member would be people who work at the vendor
schema.db
create_table "vendor_relationships", force: true do |t|
t.integer "user_id"
t.integer "vendor_id"
t.boolean "owner"
t.boolean "member"
t.boolean "follower"
t.datetime "created_at"
t.datetime "updated_at"
end
but I'm wondering if this is the best way to go about this when it comes to speed and/or quality of code. Previously I had created separate tables for vendor_owners, vendor_members, and vendor_followers. Then I associated them with a has_many :through relationship, but I'm stuck onto which way is the best.
They would all have separate logic, or rather separate permissions as to what they were allowed to view and edit with respect to the vendor. I looking at cancan and pundit for role based authorization, but I didn't think it really applied for this situation.
A role based authorization could work if I build the schema like this
create_table "vendor_relationships", force: true do |t|
t.integer "user_id"
t.integer "vendor_id"
t.string "role"
t.datetime "created_at"
t.datetime "updated_at"
end
And then check for possible user roles against the string but I'm not sure which of these is the better option.
EDIT 1:
I'm going with the t.string "role" route, but I'm wondering how I can write the code into my model so that the associations with role = "owner" would be accessible by doing Vendor.owners... etc with Vendor.members and Vendor.followers.
This is what my code looks like currently.
has_many :owners, through: :vendor_relationships, class_name: "User", -> { where role: owner }
This for each variation - owners, members, followers. I've also tried using
has_many :owners, through: :vendor_relationships, class_name: "User", conditions: => ['vendor_relationship.role = "owner"']
but everything is giving me syntax errors here. Would appreciate some help thanks.
I think you are on the right track. I would do the following:
Model your owners, members, and followers all as User, but then give them each a role. Start with a single role per user to keep it simple. See: https://github.com/ryanb/cancan/wiki/Role-Based-Authorization
In the many-to-many joins table, I would remove the booleans and instead let your association handle that logic.
has_many :owners, through: :vendor_members, class_name: "User", conditions: {"users.role = 'owner'"}
Then you can just interact with owners/members/followers like so: owners = vendor.owners
I would also consider changing the name of the vendor_members table since one of the types is also members. Maybe something like vendor_relationships

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: Polymorphic User Table a good idea with AuthLogic?

I have a system where I need to login three user types: customers, companies, and vendors from one login form on the home page.
I have created one User table that works according to AuthLogic's example app at http://github.com/binarylogic/authlogic_example. I have added a field called "User Type" that currently contains either 'Customer', 'Company', or 'Vendor'.
Note: each user type contains many disparate fields so I'm not sure if Single Table Inheritance is the best way to go (would welcome corrections if this conclusion is invalid).
Is this a polymorphic association where each of the three types is 'tagged' with a User record? How should my models look so I have the right relationships between my User table and my user types Customer, Company, Vendor?
Thanks very much!
------UPDATE------
Current setup:
#User model
class User < ActiveRecord::Base
acts_as_authentic
belongs_to :authenticable, :polymorphic => true
end
#Customer model (Company and Vendor models are similar)
class Customer < ActiveRecord::Base
has_many :users, :as => :authenticable
acts_as_authentic
end
Is this a valid way to set them up?
This is what it sounds like you are trying to do:
You have users that can be in one of three groups. If that isn't quite right a little clarification would help.
Since AuthLogic only cares about it's special fields, making and using other fields in your user model is no biggy.
Each user might be made up something like this:
#The Authlogic Fields
t.string :username, :null => false
t.string :crypted_password, :null => false
t.string :password_salt, :null => false
t.string :persistence_token, :null => false
#Your field to identify user type
t.integer :user_type
#If your user is a customer, you would populate these fields
t.integer :customer_name
...
#If your user is a company, you would populate these fields
t.integer :company_name
...
#If your user is a vendor, you would populate these fields
t.integer :vendor_name
...
I'm not sure what you mean by "single table inheritance" but if you want to keep information about a vendor in a table separate from the users table (really no reason to unless you are REALLY concerned about performance) you can just link up your models like this:
class User < ActiveRecord::Base
has_many :customers, :companies, :vendors
end
class Customer < ActiveRecord::Base
belongs_to :user
end
class Company < ActiveRecord::Base
belongs_to :user
end
class Vendor < ActiveRecord::Base
belongs_to :user
end
In this case, since a user would have no associations to 2 of the 3 user types, you are pushed into using the has_many association which works nicely with the 0 association case. You would just have to do some checks in your code to make sure you don't double up.

Resources