Active Admin nested associations - ruby-on-rails

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.

Related

Active Record includes with a belongs_to and has_many doesn't return associations

I have the following models:
user.rb
class User < ApplicationRecord
has_many :invitations
end
organization.rb
class Organization < ApplicationRecord
has_many :invitations
end
invitation.rb
class Invitation < ApplicationRecord
belongs_to :user
belongs_to :organization
end
I'm trying to query Active Record in the following way:
user = User.find(params[:id])
user.invitations.includes(:organization)
I want to be able to get all invitations for the user and also have the invitations include attributes of their related organization. However, I am only getting the invitation and none of the organization's attributes.
Even if I try:
Invitation.includes(:organization)
I'm still not getting each invitation's associated organization.
Any and all help is greatly appreciated!
includes method provides eager loading. This solves the N + 1 queries problem. You can access the loaded organization like user.invitations.first.organization. There will be no new queries here.
If you want to combine invitation and organization attributes, you can use joins and select.
user.invitations
.joins(:organization)
.select('invitations.*, organizations.foo, organizations.boo as blabla')
#demir has already answered your question.
Just as an addition - probably, you mixed up includes for models with include option of to_json \ as_json.
If you want to return some JSON result (e.g. in your API response), then you can do user.as_json(include: [invitations: { include: :organization }])

Which is the best use of discard (or act_as_paranoid) in rails-admin?

I'm using the gem discard and also rails-admin. I would like to know which is the best approach to list my active users in a shop in rails admin, taking into consideration the users kept
I've created a method in the model shop:
class Shop < ApplicationRecord
include ShopRailsAdmin
has_many :users
def active_users
users.kept
end
end
In rails admin I'm using:
field :active_users do
label 'Users'
end
But I'm receiving an AssociationRelation instead of a CollectionProxy so in the view, the association looks like
#<User::ActiveRecord_AssociationRelation:0x00007f9c34c1f8e0>
Is there another way to do this so I can avoid defining the method in the model shop?
PD: the tag should have been also discard but it does not exist and I could not create it.
Thanks!
You need to define it as an scoped association
class Shop < ApplicationRecord
include ShopRailsAdmin
has_many :users
has_many :active_users, -> lambda {
where(discarded_at: nil)
}, class_name: 'User'
end
I'm assuming you did not personalize the discard_column.
Rails admin should display them right.

rails join table issue with different roles (owner, non-owner)

In my app users can create products so at the moment User has_many :products and Product belongs_to :user. Now I want the product creator product.user to be able to invite other users to join the product, but I wanna keep the creator the only one who can edit the product.
One of the setups I've got in my mind is this, but I guess it wouldn't work, since I don't know how to distinguish between created and "joined-by-invitation" products when calling user.products.
User
has_many :products, through: :product_membership
has_many :product_memberships
has_many :products # this is the line I currently have but think it wouldn't
# work with the new setup
Product
has_many :users, through: :product_membership
has_many :product_memberships
belongs_to :user # I also have this currently but I'd keep the user_id on the product
# table so I could call product.user and get the creator.
ProductUsers
belongs_to :user
belongs_to :product
Invitation
belongs_to :product
belongs_to :sender, class: "User"
belongs_to :recipient, class: "User"
To work around this issue I can think of 2 solutions:
Getting rid of the User has_many :products line that I currently have and simply adding an instance method to the user model:
def owned_products
Product.where("user_id = ?", self.id)
end
My problem with this that I guess it doesn't follow the convention.
Getting rid of the User has_many :products line that I currently have and adding a boolean column to the 'ProductUsers' called is_owner?. I haven't tried this before so I'm not sure how this would work out.
What is the best solution to solve this issue? If none of these then pls let me know what you recommend. I don't wanna run into some issues later on because of my db schema is screwed up.
You could add an admin or creator attribute to the ProductUsers table, and set it to false by default, and set it to true for the creator.
EDIT: this is what you called is_owner?
This seems to be a fairly good solution to me, and would easily allow you to find the creator.
product.product_memberships.where(is_owner?: true)
should give you the creator

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

ActiveAdmin - customising the records that are shown

So I've got an app where users (Devise) have the ability to see either all, or a subset of main model (in this case Schools), depending on whether the user is at branch, region or national level.
Branch belongs_to Region
School belongs_to Branch
What I'd like to do is to be able to wire up the permissions (maybe with a scope) in such a way as to be transparent to ActiveAdmin. The user logs in to ActiveAdmin and is presented with a list of only the schools they're allowed to see.
So I guess this could either be an ActiveAdmin solution or something at a lower level.
Any ideas would be very welcome :)
You could set it up so a user has a polymorphic association to either a school, a branch or a region. If this association is nil it would mean that the user has access to everything (the national level you mentioned).
class User < ActiveRecord::Base
belongs_to :administrates, :polymorphic => true
end
class School < ActiveRecord::Base
belongs_to :branch
has_many :users, :as => :administrates
end
class Branch < ActiveRecord::Base
belongs_to :region
has_many :schools
has_many :users, :as => :administrates
end
class Region < ActiveRecord::Base
has_many :branches
has_many :users, :as => :administrates
end
You can't make it completely transparent to Active Admin as you have to tell Active Admin to use the particular scope. For this you should be able to get by with scope_to inside your ActiveAdmin.register blocks. You have to do a little magic to make scope_to work with a polymorphic association, but it's doable:
ActiveAdmin.register School do
scope_to do
Class.new do
def self.schools
case current_user.administrates
when School
School.where(:id => current_user.administrates_id)
when Branch
School.where(:branch_id => current_user.administrates_id)
when Region
School.where(:branch_id => current_user.administrates.branches.map(&:id))
when NilClass
School.scoped
end
end
end
end
end
This basically means that each time Active Admin will load a school (or a list of schools on the index page), it will scope it through the anonymous class we created inside the scope_to block.
You should be able to implement something similar on the Branch and Region models depending on your requirements.
You should be aware though, that there currently is an open issue when using scope_to with regards to filters and forms showing resources outside the current users scope.
You also need authorization to limit users on a certain level to only see that level and below (e.g. users on a branch level should not have access to regions). For this you should use CanCan.
For info on how to integrate CanCan in Active Admin, see this or this.

Resources