Create a Dynamic Table: Attributes = Columns, Nested Attributes = Rows - ruby-on-rails

Rows & columns, baby. Rows & columns.
Within one table:
When a User adds a new :name & :metric I want it to create a new column.
When a User adds a new :result_value & :date_value I want it to create a new row.
This is what my table currently looks like:
For the life of me I can't figure out why something that seems so elementary is so difficult. I don't know if I'm suppose to create the rows and columns in the database, model, helper or index view and/or if I should be using some other language or gem to help me. Any direction would be greatly appreciated because a dozen google searches hasn't done it for me.
Do I use add_column in the database or something?
class CreateQuantifieds < ActiveRecord::Migration
def change
create_table :quantifieds do |t|
t.string :categories
t.string :name
t.string :metric
t.timestamps null: false
end
end
end
Or do I add columns via the model?
class Quantified < ActiveRecord::Base
belongs_to :user
has_many :results #correct
accepts_nested_attributes_for :results, :reject_if => :all_blank, :allow_destroy => true #correct
scope :averaged, -> { where(categories: 'averaged') }
scope :instance, -> { where(categories: 'instance') }
CATEGORIES = ['averaged', 'instance']
end
class Result < ActiveRecord::Base
belongs_to :user
belongs_to :quantified
end
Or do I keep playing around with the index view I've tried all sorts of 's and html tags, but I could never get the code to look like above, which is why I think the answer may lie deeper.
<!-- Default bootstrap panel contents -->
<div id="values" class="panel panel-default">
<div class="panel-heading"><h4><b>AVERAGE</b></h4></div>
<table>
<% #averaged_quantifieds.each do |averaged| %>
<% if averaged.user == current_user %>
<th><%= averaged.name %> (<%= averaged.metric %>)</th>
<td><% averaged.results.each do |result| %>
<tr><td><%= result.date_value.strftime("%m-%Y") %></td>
<td><%= result.result_value %></td></tr>
<% end %></td>
<% end %>
<% end %>
</table>
</div>
<br>
<br>
<br>
<br>
<!-- Default bootstrap panel contents -->
<div id="values" class="panel panel-default">
<div class="panel-heading"><h4><b>INSTANCE</b></h4></div>
<table>
<% #instance_quantifieds.each do |instance| %>
<% if instance.user == current_user %>
<th><%= instance.name %> (<%= instance.metric %>)</th>
<td><% instance.results.each do |instance| %>
<tr><td><%= result.date_value.strftime("%m-%Y") %></td>
<td><%= result.result_value %></td></tr>
<% end %></td>
<% end %>
<% end %>
</table>
</div>
<div class="values-button">
<%= link_to new_quantified_path, class: 'btn' do %>
<b><span class="glyphicon glyphicon-plus"</span></b>
<% end %>
</div>

I think you may have a misunderstanding of how the database should be used.
I don't think you would ever want the actual database tables to be dynamic. Instead, you should set up two tables. One for the User, and one for the Metrics. Then a user can add new metrics.
# == Schema Information
# Table name: users
# id :integer
Class User < ActiveRecord::Base
has_many :metrics
end
# == Schema Information
# Table name: metrics
# id :integer
# type :string
# result :string
# user_id :integer
Class Metric < ActiveRecord::Base
belongs_to :user
end
This way, you can create as many types of metrics as you want. And you can determine who the metric belongs to by the :user_id field.
Reading this from head to toe should help: http://guides.rubyonrails.org/association_basics.html

Related

List sections with books associated with them

I'm fairly new to ruby on rails and this has been kind of an interesting problem since this seems easy to implement in other languages but I don't know how to tackle it in this one. There was a similar post to this but it had two separate models which I would like to avoid.
This is my end goal:
Section Name
Book A, author
Book B, author
Section Name
Book C, author
Book D, author
Ideally, I'd like to have books be one model, so my model looks like this:
Book Model
class Book < ApplicationRecord
validates :section, :title, :author, presence: true
Book Controller
def index
#books = Book.all
I'm assuming I would need some sort of view that has it list it like below but I'm not sure how to go from there.
<% #sections.each do |section| %>
<% Book.each do |book| %>
<%= book.name %>
<% end %>
<% end %>
Any help would be very appreciated!
Firstly you need migration and associations between these models
change_table :books do |t|
t.belongs_to :section, foreign_key: true, null: false
end
class Book < ApplicationRecord
belongs_to :section
class Section < ApplicationRecord
has_many :books, dependent: :destroy
And in view you can iterate through sections and separately through evert section books
<% #sections.each do |section| %>
<div><b><%= section.name %></b></div>
<ul>
<% section.books.each do |book| %>
<li>
<%= book.name %>, <%= book.author %>
</li>
<% end %>
</ul>
<% end %>
what you need is this:
<% #sections.each do |section| %>
<% section.books.each do |book| %>
<%= book.name %>
<% end %>
<% end %>

Looping through a nested attribute with external model details

three-months-in beginner with Ruby on Rails here, so I apologize if any of my terminology is incorrect. I have a question about referencing outside models from a nested attribute.
I have three models. Tasks with nested attributes for Task Products, and a separate Items table with pre-populated products.
Each Task has many Task Products, and the Task Product has a "product_id" column which is in reference to an existing product in the Item table. In creating an table index of each Task, I am having trouble figuring out how to have the nested Task Product's product_id's list out the Item instead of just the bare id.
Here's the code I'm working with:
tasks_controller.rb -->
def dashboard
#tasks = Task.includes(:task_products, :storeorder).last(100)
#tasks.each do |task|
task.storeorder do |storeorder|
end
task.task_products.each do |task_product|
#item = Item.where(:id => task_product.product_id)
end
end
end
task.rb -->
class Task < ApplicationRecord
has_many :task_products
accepts_nested_attributes_for :task_products
end
task_product.rb -->
class TaskProduct < ApplicationRecord
belongs_to :task
has_many :items
end
item.rb -->
class Item < ActiveRecord::Base
belongs_to :task_product
def item_select
"#{vendor_name} (#{description})"
end
end
dashboard.html.erb -->
<td>
<% t.task_products.each do |tp| %>
# Existing code that lists each task product in a list on the table:
<p><%= tp.product_id %></p>
# The ideal code I would like to run:
<p><%= link_to #item.item_select, item_path(id: #item.id) %>
<% end %>
</td>
Any ideas how I can run the #item call as it pertains to the 'tp.product_id' code in the html file?
Appreciate any help I can get. Searching for this issue has left me with many purple links, but none of which address this particular issue.
EDIT: In case anybody happens upon this that was in the same predicament as me, I have one recommendation: Learn your associations.
Updated code:
tasks_controller.rb -->
def dashboard
#tasks = Task.includes(:task_products, :storeorder).last(100)
end
task.rb -->
class Task < ApplicationRecord
has_many :task_products
accepts_nested_attributes_for :task_products
end
task_product.rb -->
class TaskProduct < ApplicationRecord
belongs_to :task
belongs_to :item, foreign_key: :product_id
end
item.rb -->
class Item < ActiveRecord::Base
has_many :task_products, foreign_key: :product_id
def item_select
"#{vendor_name} (#{description})"
end
end
dashboard.html.erb -->
<td>
<% t.task_products.each do |tp| %>
<% tp.items.each do |item| %>
<p><%= link_to item.item_select, item_path(item) %></p>
<% end %>
<% end %>
</td>
First, by using Item.where in your controller, you're actually setting #item to a collection of Items. Second, by setting it inside a loop, you're overwriting it with each TaskProduct, so only the last one will be accurate in the view.
I'm assuming you want to list [a subset of] every Item for every Task. In that case, you'd be better not to set them in your controller at all:
def dashboard
#tasks = Task.includes(:task_products, :storeorder).last(100)
end
Instead, just loop through them in the view:
<% #tasks.each do |t| %>
Task <%= t %>
<% t.task_products.each do |tp| %>
TaskProduct <%= tp %>
<% tp.items.each do |item| %>
<p><%= link_to item.item_select, item_path(item) %></p>
<% end %>
<% end %>
<% end %>
I don't see any need of all the loops in your dashboard_controller.rb
#tasks.each do |task|
task.storeorder do |storeorder|
end
task.task_products.each do |task_product|
#item = Item.where(:id => task_product.product_id)
end
end
Here's what you need in html.erb:
# The ideal code I would like to run:
<% tp.items.each do |item| %>
<p><%= link_to item.item_select, item_path(item) %>
<% end %>

show names instead of ids rails

Hello i created 2 tables (categories and employees),both are in a relationship and i want to show category_name(Categories table) in my employees index view instead of id's
****Here is my Categories Table******
class CreateCategories < ActiveRecord::Migration
def self.up
create_table :categories do |t|
t.string :category_name
end
end
def self.down
drop_table :categories
end
end
****Here is my Enployees Table******
class CreateEmployees < ActiveRecord::Migration
def self.up
create_table :employees do |t|
t.string :name
t.string :last_name
t.integer :categories_id
end
execute "ALTER TABLE employees ADD CONSTRAINT fk_employees_categories FOREIGN KEY (categories_id) REFERENCES categories (id)"
end
def self.down
drop_table :employees
end
end
****Here is my Employee controller******
class EmployeeController < ApplicationController
def index
#employees = Employee.all
end
end
****Here is my Category controller******
class CategoryController < ApplicationController
def index
#categories = Category.all
end
end
****Here is my Category Model******
class Category < ActiveRecord::Base
has_many :employees
end
****Here is my Employee Model******
class Employee < ActiveRecord::Base
belongs_to :category
end
****Here is Employee view*******
<table>
<tr>
<th>Employee</th>
<th>Last Name</th>
<th>Category</th>
</tr>
<% #employees.each do |e| %>
<tr>
<td><%=h e.name %></td>
<td><%=h e.last_name %></td>
<td><%=h e.categories_id %>
</td>
Here in e.categories_id i want to show categories_name that is from my categories table
<td>
<%= link_to ("View" ,:controller=>"employee",:action=>"show", :id=>e.id ) %>
</td>
<td>
<%= link_to ("Edit", :controller=>"employee",:action=>"edit",:id=>e.id ) %>
</td>
<td>
<%= link_to ( "Delete",:controller=>"employee",:action=>"destroy", :id=>e.id ,:confirm=>"sure?" %>
</td>
</tr>
<% end %>
</table>
Can someone help me with this problem please?
In your employee index views,
<td><%=employee.categories.category_name %>
instead of
<td><%=h e.categories_id %>
<%= h e.category.category_name %>
will do the trick.
Note that if you just do this, it will cause an SQL query to be executed for each row that you're displaying (known as the N+1 problem).
So in your controller, you'll have to change
#employees = Employee.all
to
#employees = Employee.find(:all, :include => [:category])
This will force eager loading, and reduce the number of queries to 2.
(As a side note, you really should have moved on to Rails 3 by now. Rails 4 is current. Each version offers a lot more features than the last.)
In Your employee controller add this
It means that get category_name to appear on your index view instead of ID.
#category= Category.all()
#category_list=[]
#category.each do |c|
#category_list << [c.category_name,c.id]
end
and then in your index views
<td><%=employee.categories.category_name %>
I know this is a late answer, but for anyone who is looking for an answer, here is how I accomplished this,
I have a table realties that has a one to many association with features table. I used the railscast method for multiple checkbox selection, so for each realty it has certain features. I wanted to show the features of each realty in its row in the index view. I had in the show view, features are shown like this,
#realty.feature_ids
where #realty in the controller is
#realty = Realty.find(params[:id])
however, in the index view the realty.feature_ids only showed numbers only not the names.
To work this out here is what I did in the index view:
<% #realties.each do |realty| %>
<td>
<% realty.feature_ids.each do |feature| %>
<%= Feature.find(feature).name %>
<% end %>
</td>
<% end %>
so for the above question, loop through the ids with each loop, this will give you the id number, find it in the Category model with the .name method. So I guess the solution for you would look like this:
<% #employees.each do |employee| %>
<td>
<% employee.category_ids.each do |category| %>
<%= Category.find(category).name %>
<% end %>
</td>
<% end %>
I hope this will benefit someone.

Help with rails nested forms

I have these three models.
Student.
Evaluation.
Grade (student_id, evaluatioin_id, value)
I want to make a form so that a User can set the grade for each student of a evaluation...
I want to keep this is as clean as possible (and restful)...
Im open to any suggestions on how to get this done.
Please Help.
See these
http://railscasts.com/episodes/196-nested-model-form-part-1
http://railscasts.com/episodes/197-nested-model-form-part-2
I suppose you have the following associations:
Evaluation:
class Evaluation < ActiveRecord:Base
has_many :grades
accepts_nested_attributes_for :grades
end
Student:
class Student < ActiveRecord::Base
has_many :grades
end
Grade:
class Grade < ActiveRecord::Base
belongs_to :student
blongs_to :evaluation
end
Regarding your comment I think you want to use the nested_form_for gem made by Ryan Bates. It helps you add/delete dynamically nested attributes in your form, very efficiently. You can use it like this:
<% nested_form_for #evaluation do |f| %>
<% f.fields_for :grades do |gf| %>
<%= gf.label 'Username of the student' %>
<%= gf.collection_select(:student, :student_id, Student.All, :id, :username) %> <br/>
<%= gf.label :value %>
<%= gf.text_field :value %> <br/>
<%= gf.link_to_remove 'delete' %> <br/>
<% end %>
<%= f.link_to_add 'Add grade', :grades %> <br/>
<%= f.submit %>
<% end %>
Tell me if it works/fits your needs. I have been using this gem a lot these previous days.
What I ended up doing was..
In Grade Model
scope :for_student,lambda{|student, evaluation| where("student_id"=>student.id).where("evaluation_id"=>evaluation.id)}
In EvaluationsController
def assign_grades])
#evaluation = Evaluation.find(params[:id])
#students = Student.all
#students.each do |s|
grade = Grade.for_student(s,#evaluation)
if !grade.exists?
#evaluation.grades.build(:value=> 0,:student_id=>s.id)
end
end
end
def save_grades
#evaluation = Evaluation.find(params[:id])
#evaluation.update_attributes(params[:evaluation])
redirect_to [#evaluation.batch_subject, #evaluation]
end
In View
<table class="medium">
<thead>
<tr>
<th>name</th>
<th>Grade</th>
</tr>
</thead>
<tbody>
<%=f.fields_for :grades do |g|%>
<tr>
<td><%=g.label :value,g.object.student.full_name%> <%= g.hidden_field :student_id%></td>
<td><%=g.text_field :value%></td>
</tr>
<%end%>
</tbody>
</table>

form_for for relation table with type of many to many relation

My goal is to display select box for each relation for users and specific project.
All users need to be listed but only project users have some type of relation. Other users have none selected in theirs select box.
I have this model:
class Project < ActiveRecord::Base
belongs_to :company
has_many :tasks, :order => 'state_type_id ASC'
has_many :project_user_relations
has_many :users, :through => :project_user_relations
def t_name
name.camelcase
end
end
class User < ActiveRecord::Base
belongs_to :company
has_many :tasks , :foreign_key => :assigned_user_id
has_many :project_user_relations
has_many :projects, :through => :project_user_relations
def full_name
firstname + ' ' + lastname
end
def relation_to(project)
relation=ProjectUserRelation.find_by_project_id_and_user_id(project.id, id)
relation ||= relation=ProjectUserRelation.new
end
end
class ProjectUserRelation < ActiveRecord::Base
belongs_to :project
belongs_to :user
has_one :project_user_relation_type
end
class ProjectUserRelationType < ActiveRecord::Base
def t_name
I18n.t("app.projects.users.relation.type."+code)
end
end
I want make a form to display all users, with collection_select.
I used code:
def edit_all
#project = Project.find(params[:project_id])
#users = User.all
....
in my controler
routes works ok.
in my view:
<% #users.each do |user| %>
<%= f.fields_for :users, user do |user_fields| %>
<tr class="reference" rel="<%= parent_user_path(user) %>" >
<td class="name"><%= link_to user.full_name, parent_user_path(user) %></td>
<td class="email"><%= mail_to user.email %></td>
<td class="type">
<%= user_fields.fields_for user.relation_to #project do |relation_fields| %>
<%= relation_fields.collection_select :project_user_relation_type, ProjectUserRelationType.all, :id, :t_name, {:include_blank => false, :prompt => t("helpers.select.prompt") } %>
<% end %>
</td>
</tr>
<% end %>
<% end %>
or for test:
<%= f.fields_for :users, #users do |xuser_fields| %>
<% logger.debug "#{self.to_s} xuser_fields = #{xuser_fields.object.inspect} ;" %>
<tr>
<td><%= xuser_fields.text_field :firstname %></td>
<td></td>
<td></td>
<td></td>
</tr>
<% end %>
but notnihng woks right
first one generates wrong name in html:
select id="project_users_project_user_relation_project_user_relation_type" name="project[users][project_user_relation][project_user_relation_type]"
second one generates error:
undefined method `firstname' for # Array:0x4d03658
Can you help me to solve this situation.
PS:sorry for long code :(
SOLUTION (probably - solved by reading RoR sources)
I found sollution i thing.
A method
def name_attributes=(attributes)
# Process the attributes hash
end
in Project model was missing.
It is unbelievable sollution :].
There is also exact syntax after fields_for: :name, #some_collection, where name must be exactly same name as in the beginign of mentioned def in Model.

Resources