Rails ActiveAdmin and has_many association - ruby-on-rails

I am fairly new to active_admin, I was wondering if there is a way to achieve the following
I have two models
User
belongs_to :group
Group
has_many :users
I have successfully created pages in activeadmin for groups and users, now what I want is show users that belong to a certain group. I have button manage_members on groups index page which should show members of only that group. Where I can remove members from the group or add more members.
This is what I have been able to do so far
member_action :manage_members do
#group = Group.find(params[:id])
#page_title = "Manage Groups > ##{#group.name}, Edit Members"
end
and a the view app/vies/admin/groups/manage_users.html.arb
table_for assigns[:group].users do
column "Name" do |u|
u.user_id
end
column "email" do |u|
u.user.email
end
column "Created Date" do |u|
u.user.created_at
end
column "OfficePhone" do |u|
u.user.office_no
end
end
This shows the member of the groups, but I have to do all the work on this page to add edit delete a member, I cannot have active_admin filters and other cool stuff here, this is like a custom page,
Is there a way to have an index page (with all goodness of filters batch actions etc) ( like that of users ) but only showing users of a group. Something like a scoped index page which shows on users from a group and I have the same control over that page as any active admin index page ? More like the image below
rather than having to do all that work my self which currently looks like
Very new to active_admin so apologies if there something really straight forward that I am missing.
Thanks

Maybe a filter will do. That would look like (put it in the same file where you put the member_action)
filter :group, :as => :select, :collection => proc { Group.for_select }
The proc is there to make sure changes to groups (adding/removing/..) are immediately reflected to the select-list in the filter. That has to do with class caching in production.
Dont forget to put this scope in your Group model.
scope :for_select, :select => [:id, :name], :order => ['name asc']
Another way is to use scopes. If you have a field in your Group model, like a slug/label that could serve as a method header then you could do something like this in your activeadmin user register block:
Group.all.each do |group|
# note the sanitization of the Group name in the gsub
scope "#{group.name.gsub(/-/,'_')}".to_sym
end
And this in your User model:
Group.all.each do |group|
# note the sanitization of the Group name in the gsub
scope "#{group.name.gsub(/-/,'_')}".to_sym, joins(:group).where("group.name = ?",role.name)
# using joins(:group) or joins(:groups) makes a difference,
# so not sure as I have not tested, but maybe the where should be
# ....where("groups.name = ....
end
It should give you nice buttons above your index views like here: http://demo.activeadmin.info/admin/orders
If you want this for a has_and_belongs_to_many relation, I suggest you take a look at this Rails3 Active Admin - How to filter only records that meet all checked items in collection
Good luck!

Related

Merging parameters rails

in my rails app, a user can select other users they'd like to join a 'party' with. I'm trying to merge the current user's ID with those that the user has selected. This is one way i've tried:
def party_params
params.require(:party).permit(:length, :game_id, user_ids: []).merge("user_ids" => ["user_ids"] + [current_user.id])
end
This comes back with :
Couldn't find all Users with 'id': (0, 17) (found 1 results, but was
looking for 2)
Doing .merge(:user_ids => [current_user.id]) instead results in the party only being created with the current user and removing all the selected users.
I've also tried moving the .merge to a function and calling it on new, but that doesn't seem to work either.
In my models a party has_and_belongs_to_many :users and likewise a user has_and_belongs_to_many :parties
Any help appreciated!
Thanks.
I would do something like this:
def party_params
params.require(:party).permit(:length, :game_id, user_ids: []).tap do |p|
p[:user_ids] << current_user.id
end
end

Only allow several several values for has_many association

I have a homepage for my sports club wich consists of several departments.
At the moment the authorization system is realized by using cancan.
Each department can have multiple users and each user can belong to multiple departments:
#department.rb
has_and_belongs_to_many :users
\user.rb
has_and_belongs_to_many :departments
This works very well. But I want to have a possibility to administrate this association in the User form. There I have a group of checkboxes for each department. This is realized by this line (using simple_form):
<%= f.association :departments,
:as => :check_boxes,
:collection => Department.specific.order('name' => :asc),
:label_method => :name,
:value_method => :id %>
Now I only want to allow to change several values. On client side I can achieve this by simply hiding or disabling some checkboxes. But this is not save on server when I do not check it again.
The checkbox values will be transmitted by an array of ids. There is a huge potential to manipulate ids in this array.
On the server side I would have to check if the current user has the permission to assign a user to the departments. When he has no rights I need to leave this association unchanged.
Is there an elegant way to achieve this?
I am using Rails 4.0 with strong parameters.
Thanks
So your question is basically about server-side validation -- checking if a user can edit an association on the server side?
I'd look at setting dynamic strong params to start with, with a view to integrate into the model if it works:
#app/controllers/your_controller.rb
def update
#item = Model.find(params[:id])
#params = (can? :update, #item) ? "" : "departments: []"
end
private
def controller_params
params.require(:controller).permit(:attributes, #params.to_sym)
end

Ruby on Rails Active Admin - Duplicate Records showing for HABTM

I am designing a basic file manager (the Asset model) in the Active Admin gem. Each Asset HABTM Groups, and vice-versa.
In my active_admin Asset resource I have a filter where I want to be able to
select multiple groups to filter by, so I added:
filter :groups_id, :as => :check_boxes, :collection => proc {Group.all}
All of the groups show up as checkboxes as expected. However, if I have asset_1, asset_2 and I have group_1 assigned to asset_1 and asset_2, and group_2 to asset_2, when I
filter by both roles, asset_2 lists itself twice.
How can I restrict the filter to use only "distinct" or "unique" assets to be returned?
I also have another problem, which is that the filters are not working at all in any of my scopes.
A quick update on Will's answer. I'm running Rails 5.0 and ActiveAdmin 1.0, and clean_search_params returned an error. But this worked instead:
def apply_filtering(chain)
super
#search.result(distinct: true)
end
Thanks!
Active admin read indicates to add
distinct: true
to get unique results.
To apply that to active admin, I'm using doing that like this:
controller do
def apply_filtering(chain)
#search = chain.ransack clean_search_params params[:q]
#search.result(distinct: true)
end
end
has_and_belongs_to_many accepts a :uniq option which ensures that only uniq records will be returned. Setting this in your model should do the trick.
class MyModel
has_and_belongs_to_many :things, :uniq => true
end
... and, a quick addition Alex's answer:
If you want to do this for all controllers in your app, you can add this to an initializer (mine's called active_admin_patches.rb) -
# This guarantees that the results for a filtered #index page search do not appear more than once, on any #index page in the AA app
# TODO: THIS WILL PROBABLY FAIL WITH SOME FUTURE UPDATE, SO BE READY TO UPDATE IT FROM THE LATEST GEM SOURCE
module ActiveAdmin::ResourceController::DataAccess
# Applies any Ransack search methods to the currently scoped collection.
# Both `search` and `ransack` are provided, but we use `ransack` to prevent conflicts.
def apply_filtering(chain)
#search = chain.ransack(params[:q] || {})
# This is the original line
# #search.result
# This is the patch
#search.result(distinct: true)
end
end
I'm not sure why anybody wouldn't want this to be the default behavior, but there's probably a reason. Hmm, maybe for cases where a column of the index view is one of the non-distinct rows. Yeah, that must be it.
Also, there's bound to be a better way to patch this less intrusively, but I'm in a hurry. :-)

how can I get rails_admin to handle associations properly?

Two questions:
1) How can I make a column in the 'list' for a model consist of data from the record's association? In other words, I have a user model and a user has_many posts. I want to simply have a "post count" column in the list. I tried doing:
field :posts do
formatted_value do
value.count
end
end
but that results in a divide by zero error. I even tried doing:
field :posts do
formatted_value do
bindings[:object].posts.count
end
end
but got the same results.
2) How can I filter the listing to a particular scope? For example, I want to make the users post count be a link that is clickable which will show all posts for the given user.
The best I could figure out how to do this was to do:
# note that I created a method post_count to temporarily solve problem #1
field :post_count do
formatted_value do
bindings[:view].link_to value, "/admin/posts?query=#{bindings[:object].id}"
end
end
Which doesn't work very well. Is there a way to instruct rails-admin to do a .where(:user_id => xxx) on the model?
The other thing I wasn't crazy about was having to manually put in 'admin/posts'.. I was trying to see if I could do rails_admin_list_path(:model_name => "posts"). but that didn't seem to work.
You'd probably get a better response on the rails_admin mailing list - http://groups.google.com/group/rails_admin/
For your first question, this should do the trick:
field :posts, :virtual do
formatted_value do
bindings[:object].posts.count
end
end
For your second question, rails_admin now has a filter system - see the "add filter" dropdown at http://demo.railsadmin.org/admin/players . Tapping into that would be a much better method.
rails_admin_list_path(:model_name => "posts") should work, you might have to include Rails.application.routes.url_helpers or similar.
Try adding this to your rails_admin.rb
RailsAdmin.config {|c| c.label_methods << :field_name}
worked for me

Displaying Posts by number of "vote" records in separate table

I am new to Rails so go easy. I have created a blog and also created the ability for users to indicate they "like" a particular post. The way I am implementing this is by using the post table and a separate table 'vote'. When the user clicks the "like" button it sends the record to the 'vote' table with a value of '1' and the particular post id.
I would like to display the "most liked" posts in the sidebar. How do I go about calling such a thing. I would like to display the post_title and the number of 'votes', that is, I would like to somehow query the 'vote' table for the post_id's that have the most records and display them in descending order.
I hope this is an easy question.
There are several ways to accomplish this, but probably the most versatile and Rails-ish would be to create a module with a method to do the ranking, and then have any classes or associations that can be "liked" extend that module.
# lib/likable.rb
#
module Likable
def most_liked (limit = 10)
# This may be possible without a find_by_sql... see the API docs.
find_by_sql("SELECT Posts.*, SUM(votes.count) AS num_votes FROM Posts, Votes WHERE Posts.id = post_id GROUP BY post_id ORDER BY num_votes DESC LIMIT #{limit}")
end
end
# app/models/post.rb
#
require 'likable'
class Post < ActiveRecord::Base
extend Likable
# ...whatever else you've got in here
end
# app/models/user.rb (or any other model with things that can be "liked")
#
require 'likable'
class User < ActiveRecord::Base
has_many :posts, :extend => Likable
# ...the rest of the User class
end
This lets you do things like...
Post.most_liked # => an array of the 10 most liked posts
#some_user.posts.most_liked(5) # => that user's 5 most liked posts
If you needed to later, you could add methods to the module to see, eg, how many votes a particular Post has. You could also change the post_id to a target_id in Vote and make it a polymorphic association, and then you could use your Likable module to vote for anything, not just posts (you would need to generalize the call to find in most_liked if you did that).
This is actually best done by adding a counter cache to the post model, avoiding the database count on every load.
This railscast episode explains how to setup the counter cache.
Assuming you named your counter cache votes_count, you can do this to get the 10 most popular posts from your controller.
#popular_posts = Post.find(:all, :limit => 10, :order => "votes_count DESC")

Resources