Rails ActiveAdmin showing attributes from different model - ruby-on-rails

ActiveAdmin.register User do
index do
column :email
column :first_name
column :surname
end
end
app/models/user.rb
has_many :tasks
app/models/task.rb
belongs_to :user
I have all the attributes in the User model. Is it possible to call those attributes in the Task model. I tried to look up for it but couldn't find anything suitable. For example:
ActiveAdmin.register Task do
index do
column :email
column :first_name
column :surname
end
end

Yes, you can! For example:
ActiveAdmin.register Task do
index do
column "Email" do |task|
task.user.email
end
column "First name" do |task|
task.user.first_name
end
end
end

Related

Rails 4 - How can I map 2 columns of a Model to another column of another model

I want to map 2 columns of the same model (dev_status and test_planning_status) to another model's column (Status.name) and in the UserStory form I want to have a dropdown with values from Status table
I have tried something like this but unable to figure out
Status model is like this
class Status < ActiveRecord::Base
has_many :dev_status, :class_name => 'UserStory', :foreign_key => 'dev_status_id'
has_many :test_planning_status, :class_name => 'UserStory', :foreign_key => 'test_planning_status_id'
end
Currently I have this in models/UserStory
class UserStory < ActiveRecord::Base
validates :us_number, presence: true
validates :team_id, presence: true
validates :dev_status, presence:true
validates :test_status, presence:true
belongs_to :team
CreateUserStories migration is
class CreateUserStories < ActiveRecord::Migration
def change
create_table :user_stories do |t|
t.string :us_number
t.references :team
t.string :dev_status
t.string :test_planning_status
t.integer :tc_count
t.timestamps null: false
end
add_foreign_key :user_stories, :pod
end
My UserStoryController params is
def user_story_params
params.require(:user_story).permit(:us_number, :team_id, :dev_status, :test_planning_status)
end
UserStory _form is
<%= f.label :dev_status,'Dev Status' %>
<%= f.select :status, Status.all.map {|u|[u.status, u.id]},
{include_blank: true} %>
<%= f.label :test_planning_status, 'Test Planning Status' %>
<%= f.select :status, Status.all.map {|u|[u.status, u.id]},
{include_blank: true} %>
The goal should be to call UserStory.dev_status.name to get the dev_status name, and UserStory.test_planning_status.name to get the test_planning_status name.
Your migration should be creating columns dev_status_id (not dev_status) and test_planning_status_id (not test_planning_status).
Use t.references or t.belongs_to in your future migrations.
Above columns should be integers, not strings.
You need to specify belongs_to on the UserStory object for your status fields.
belongs_to :dev_status, class_name: 'Status'
belongs_to :test_planning_status, class_name: 'Status'
Change
validates :test_status, presence:true
to
validates :test_planning_status, presence:true
The two f.select :status in your form need to be changed to f.select :test_planning_status and f.select :dev_status
That should get you pointed in the right direction. Hope it helps!
This sounds like a standard has_many relationship::
class Status < ActiveRecord::Base
# columns id | name | value | other | information | created_at | updated_at
has_many :user_stories
end
class UserStory < ActiveRecord::Base
# columns id | title | value | dev_status_id | test_planner_status | created_at | updated_at
belongs_to :dev_status, class_name: :status
belongs_to :test_planning_status, class_name: :status
end
This would give you the ability to access the following:
#app/controllers/statuses_controller.rb
class UserStoriesController < ActionController::Base
def show
#story = UserStory.find params[:id]
##story.dev_status = gives you dev's details, with status value from Status table
end
end
If you wanted to avoid the law of demeter (IE only have one point to access your data), you'll want to use the delegate method:
#app/models/user_story.rb
Class UserStory < ActiveRecord::Base
delegate :name to: :dev_status, prefix: true
# this will allow you to call #user.dev_status_name
end
If you then wanted to have statuses changed, you'll be able to use the collection_select helper to get it working with the Status objects:
#app/views/user_stories/edit.html.erb
...
<%= f.collection_select :dev_status_id, Status.all, :id, :name, prompt: true %>
<%= f.collection_select :test_planner_status, Status.all, :id, :name, prompt: true %>
--
ActiveRecord
You must remember that models are built, they are just classes. Rails uses an ORM (ActiveRecord) to pull data to populate these classes.
Many people become confused about how models fit into the Rails ecosystem. A model is made of "attributes", which you have to populate, either manually or through the Rails ORM API. IE your User model could have the following:
#app/models/user.rb
class User < ActiveRecord::Base
def will_you_marry_me?
"no"
end
end
#app/views/application.html.erb
Will the user marry?
<%= #user.will_you_marry_me? %>
When you talk about "mapping" columns, what you're really asking is how to call a different table's data to attributes in your model class. For example, if you have User class, how to populate #user.birthday with data from profiles table etc.
The answer to that is to use the relational structure of ActiveRecord. Relational databases simply work with foreign_keys to load data from other tables. For example, you could have profiles table with user_id to get information about a specific user (see the image above).
ActiveRecord makes the process of loading "other table" data very simple. By using the relationships in the API, you can populate data with the likes of has_many etc.

Sortable custom column in ActiveAdmin index view : joins, group and count hell

We're using ActiveAdmin for our admin view, here's the current index code. The Book model retrieves data from a single "books" postgres table :
class Book < ActiveRecord::Base
has_many :stories, class_name: "BookStory"
...
ActiveAdmin.register Book do
index do
column :id
column :title
column :subtitle
column :isbn_13
default_actions
end
...
I would like to add a "stories" column in our index view. A "story" is an action from an user, associated with a book. The stories are stored in a "book_stories" table.
class BookStory < ActiveRecord::Base
belongs_to :user,
belongs_to :book,
From a SQL point of view, this is how I would like to implement the query into ActiveAdmin. This query gives me the wanted result into pgAdmin3 :
SELECT books.id,books.title,books.subtitle,books.isbn_13,COUNT(book_stories.book_id) AS count
FROM books
INNER JOIN book_stories ON books.id = book_stories.book_id
GROUP BY books.id
ORDER BY count DESC
LIMIT 30;
And I really don't know how to implement a sortable "Stories" column into our admin view. By sortable, I mean, being able to sort the book like by their stories count. I managed to show the stories count per book with this code, but the column isn't sortable :
ActiveAdmin.register Book do
index do
column :id
column :title
column :subtitle
column :isbn_13
column :stories do |book|
BookStory.joins(:book).group.where("books.id = #{book.id}").count
default_actions
end
...
Any ideas ?
Take a look on the belongs_to counter_cache option. http://guides.rubyonrails.org/association_basics.html#counter-cache
class BookStory < ActiveRecord::Base
belongs_to :book, counter_cache: true
end
ActiveAdmin.register Book do
index do
column :id
column :title
column :subtitle
column :isbn_13
column :book_stories_count # will be sortable by default
default_actions
end
end
Okay, it's working. Thank you very much ! Here's my code :
Book model :
class Book < ActiveRecord::Base
has_many :stories, class_name: "BookStory"
Book stories model :
class BookStory < ActiveRecord::Base
belongs_to :user, :counter_cache => true
belongs_to :book, :counter_cache => true
Migration file :
class AddBookStoriesCountColumn < ActiveRecord::Migration
def self.up
add_column :books, :book_stories_count, :integer, :null => false, :default => 0
Book.reset_column_information
Book.find_each do |b|
Book.reset_counters b.id, :stories
end
end
def self.down
remove_column :books, :book_stories_count
end
end
Index view :
ActiveAdmin.register Book do
index do
column :id
column :title
column :subtitle
column :isbn_13
column :book_stories_count
default_actions
end
I also created a rake task to manually update the counter if needed :
task :update_counters => [:environment] do
Book.reset_column_information
Book.find_each do |b|
Book.reset_counters b.id, :stories
end
Now, I would like to implement a stories counter for the users. And I believed I'll just have to use the same code up here, just changing "Book" with "User", but I have another problem.
User model :
class User < ActiveRecord::Base
has_many :book_actions
has_many :book_stories
has_many :books, through: :book_stories
but I encouter an error when I try to update / reset the counter
NoMethodError: undefined method `options' for nil:NilClass
Still investigating...

Rails 4 and gem sunspot cannot order correctly

Im getting this error:
Sunspot::UnrecognizedFieldError in SitesController#products
No field configured for Product with name 'created_at'
here is my model Product.rb
class Product < ActiveRecord::Base
searchable do
text :name
text :description
text :specification
string :name
end
end
and here is a mehod in my controller:
def list_all_products
#search = Product.search do
fulltext params[:search]
order_by :created_at, :desc
end
#products = #search.results
end
the name field in my table products is a string. I defined in searchable products both for text and string. Why I still getting an error like this? thank you.
just add time :created_at in your model
class Product < ActiveRecord::Base
searchable do
text :name
text :description
text :specification
time :created_at
string :name
end
end

Showing index to current user, only their information

I have a model Order. In the index, I want the current_user to see the index with only their orders. Order belongs_to :admin_user. AdminUser has_many :orders. I am using activeadmin in my app, if that makes a difference. I am getting this error:
Couldn't find Order without an ID
The line giving the error is the second line in my order controller.(redacted unnecessary info)
index do
#order = Order.where(admin_user_id: current_admin_user.id, order_id: resource.id)
column "ID" do |order|
link_to order.id, admin_order_path(order)
end
column "Proof" do |order|
image_tag order.proof_url(:proof).to_s
end
column "Name" do |order|
link_to order.name, admin_order_path(order)
end
column(:customer, :sortable => :customer_id)
column "Category", :order_category
column "Status", :order_status
column "Priority", :order_priority
column "Due Date", :end_date
default_actions
end
here is my order model requested by #jamesw
class Order < ActiveRecord::Base
attr_accessible :color_back, :color_front, :color_sleeve, :end_date, :name, :start_date, :whiteboard, :customer_id, :order_category_id, :order_type_id, :order_status_id, :order_priority_id, :print_location_id, :artwork, :proof, :line_items_attributes, :assignee_id, :admin_user_id
mount_uploader :artwork, ArtworkUploader
mount_uploader :proof, ProofUploader
has_many :line_items
belongs_to :assignee, :class_name => "AdminUser"
belongs_to :customer
belongs_to :order_category
belongs_to :order_type
belongs_to :order_status
belongs_to :order_priority
belongs_to :print_location
belongs_to :admin_user
accepts_nested_attributes_for :line_items, :allow_destroy => true
scope :owned_by, lambda { |user| includes(:assignee).where("admin_users.id = ?", user.id) }
def default_values
if new_record?
self.start_date ||= Date.today
self.number ||= (Order.maximum(:number) + 1 rescue 1)
end
end
end
It looks like you're trying to filter the Orders table by order_id. Unless you've built your DB in a non-standard manner, the ID field of the orders table would typically be id (not order_id).
That issue aside, I doubt you want to be passing in an order id for the index action since that would only return a single record, and by it's nature the index action should list many records (in your case, all records for the current_admin_user).
If neither of those issues solve your problem, try commenting out the lines 1 by 1.
Try to add this controller method
ActiveAdmin.register Order do
controller do
def scoped_collection
Order.where(:admin_user => current_admin_user.id)
end
end
end
see more here:
Two pages for the same resource - ActiveAdmin
I fixed the issue by taking the line in question out and using scope_to :current_user. I am wondering though, how to add a conditional statement to still allow the admin to view this? here is a look at the controller now.
scope_to current_user
index do
column "ID" do |order|
link_to order.id, admin_order_path(order)
end
column "Proof" do |order|
image_tag order.proof_url(:proof).to_s
end
column "Name" do |order|
link_to order.name, admin_order_path(order)
end
column(:customer, :sortable => :customer_id)
column "Category", :order_category
column "Status", :order_status
column "Priority", :order_priority
column "Due Date", :end_date
default_actions
end

Select many box for HABTM

I have two models; question and category which have a HABTM association between them. Now I want to have a form where I can edit the questions categories, however I don't know how. I started with this but I am lost, I am unsure on what to name the "name" attributes etc and how it is automatically edited/created with the question, how do I set this up?
<%= f.fields_for :categories do |categories_form| %>
<%= categories_form.select "category_ids", Category.all.collect { |c| [c.description, c.id] }, {}, {:multiple => true, :size => 9} %>
<% end %>
I managed to set up question(has_many) --> answer with fields_for and accepts_nested_attributes_for, but not this.
You should take a look at the following screencasts by Ryan Bates Nested Model Form Part 1 and Nested Model Form Part 2.
Migrations
You need to create the migrations for the tables
You need to create the migration for the middle table of the association
+ the middle table name that is created by the association is :categories_questions
or :questions_categories, in the second case you must define the name in models as shown in the link
Do I need to manually create a migration for a HABTM join table?
class CreateCategoriesQuestions < ActiveRecord::Migration
def self.up
create_table :categories_questions, :id => false do |t|
t.references :category
t.references :question
end
add_index :categories_questions, [:category_id, :question_id]
add_index :categories_questions, [:question_id, :category_id]
end
def self.down
drop_table :categories_questions
end
end
Question Model
class Question < ActiveRecord::Base
has_and_belongs_to_many :categories
end
Category Model
class Category < ActiveRecord::Base
has_and_belongs_to_many :questions
end
Controller Stuf
questions_controller.rb
def new
#question = Question.new
#question.categories.build #Build a categories_questions so as to use fields_for
end
Form Stuff
= f.fields_for :categories do |categories_fields|
= categories_fields.text_field :name
= categories_fields.text_field :description
At this point i must tell you ( i am new in ruby & rails ) that to create a new object here you can use jquery to append a html block name properly, or create helpers (that use javascript in the end) to add a new object and on save, save the association.
In the next link someone demonstrated the exact way .
http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for#512-Setting-child-index-while-using-nested-attributes-mass-assignment

Resources