Say I have a model Post and a model Author, and an author has_many posts. Post also has a 'state' attribute and a scope, where I can filter by Post.where(state: 'active'). In RailsAdmin, I'd like to have two fields in the Author page: posts and active_posts, where active_posts are scoped to only associated posts with an active state. I've tried the following:
config.model 'Author' do
field :posts
field :active_posts do
formatted_value do
bindings[:object].posts.active
end
end
end
Obviously, this won't work because this just shows an ActiveRecord Association as the formatted value.
I've also tried defining an active_posts method on the Author model, and just calling field :active_posts for config.model 'Author', but that just returns an association as well.
On the other hand, something like on the wiki (https://github.com/sferik/rails_admin/wiki/Associations-scoping) for association scoping isn't working at all (the posts field is showing all associated posts, not posts within the scope). Additionally, this method would only give me one Posts field that would only show active posts.
config.model 'Author' do
field :posts do
associated_collection_cache_all false
associated_collection_scope do
Proc.new { |scope|
scope = scope.where(state: 'active')
}
end
end
end
Is there an alternative method?
I ended up simply creating a list of links:
field :active_posts do
formatted_value do
posts = bindings[:object].posts.active
posts.collect { |p| "<a href='/admin/post/#{p.id}'>#{p.title}</a>" }.join(", ").html_safe
end
end
I wish there were a prettier way, but this works too.
Related
I have a Post model combined with gem Acts_as_Taggable_on.
I would like to display all posts with all their tags, but the tags should be sorted by number of their use (number of posts tagged with certain tag).
To do that I looped through ActiveRecord_Relation and did a sort on Tags column:
def index
temp_posts = Post.all.order('updated_at DESC')
temp_posts.each_with_index do |temp_post, index|
temp_posts[index].tags = temp_post.tags.sort_by {|tag| -tag.taggings_count}
end
#show = temp_posts.first.tags.sort_by {|tag| -tag.taggings_count} # according to this control output it should work
#posts = temp_posts
end
When looking through the control output #show, the tags are sorted as required, but they are not saved into the temp_posts variable. The output is thus unsorted.
What can I do to 'save' the changes I made in the loop?
Since you have Tag#taggings_count, you can order your associations by it. I don't know if this will conflict with what ActsAsTaggable is up to, but this is what it would look like in vanilla Rails. Perhaps ActsAsTaggable has some options to accomplish the same thing.
class Post < ActiveRecord::Base
has_many :taggings
has_many :tags, through: :taggings, -> { order(taggings_count: :desc) }
end
For more details, see Scopes for has_many.
If you don't want the order applied globally, andrykonchin's idea is a good one. Write a Post#sorted_tags method and you can access it on the Post when you want it. Memoizing it into an instance variable will prevent extra database queries.
class Post < ActiveRecord::Base
def sorted_tags
#sorted_tags ||= tags.sort_by(&:taggings_count).reverse
end
end
The problem was in the end only with using an invalid variable for saving the sorted tags.
Acts as Taggable on uses variable tag_list to store the tags associated with the Tag model. Instead I have wrongly used variable tags.
The full correct version of my code:
def index
temp_posts = Post.all.order('updated_at DESC')
temp_posts.each_with_index do |temp_post, index|
// CHANGE: temp_posts[index].tags => temp_posts[index].tag_list
temp_posts[index].tag_list = temp_post.tags.sort_by {|tag| -tag.taggings_count}
end
#posts = temp_posts
end
I'm working on implementing a tagging system and I'm having problem querying for tagged objects with a scope.
For example, I would like to find all the user's items with a certain tag. With a class method I can currently find all the objects:
def self.tagged_with(name)
Tag.find_by_name(name).items
end
However, this has a problem. If I were to do something like: current_user.items.tagged_with(name) won't this existing method return ALL the items and not just items owned by the current_user? I suppose this is a simply querying issue but I can't figure out how to change a class method into something called on a collection. I have tried going the opposite way, to get a the collection through the tags, something like... tag.items.where(:user_id => current_user.id) but in this case, it's a many-to-many relationship and I haven't been able to get on thumb on this either.
What's the proper way to restrict a query like this?
Create an association on your User class that points to your Tag class.
class User < ActiveRecord::Base
has_many :tags
end
Then you can do:
current_user.tags.where(...)
If you don't already have an association in place, you'll need to create a migration to have the tags table reference your users table with a foreign key.
I think this will help you:
class Account < ActiveRecord::Base
has_many :people do
def find_or_create_by_name(name)
first_name, last_name = name.split(" ", 2)
find_or_create_by_first_name_and_last_name(first_name, last_name)
end
end
end
person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
person.first_name # => "David"
person.last_name # => "Heinemeier Hansson"
So, basically you can define your method tagged_with directly into the association!
This example is took from the documentations ActiveRecord::Associations
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!
I have a few scenarios where I would like to do as little DB calls as much as possible via eager loading, but I have not been able to do it well.
Given the 2 scenarios below, how can i change my RABL to do as little calls as possible?
OBJECT MODEL:
Posts
-> belongs_to user
-> has_many: Comments
-> Comment belongs_to user
-> has_many: Tags
-> Tag belongs_to user
RABL (Both of these will cause the DB to do many individual calls)
node(:comments) do |p|
p.filtered_comments(#user)
end
child :tags do
attribute :text
child :users do
attribute :nickname
end
end
CONTROLLER QUERY
Post.includes(user, comments, tags)...
POST.RB
def filtered_comments
comments = self.comments.where(:blocked=>false).all
json = Rabl::Renderer.json(comments, 'comments/list', view_path: 'app/views')
JSON.parse(json).map do |c|
c['comment']
end
end
Usually, the controller defines the object that rabl iterates across, say a #user.
So in the controller, I normally eager-load relations, such as permissions and articles like so: #user = User.find(1).includes(:permissions, :articles), and respons with said user object like this: respond_with #user.
Then, in the rabl file, I have something like:
# some_file.json.rabl
object #user
child :permissions do
attributes :name
end
node :first_article do |u|
u.articles.first
end
This fixed my version of the chatty view files.
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