Ruby on Rails multiple associations between two models - ruby-on-rails

Here is my problem:
I have a model User. a User may be admin.
A user can send error report to a pool of reports and this report can be assigned to an admin
How would you link these two models (User/Report) knowing that an admin is a user?
I'd want a report.sender and a report.admin but I can't find how to do so.
Thanks!

You can define the relations like this:
class Report < ActiveRecord::Base
belongs_to :reporter, foreign_key: reporter_id, class_name: 'User'
belongs_to :admin, foreign_key: admin_id, class_name: 'User'
And use them like that:
report.admin # => returns a User object
report.reporter # => also returns a User object

Related

Question about table/models logic before database generation

Im trying to make my first side project using rails to learn would you kindly help me understand this?
The basic idea is to have a betting game where one user generates a new bet that can only be accepted by another user (only 2 competitors assigned for each bet, the creator and the other player).
I'm thinking about 2 tables:
users
bets
Normally I would just have a one to many relationship for the user that created the bet. But I'm confused about the 'competitor' column where another user is also a user with a user_id. How can I express this relationship better than this:  
After thinking it through it doesn't look like a good setup because I'm renaming a column where I'm storing the user_id and having a many to many 'through' model doesn't make sense since it is a "only one competitor can participate in that bet".
I was thinking about a 'one to one through' creating a 'competitors' table like so:
Could you explain to me how to build it in a better way?
Many thanks!
just an idea, you can do this with 2 foreign_keys
so user can be as creator or competitors, you can also makesure that creator_id and competitor_id cannot be same value since user cannot bet with self
class Bet < ActiveRecord::Base
belongs_to :creator, foreign_key: "creator_id", class_name: "User"
belongs_to :competitor, foreign_key: "competitor_id", class_name: "User"
end
class User < ActiveRecord::Base
# as creator to create bet
has_many: creator_bets, foreign_key: :creator_id, class_name: "Bet"
# as competitor to create bet
has_many: competitor_bets, foreign_key: :competitor_id, class_name: "Bet"
end
#user = User.first
#user.creator_bets.build(...)
# this to create bet as creator
#user.competitor_bets.build(...)
# this to create bet as competitor
having a many to many 'through' model doesn't make sense since it is a
"only one competitor can participate in that bet".
Actually it does. Its is in many ways simpler than having muliple assocations pointing to the same table as you don't have to deal with the situation where a user could be in either column which requires something like:
# this gets much messier as you have to deal with more complex problems
Bet.where('bets.user_id = :id OR bets.competitor_id = :id', id: params[:user_id])
A many to many association also gives you the option of removing that limitation later with minimal redesign.
Given the following associations:
# This represents an event that users can bet on
# for example Elon Musk being the first man on mars.
class Event < ApplicationRecord
has_many :bets
has_many :users, through: :bets
end
class User < ApplicationRecord
has_many :bets
has_many :events, through: :bets
end
# This is the "join model" that joins User and Event
# columns:
# - user_id [bigint, fk]
# - event_id [bigint, fk]
# - amount [decimal]
class Bet < ApplicationRecord
belongs_to :user
belongs_to :event
end
You can simply create a bet by:
#event = Event.create!(description: "Elon Musk will be the first man on mars.")
#event.bets.new(user: User.first, amount: 500)
You can of course cap the number of users to two by adding a custom validation or in your controllers. If you only have two you can assume that event.users.first is the creator and event.users.last is the competitor.

Rails 4 return tasks associated with more than one user

Question: How can I return Assignments associates with one of many users in the users array?
I researched the Rails guides and some only posts but I can't figure this out yet.
https://codereview.stackexchange.com/questions/46319/is-there-a-better-approach-to-searching-has-and-belongs-to-many-relations
http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
http://guides.rubyonrails.org/active_record_querying.html#retrieving-multiple-objects-in-batches
I am associating users to assignments two different ways.
1- user "user_id" is the one who creates the assignment
2- The assignment is given to multiple users. Users are associated to assignments using has_and_belongs_to_many :users
Basically each assignment is associated to the user who owns the task.
The assignment is also given to multiple users who will work on it.
I can successfully return all tasks associated with user but not with users.
I'm trying to display only the tasks associated with the current_user (devise)
This works for user:
assignment.rb
class Assignment < ActiveRecord::Base
belongs_to :deliverable
belongs_to :user
has_and_belongs_to_many :users
end
user.rb
has_and_belongs_to_many :assignments
The association are working fine. In the console I can get all the users associated via HABTM to the assignment.
I have a designer dashboard where i only want to display assignments given to the current user.
designer_dashboard controller:
#if I do this I'll get all the assignments:
#assignments = Assignment.all
# but I want to be able to do something like this to get only the assingments associated with the current user via HABTM
#assignments = Assignment.includes(:users).where(["user_ids =?", current_user])
The data isn't modelled to reflect the 2 different types of relationship that exist between users and tasks - task owners and task designers. You've only set up one of them.
You will need to remodel the data and give the relationships more meaningful names.
One way would be to use has_many_through for the association that a Task has with who its assigned to. A Task has_many Designers through AssignedTasks. The association between Task and User can be named as TaskOwner. If you set up both these associations you will be able to get current_user.owned tasks and current_user.assigned tasks which is more clear than referring to user and users.
class Task
belongs_to :task_owner, class_name: "User"
has_many :assigned_tasks
has_many :designers, through: assigned_tasks, class_name: "User"
end
class User
has_many :owned_tasks, class_name: "Task"
has_many :assigned_tasks, foreign_key: designer_id
has_many :tasks, through: :assigned_tasks,
end
class AssignedTask
belongs_to :designer
belongs_to :task
end
You will need to generate some migrations to add the requisite ids.
Also, I seem to remember reading somewhere that Task is a reserved word. You may want to rename task to something else.
Margo answer is correct.
This works in controller:
#assignments = current_user.assignments

Users and Roles for a Post in Ruby on Rails

I have a number of users who have different roles in relation to a numbers of posts. These roles are owner, editor, viewer, none. Each user may only have one role for a post. I have represented this as a has many through relationship in rails as follows:
class User < ActiveRecord::Base
has_many :roles
has_many :posts, :through => :roles
end
class Post < ActiveRecord::Base
has_many :roles
has_many :users, through => :roles
end
class Role < ActiveRecord::Base
attr_accessor :role
belongs_to :users
belongs_to :posts
end
Where the role attribute is used to indicate which type of role the user has on the post.
When setting a new role I cannot simply use the << operator as it wont set the role attribute. What is the preferred way of handling this situation? How can I enforce that there is only one role per user / post combination and enforce this in my Role creation logic?
You can check in the creation of roles for the User , if he already has a role assigned in which case you can skip assigning this role.
unless user.roles.present?
user.roles.create
end
I understand that you want to make sure that no user will have more than one role for a certain post. If this is what you want to achieve then you just need to add uniquness validation to your Role mode
validates :user_id, uniqueness: {scope: :post_id, message: 'User can have one role per post'}
this will ensure that the combination of user_id and post_id will be unique, you can see more on rails guide on validation with scope.

Active Admin nested associations

So I have the following models in my program:
class User < ActiveRecord::Base
has_many :group_members
has_many :groups, through: :group_members
class GroupMember < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
class Group < ActiveRecord::Base
has_many :group_members
has_many :users, through: :group_members
end
And I'm using ActiveAdmin to administer these resources. My groups.rb file in the admin folder looks like this:
ActiveAdmin.register Group do
.
.
belongs_to :user, :optional => true
.
.
end
The problem is, Group is a resource by itself - an admin can manage groups by going to the /admin/groups route - but it also is a resource of users, and I'm able to view the user groups by accessing the /admin/users/:user_id/groups route. However, when I try to create a new group to this user by accessing the /admin/users/:user_id/groups/new page and filling in the form, I get an error:
ActiveRecord::RecordNotFound in Admin::GroupsController#show
Couldn't find Group with id=13 [WHERE `group_members`.`user_id` = 2]
The user id corresponds to my currently logged in user and it is correct, while the group id = 13 corresponds to the id of the newly created group (it is saved in the database correctly), but the association in group_members table is not created. Actually, that's not even exactly what I'm trying to achieve here: the ideal scenario would be to retrieve the list of groups and display it in the new page, so as not to create new groups in this page - only group_members.
How do I proceed in this case? Thanks in advance!
Have you considered removing the
belongs_to
declaration in the Group register block? The interface nesting that this aa dsl statement gives you, looks nice and gives you a clickpath, but, in the end it is merely a decoration on the data(model). Using filters and scopes in the user model could do as well. If you really need aa's belongs_to you will have to create custom edit screens, and custom redirects. Good luck.

Ruby on rails with different user types

I'm trying to build a application that has different kinds of users, I'm using authlogic for user authentication.
So I have one user model that has the required field for authlogic to do its magic. I now want to add a couple of different models that would describe the extra fields for the different kinds of users.
Lets say that a user registers, he would then select his user type, when he is done registering he would be able to add information that is specific for his user model.
What would be the best way to do this? I am currently looking into polymorphic models but I'm not sure that's the best route to take. Any help would be greatly appreciated, thanks.
You can create different profile tables and just tie the profile to the user. So for each user type you can create a table and store the specific info there and have a user_id column to point back to users.
class User < ActiveRecord::Base
has_one :type_1
has_one :type_2
end
class Type1 < ActiveRecord::Base
belongs_to :user
end
class Type2 < ActiveRecord::Base
belongs_to :user
end
Now this isn't very DRY and could lead to problems if you are constantly adding user types. So you could look into polymorphism.
For polymorphism, the users table would define what type the user is (profileable_id and profileable_type). So something like this:
class User < ActiveRecord::Base
belongs_to :profileable, :polymorphic => true
end
class Type1 < ActiveRecord::Base
has_one :user, :as => :profileable
end
class Type2 < ActiveRecord::Base
has_one :user, :as => :profileable
end
Then there is a third option of STI (single table inheritance) for the user types. But that doesn't scale well if the user type fields differ dramatically.
The best approach I saw it here
http://astockwell.com/blog/2014/03/polymorphic-associations-in-rails-4-devise/

Resources