I cannot seem to find any documentation on how to modify the administrate gem's default dashboards in order to customize what gets displayed in the index and show pages. Here's my specific goal:
Given that an Article belongs_to an Author
When I create an Article
I want to see the Author's last name in the dropdown list for the associated field
And once saved, I want to see the Author's last name in the Article's index and show pages
Right now, instead, I get a not-so-useful "Author #4" as the record label. Here's the automatically generated dashboard:
class ArticleDashboard < Administrate::BaseDashboard
ATTRIBUTE_TYPES = {
author: Field::BelongsTo,
id: Field::Number,
title: Field::String,
content: Field::Text,
created_at: Field::DateTime,
updated_at: Field::DateTime,
}.freeze
[snip]
end
The "Customizing Dashboard" documentation page says:
Each of the Field types take a different set of options, which are specified through the .with_options class method.
So I figure that calling with_options on Field::BelongsTo might be the way to go, but what options are available for that field (or for any other, for that matter)?
In administrate, you may customize how a resource is displayed by overwriting the #display_resource method in the resource's dashboard.
All of your dashboards inherit from Administrate::BaseDashboard, which uses the following method to display resources:
def display_resource(resource)
"#{resource.class} ##{resource.id}"
end
You'll want to add something like this to the AuthorDashboard to overwrite the default:
def display_resource(author)
author.last_name
end
Related
Issue: I have installed the friendly_id gem, it is working how it supposed to for my Users table, but for my Listings model, the friendly URL to use a :name instead of :id isn't working.
Here's my Listings controller in a nutshell:
def show
#order = #listing.orders.new()
#as you can see, I have no need for a find because i noticed i didn't need it for the show page to show up correctly - idk
end
private
def set_listing
#listing = Listing.find(params[:id])
end
Tried using Listing.friendly.find(params[:id]) in the set listing as well, and in the def show.
What's confusing to me is that I don't even need something like #listing=Listing.find(params[:id])
I tried using this in the show: #listing=Listing.friendly.find(params[:id]
but it didn't make a difference. I also tried changing the "set_listing" method to include the friendly snippet but that didn't help either - just a shot even though it shouldn't work.
I included this in the model - just like i did for my Users table:
extend FriendlyId
friendly_id :name, use: :slugged
For example, I have my listing, id: 1, name: Listing Test 1, slug: listing-test-1
I go to domain.com/listings/listing-test-1 and I get
Couldn't find Listing with 'id'=listing-test-1
I did everything the same as I did with the Users table but it doesn't seem to work.
Question: Is the fact that I don't call the #listing in the show the issue? If so, is there other ways around this?
It is important that I call the name instead of the ID
I'm using Active Admin addons gem to create pretty tags for my enums. https://github.com/platanus/activeadmin_addons
In the Order model I have:
enum status: %i[pending not_started in_progress completed]
And in the ActiveAdmin model I have:
tag_column(:status, class: :colored_status)
However, I can't find anyway to translate the status to other language. I'm so over this that the solution doesn't even have to involve locales. I just want to change the tag label to something else.
I figured out I could do the following to change the text of the tag, but now the style is gone:
tag_column(:status, class: :status) do |delivery|
I18n.t("activerecord.enums.delivery.statuses.#{delivery.status}")
end
This approach is also not the best because I have to translate it everywhere in the app.
I know it's way too late but what I do in those cases is to use a draper decorator that translates the enum options. So my admin file looks like this:
ActiveAdmin.register Car do
decorate_with CarDecorator
index do
tag_column :human_status
end
end
and in the decorator I define the human_status as follows:
class CarDecorator < Draper::Decorator
delegate_all
def human_status
I18n.t(status, scope: 'activerecord.attributes.car.statuses')
end
end
Additionally in the yml for the translations (es.yml in my case), you need to add the statuses as entries under statuses:
es:
activerecord:
attributes:
car:
statuses:
approved: Aprobado
declined: Rechazado
pending: Pendiente
This way, the column uses the new :human_status instead of :status, which is translated.
You can skip the decorator and implement the method directly in the model, but decorators are the way to go in my opinion, because otherwise models would get a lot of presentation-related logic which doesn't belong there.
Now, regarding the color issue, in ActiveAdmin Addons, the tagged columns automatically render with two css classes, the status_tag one and one per value as <option> (for example status_tag approved). An example is shown here, so you can customize those clases to get your desired colors. However, with tag_column(:human_status, class: :colored_status) it should also work.
I'd like to incorporate a step to check for an existing relation object as part of my model creation/form submission process. For example, say I have a Paper model that has_and_belongs_to_many :authors. On my "Create Paper" form, I'd like to have a authors_attributes field for :name, and then, in my create method, I'd like to first look up whether this author exists in the "database"; if so, then add that author to the paper's authors, if not, perform the normal authors_attributes steps of initializing a new author.
Basically, I'd like to do something like:
# override authors_attributes
def authors_attributes(attrs)
attrs.map!{ |attr| Author.where(attr).first_or_initialize.attributes }
super(attrs)
end
But this doesn't work for a number of reasons (it messes up Mongoid's definition of the method, and you can't include an id in the _attributes unless it's already registered with the model).
I know a preferred way of handling these types of situations is to use a "Form Object" (e.g., with Virtus). However, I'm somewhat opposed to this pattern because it requires duplicating field definitions and validations (at least as I understand it).
Is there a simple way to handle this kind of behavior? I feel like it must be a common situation, so I must be missing something...
The way I've approached this problem in the past is to allow existing records to be selected from some sort of pick list (either a search dialog for large reference tables or a select box for smaller ones). Included in the dialog or dropdown is a way to create a new reference instead of picking one of the existing items.
With that approach, you can detect whether the record already exists or needs to be created. It avoids the need for the first_or_initialize since the user's intent should be clear from what is submitted to the controller.
This approach struggles when users don't want to take the time to find what they want in the list though. If a validation error occurs, you can display something friendly for the user like, "Did you mean to pick [already existing record]?" That might help some as well.
If I have a model Paper:
class Paper
include Mongoid::Document
embeds_many :authors
accepts_nested_attributes_for :authors
field :title, type: String
end
And a model Author embedded in Paper:
class Author
include Mongoid::Document
embedded_in :paper, inverse_of: :authors
field :name, type: String
end
I can do this in the console:
> paper = Paper.create(title: "My Paper")
> paper.authors_attributes = [ {name: "Raviolicode"} ]
> paper.authors #=> [#<Author _id: 531cd73331302ea603000000, name: "Raviolicode">]
> paper.authors_attributes = [ {id: paper.authors.first, name: "Lucia"}, {name: "Kardeiz"} ]
> paper.authors #=> [#<Author _id: 531cd73331302ea603000000, name: "Lucia">, #<Author _id: 531cd95931302ea603010000, name: "Kardeiz">]
As you can see, I can update and add authors in the same authors_attributes hash.
For more information see Mongoid nested_attributes article
I followed the suggestion of the accepted answer for this question and implemented a reject_if guard on the accepts_nested_attributes_for statement like:
accepts_nested_attributes_for :authors, reject_if: :check_author
def check_author(attrs)
if existing = Author.where(label: attrs['label']).first
self.authors << existing
true
else
false
end
end
This still seems like a hack, but it works in Mongoid as well...
I have an app where the User can have multiple roles inside a Room. I want to have a relation/embedding between them where I want to have, besides the normal fields embedded from the User, one more called role. For example:
irb(main):001:0> #user
=> #<User _id: 52c9d44d72616e19bf000000, name: "ranisalt">
And...
irb(main):001:0> #room
=> #<Room _id: 52ca3a7872616e2204000000, name: "Test Room", users: [#<User _id: 52c9d44d72616e19bf000000, name: "ranisalt", role: "admin">, (...)]
(mind the 'role: "admin"')
How is it possible to add this one more field to the user inside the room?
Also, I'm using Facebook authentication, so my user has lots of data that aren't useful for the room, for example provider, oauth_token, oauth_expires_at, etc. How can I strip these fields off when embedding the user? They use most of the space and will quickly fill the space without being useful.
I use Rails 4. If needed, ask for my models, I can show them.
Is there a reason you're denormalizing rooms so aggressively? Instead of rooms holding onto users that magically acquire roles, why not have rooms embed roles that reference users?
class Room
embeds_many :roles
end
class Role
belongs_to :user
field :type
embedded_in :room
end
class User
has_many :roles
end
Nested Attributes
provide a mechanism for updating documents and their relations in a single operation, by nesting attributes in a single parameters hash. This is extremely useful when wanting to edit multiple documents within a single web form.
Please refer http://mongoid.org/en/mongoid/docs/nested_attributes.html#common
Which help you to build relationship across tables.
Thank you
I am using the acts as taggable on steroids plugin and my web app has 4 models:
Category
Entry - acts as taggable and contains the following using single table inheritance
Story
Topic
Category has two important fields:
tags:string - A list of tags associated with this category
primary_tag:string - A single tag that gets assigned to any Topics created for the category
In the site have the following two pages:
The Stories Page for a Category lists all stories tagged with that category's tags.
The Topics Page for a Category lists all Topics AND Stories tagged with that category's tags.
The creation of Stories and Topics is as follows:
When an Editor creates a Story they supply a list of tags to tag the story with.
When a User creates a Topic it is created within the context of a Category. It is automagically tagged with the category's primary_tag upon creation.
I'm trying to define 3 has_many relationships for Category that uses tags to find associated Entries, Stories and Topics. If I were doing it in controller code I would use the following:
#category = Category.find(1)
#entries = Entry.find_tagged_with(#category.tags) # All Entries
#entries = Story.find_tagged_with(#category.tags) # Just Stories
#entries = Topic.find_tagged_with(#category.tags) # Just Topics
I'd like to make this an instance method of Category so that I call it just as follows:
#category.find(1)
#entries = #category.entries # All Entries
#entries = #category.stories # Just Stories
#entries = #category.topics # Just Topics
I can't figure out how/what to specify in the :has_many declaration to specify the Class methods above to do the work using the category instance in question.
I've tried this:
has_many :entries do
def tagged
Entry.find_tagged_with(self.tags)
end
end
But end up with the following error:
>> #category = Category.find(2)
=> #<Category id: 2, title: "Comics", description: "Comics", enabled: true, tags: "comics", created_at: "2009-06-22 13:29:52", updated_at: "2009-07-01 13:44:09", parent_id: nil, image: "", important: true, stories_enabled: true, topics_enabled: true, primary_tag: "comics">
>> #category.entries.tagged
NoMethodError: undefined method `tags' for #<Class:0x22781dc>
I've tried this.tags and reflection.tags but they all complaign about an undefined method, so there's something I definitely don't understand about the scope of the method.
I think you want to use proxy_owner instead of self.
Try this:
has_many :entries do
def tagged
Entry.find_tagged_with(proxy_owner.tags)
end
end
I found a good reference on Association Extensions here.