The nested attribute of quantifieds are results. In the results partial a User will checkoff if their quantified result is a :good thing or not.
In the sidebar I want to .count how many good results the User has marked off to serve as a reference point of their success.
SIDEBAR SECTION: layouts/_count.html.erb
<div class="stats">
<a href="<%= following_user_path(#user) %>">
<strong id="following" class="stat">
<%= #user.quantifieds.count %> #Works
</strong>
Quantified
</a>
<a href="<%= followers_user_path(#user) %>">
<strong id="followers" class="stat">
<%= #user.results.good.count %> #Remains zero regardless of number of checked :good boxes
</strong>
Good
</a>
</div>
quantifieds/_result_fields.html.erb
<div class="nested-fields">
<div class="form-group">
<%= f.text_field :result_value, class: 'form-control', placeholder: 'Enter Result' %>
<br/>
<%= f.date_select :date_value, :order => [:month, :day, :year], :with_css_classes => true, :class => "modular-date-field" %>
<b><%= link_to_remove_association "Remove Result", f %></b>
<div class="america3">
<label> Good: </label>
<%= f.check_box :good %>
</div>
</div>
</div>
result.rb
class Result < ActiveRecord::Base
belongs_to :user
belongs_to :quantified
default_scope { order('date_value DESC') }
scope :good, -> { where(good: true) }
scope :bad, -> { where(good: false) }
end
Should we add a method to the application controller?
class ApplicationController < ActionController::Base
before_action :load_todays_habits
before_action :set_top_3_goals
before_action :randomize_value
before_action :set_stats
protect_from_forgery with: :exception
include SessionsHelper
def set_top_3_goals
#top_3_goals = current_user.goals.unaccomplished.top_3 if current_user
end
def randomize_value
#sidebarvaluations = current_user.valuations.randomize if current_user
end
def set_stats
#quantifieds = Quantified.joins(:results).all
#averaged_quantifieds = current_user.quantifieds.averaged if current_user
#instance_quantifieds = current_user.quantifieds.instance if current_user
end
private
def load_todays_habits
#user_tags = current_user.habits.committed_for_today.tag_counts if current_user
#all_tags = Habit.committed_for_today.tag_counts if current_user
end
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
end
class QuantifiedsController < ApplicationController
before_action :set_quantified, only: [:show, :edit, :update, :destroy]
before_action :logged_in_user, only: [:create, :destroy]
def index
if params[:tag]
#quantifieds = Quantified.tagged_with(params[:tag])
else
#quantifieds = Quantified.joins(:results).all
#averaged_quantifieds = current_user.quantifieds.averaged
#instance_quantifieds = current_user.quantifieds.instance
end
end
def show
end
def new
#quantified = current_user.quantifieds.build
end
def edit
end
def create
#quantified = current_user.quantifieds.build(quantified_params)
if #quantified.save
redirect_to quantifieds_url, notice: 'Quantified was successfully created'
else
#feed_items = []
render 'pages/home'
end
end
def update
if #quantified.update(quantified_params)
redirect_to quantifieds_url, notice: 'Goal was successfully updated'
else
render action: 'edit'
end
end
def destroy
#quantified.destroy
redirect_to quantifieds_url
end
private
def set_quantified
#quantified = Quantified.find(params[:id])
end
def correct_user
#quantified = current_user.quantifieds.find_by(id: params[:id])
redirect_to quantifieds_path, notice: "Not authorized to edit this goal" if #quantified.nil?
end
def quantified_params
params.require(:quantified).permit(:categories, :metric, :result, :date, :comment, :private_submit, :tag_list, :good, results_attributes: [:id, :result_value, :date_value, :good, :_destroy])
end
end
quantifieds/_form
<%= javascript_include_tag "quantified.js" %>
<%= simple_form_for(#quantified) do |f| %>
<%= f.error_notification %>
<div class="america">
<form>
<% Quantified::CATEGORIES.each do |c| %>
<%= f.radio_button(:categories, c, :class => "date-format-switcher") %>
<%= label(c, c) %>
<% end %>
<br/>
<br/>
<div class="form-group">
<%= f.text_field :tag_list, quantified: #quantified.tag_list.to_s.titleize, class: 'form-control', placeholder: 'Enter Action' %>
</div>
<div class="form-group">
<%= f.text_field :metric, class: 'form-control', placeholder: 'Enter Metric' %>
</div>
<div id="results">
<%= f.fields_for :results do |result| %>
<%= render 'result_fields', :f => result %>
<% end %>
</div>
<div class="links">
<b><%= link_to_add_association 'Add Result', f, :results %></b>
</div>
<div class="america2">
<%= button_tag(type: 'submit', class: "btn") do %>
<span class="glyphicon glyphicon-plus"></span>
<% end %>
<%= link_to quantifieds_path, class: 'btn' do %>
<span class="glyphicon glyphicon-chevron-left"></span>
<% end %>
<%= link_to #quantified, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn' do %>
<span class="glyphicon glyphicon-trash"></span>
<% end %>
</div>
<label> Private: </label>
<%= f.check_box :private_submit %>
</form>
</div>
<% end %>
quantifieds.rb
class Quantified < ActiveRecord::Base
belongs_to :user
has_many :results #correct
has_many :comments, as: :commentable
accepts_nested_attributes_for :results, :reject_if => :all_blank, :allow_destroy => true #correct
scope :averaged, -> { where(categories: 'Averaged') }
scope :instance, -> { where(categories: 'Instance') }
scope :private_submit, -> { where(private_submit: true) }
scope :public_submit, -> { where(private_submit: false) }
validates :categories, :metric, presence: true
acts_as_taggable
CATEGORIES = ['Averaged', 'Instance']
end
schema.rb
# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150317171422) do
create_table "activities", force: true do |t|
t.integer "trackable_id"
t.string "trackable_type"
t.integer "owner_id"
t.string "owner_type"
t.string "key"
t.text "parameters"
t.integer "recipient_id"
t.string "recipient_type"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "activities", ["owner_id", "owner_type"], name: "index_activities_on_owner_id_and_owner_type"
add_index "activities", ["recipient_id", "recipient_type"], name: "index_activities_on_recipient_id_and_recipient_type"
add_index "activities", ["trackable_id", "trackable_type"], name: "index_activities_on_trackable_id_and_trackable_type"
create_table "comments", force: true do |t|
t.text "content"
t.integer "commentable_id"
t.string "commentable_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "comments", ["commentable_id", "commentable_type"], name: "index_comments_on_commentable_id_and_commentable_type"
create_table "days", force: true do |t|
t.integer "level_id"
t.integer "habit_id"
t.boolean "missed", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "goals", force: true do |t|
t.string "name"
t.date "deadline"
t.boolean "accomplished"
t.boolean "private_submit"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "goals", ["user_id", "created_at"], name: "index_goals_on_user_id_and_created_at"
add_index "goals", ["user_id"], name: "index_goals_on_user_id"
create_table "habits", force: true do |t|
t.datetime "left"
t.integer "level"
t.text "committed"
t.datetime "date_started"
t.string "trigger"
t.string "target"
t.string "reward"
t.boolean "private_submit"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "habits", ["user_id", "created_at"], name: "index_habits_on_user_id_and_created_at"
add_index "habits", ["user_id"], name: "index_habits_on_user_id"
create_table "levels", force: true do |t|
t.integer "user_id"
t.integer "habit_id"
t.boolean "passed", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "quantifieds", force: true do |t|
t.string "categories"
t.string "metric"
t.boolean "private_submit"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "quantifieds", ["user_id", "created_at"], name: "index_quantifieds_on_user_id_and_created_at"
add_index "quantifieds", ["user_id"], name: "index_quantifieds_on_user_id"
create_table "relationships", force: true do |t|
t.integer "follower_id"
t.integer "followed_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "relationships", ["followed_id"], name: "index_relationships_on_followed_id"
add_index "relationships", ["follower_id", "followed_id"], name: "index_relationships_on_follower_id_and_followed_id", unique: true
add_index "relationships", ["follower_id"], name: "index_relationships_on_follower_id"
create_table "results", force: true do |t|
t.integer "user_id"
t.string "result_value"
t.date "date_value"
t.integer "quantified_id"
t.boolean "good"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "taggings", force: true do |t|
t.integer "tag_id"
t.integer "taggable_id"
t.string "taggable_type"
t.integer "tagger_id"
t.string "tagger_type"
t.string "context", limit: 128
t.datetime "created_at"
end
add_index "taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true
add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context"
create_table "tags", force: true do |t|
t.string "name"
t.integer "taggings_count", default: 0
end
add_index "tags", ["name"], name: "index_tags_on_name", unique: true
create_table "users", force: true do |t|
t.string "name"
t.string "email"
t.text "missed_days"
t.text "missed_levels"
t.string "provider"
t.string "uid"
t.string "oauth_token"
t.datetime "oauth_expires_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "remember_digest"
t.boolean "admin", default: false
t.string "activation_digest"
t.boolean "activated", default: false
t.datetime "activated_at"
t.string "reset_digest"
t.datetime "reset_sent_at"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
create_table "valuations", force: true do |t|
t.string "name"
t.boolean "private_submit"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "valuations", ["user_id", "created_at"], name: "index_valuations_on_user_id_and_created_at"
add_index "valuations", ["user_id"], name: "index_valuations_on_user_id"
end
class User < ActiveRecord::Base
has_many :authentications
has_many :habits, dependent: :destroy
has_many :levels
has_many :valuations, dependent: :destroy
has_many :comments, as: :commentable
has_many :goals, dependent: :destroy
has_many :quantifieds, dependent: :destroy
has_many :results, dependent: :destroy
Thanks so much for your time!
You can update your Result as follows:
class Result < ActiveRecord::Base
# rest of the code
scope :good, -> { where(good: true) }
scope :good_count, -> { good.count }
end
Let's perform some tests in rails console:
u = User.create({ user_attributes })
u.results.create(good: true)
u.results.create(good: false)
u.results.create(good: true)
u.results.count
# => SELECT COUNT(*) FROM "results" WHERE "results"."user_id" = ? [["user_id", 1]]
# => 3
u.results.good_count
# => SELECT COUNT(*) FROM "results" WHERE "results"."user_id" = ? AND "results"."good" = 't' [["user_id", 1]]
# => 2
If you changed your User as follows:
class User < ActiveRecord::Base
# rest of the code
def good_results_count
results.good_count
end
end
which is much cleaner solution, you can use it in your application like:
# assuming we have data set like in previous step
u = User.last
u.good_results_count # you can use this line in your template
# => SELECT COUNT(*) FROM "results" WHERE "results"."user_id" = ? AND "results"."good" = 't' [["user_id", 1]]
# => 2
If, for some reason, the value remains 0 - as you mentioned - try performing the operations I described in console to see real data in database. Maybe this is not the problem with fetching proper data, maybe data is not saved correctly?
Let me know how it goes, so we're try to address the real problem!
Good luck!
Update
Problem in view is due to lack of proper association between User and Result. What currently is in User:
class User < ActiveRecord::Base
# rest of the code
has_many :quantifieds, dependent: :destroy
has_many :results, dependent: :destroy
# rest of the code
end
When new Results are created through Quantifieds, this is what you have in QuantifiedsController#create:
class QuantifiedsController < ApplicationController
def create
#quantified = current_user.quantifieds.build(quantified_params)
if #quantified.save
redirect_to quantifieds_url, notice: 'Quantified was successfully created'
else
#feed_items = []
render 'pages/home'
end
end
end
Well, everything looks perfectly all right, but the problem is actually in line #quantified = current_user.quantifieds.build(quantified_params). All Results created this was have user_id = nil, only the quantified_id is set properly.
How to fix this?
By simplifying the associations! There is no need to store both quantify_id and user_id in Result.
If there is a relation like: User has many Quantifies, and Quantify has many Results, you can quite easily access Results of Users by proper declaration in User:
class User < ActiveRecord::Base
# rest of the code
has_many :quantifieds, dependent: :destroy
has_many :results, through: :quantifieds
# rest of the code
end
This is quite clever thing! Let's see, what happens under the hood:
u = User.last
u.results
Result Load (0.3ms) SELECT "results".* FROM "results" INNER JOIN "quantifieds" ON "results"."quantified_id" = "quantifieds"."id" WHERE "quantifieds"."user_id" = ? ORDER BY date_value DESC [["user_id", 5]]
=> #<ActiveRecord::Associations::CollectionProxy ...>
Now, when relation is set up as described above, the proper counts should appear in website.
There is no need to store user_id in Result, so you can freely remove it!
<div class="stats">
<a href="<%= following_user_path(#jadenmcgruder) %>">
<strong id="following" class="stat">
<%= #jadenmcgruder.quantifieds.count %> #Works
</strong>
Quantified
</a>
<a href="<%= followers_user _path(#jadenmcgruder) %>">
<strong id="followers" class="stat">
<%= #jadenmcgruder.results.good.count %> #Remains zero regardless of number of checked :good boxes
</strong>
Good
</a>
</div>
Related
I am very new to rails and active record so I'm probably doing something wrong, but... using the schema below, I want to create a list of courses in the outer do loop, then list each student enrolled in the course with the inner do loop. I m not sure if this approach will work, but this error keeps popping up:
ActionView::Template::Error (undefined method `enrollments' for #):
it seems that the association is made. what am i doing wrong?
Professor show page:
<div class="col-md-8">
<h2 class="card-title"><%= #professor.name %></h2>
<% #courses_taught.each do |course| %>
<div class="card mb-4 card-header">
<img class="card-img-top" src="http://placehold.it/750x300" alt="Card image cap">
<h3 class="card-text"><%= course.title %></h3>
</div>
<div class="card-body">
<% course.sections.enrollments.students.each do |student| %>
<p><% student.name %></p>
<% end %>
</div>
<% end %>
</div>
models:
enrollment
class Enrollment < ApplicationRecord
belongs_to :section
belongs_to :student
end
Student:
class Student < ApplicationRecord
has_many :enrollments
end
Professor:
class Section < ApplicationRecord
has_many :enrollments
belongs_to :professor
belongs_to :course
validates_uniqueness_of :professor_id, scope: :course_id
scope :by_professor_id, ->(prof_id) { where('professor_id = ?', prof_id) }
end
Course:
class Course < ApplicationRecord
enum status: { planning: 0, offered: 1 }
scope :offered, -> { where(status: 1) }
scope :planning, -> { where(status: 0) }
belongs_to :department
has_many :sections
has_many :professors, through: :sections
validates :title, :number, :status, :description, presence: true
validates :description, length: { in: 10..500 }
validates :title, :number, uniqueness: { case_sensitive: false }
def self.search(term)
if term
where('title LIKE ?', "%#{term}%").order('title DESC')
else
order('title ASC')
end
end
def self.taught_by(professor_id)
Course
.joins(:sections)
.joins(:professors)
.where(sections: { professor_id: professor_id })
.select('distinct courses.*')
end
end
Schema:
ActiveRecord::Schema.define(version: 20171013201907) do
create_table "courses", force: :cascade do |t|
t.string "title"
t.text "description"
t.string "number"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "status", default: 0
t.integer "department_id"
t.index ["department_id"], name: "index_courses_on_department_id"
end
create_table "departments", force: :cascade do |t|
t.string "name"
t.text "description"
t.text "main_image"
t.text "thumb_image"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "enrollments", force: :cascade do |t|
t.integer "section_id"
t.integer "student_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["section_id"], name: "index_enrollments_on_section_id"
t.index ["student_id"], name: "index_enrollments_on_student_id"
end
create_table "professors", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "status", default: 0
t.integer "department_id"
t.text "bio"
t.index ["department_id"], name: "index_professors_on_department_id"
end
create_table "sections", force: :cascade do |t|
t.integer "number"
t.integer "max_enrollment"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "professor_id"
t.integer "course_id"
t.string "room"
t.index ["course_id"], name: "index_sections_on_course_id"
t.index ["professor_id", "course_id"], name: "index_sections_on_professor_id_and_course_id", unique: true
t.index ["professor_id"], name: "index_sections_on_professor_id"
end
create_table "students", force: :cascade do |t|
t.string "name"
t.decimal "gpa"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "name"
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "roles"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
end
enrollment belongs_to user, there is no need to use each on single record rather it will throw error. You can use below code.
<% course.sections.each do |section| %>
<% section.enrollments.each do |enrollment| %>
<p><% enrollment.student.name %></p>
<% end %>
<% end %>
Sean's answer is almost correct but since enrollments belongs_to student
you would use the singular form enrollment.student. but more simply you can just call student from inside enrollments block.
<div class="col-md-8">
<h2 class="card-title"><%= #professor.name %></h2>
<% #courses_taught.each do |course| %>
<div class="card mb-4 card-header">
<img class="card-img-top" src="http://placehold.it/750x300" alt="Card image cap">
<h3 class="card-text"><%= course.title %></h3>
</div>
<div class="card-body">
<% course.sections.each do |section| %>
<% section.enrollments.each do |enrollment| %>
<p><% enrollment.student.name %></p>
<% end %>
<% end %>
</div>
<% end %>
</div>
<div class="col-md-8">
<h2 class="card-title"><%= #professor.name %></h2>
<% #courses_taught.each do |course| %>
<div class="card mb-4 card-header">
<img class="card-img-top" src="http://placehold.it/750x300" alt="Card image cap">
<h3 class="card-text"><%= course.title %></h3>
</div>
<div class="card-body">
<% course.sections.each do |section| %>
<% section.enrollments.each do |enrollment| %>
<p><% enrollment.student.name %></p>
<% end %>
<% end %>
</div>
<% end %>
</div>
I'm creating a clothing store. I have Categories that have Sizes. A womens shirt (Category) might have XS, S, M, Large. A mens shirt can have XS, S, M, L. Shoes can have 4-16 and so on.
I have created a has_many through: association that connects the Category table with Sizes table by a Cateogry_Sizes table.
When an admin creates a Category, they should select all the Sizes that the Category will need.
How can I select the Sizes in the below view?
The current code is incorrect. In the console, when I go to category.sizes, I just get an empty array.
View:
<div class="container">
<div class=βrowβ>
<div class="col-md-6 col-md-offset-3">
<div class="panel panel-primary">
<div class="panel-body">
<%= simple_form_for(#category) do |f| %>
<div class="form-inputs">
<%= f.input :name %>
<%= f.select(:sizes, Size.all.map {|s| [s.title, s.id]}, :multiple => true) %>
<%= f.collection_select :parent_id, Category.order(:name), :id, :name, {prompt: "Select Parrent ID If Applicable"},include_blank: true %>
<div class="form-actions"><%= f.button :submit %></div>
</div>
<% end %>
</div>
</div>
</div>
</div>
</div>
Category model:
class Category < ActiveRecord::Base
has_ancestry
has_many :items
validates :name, presence: true, length: { maximum: 20 }
has_many :category_sizes
has_many :sizes, through: :category_sizes
end
Size model:
class Size < ActiveRecord::Base
validates :title, presence: true, length: { maximum: 15 }
validates :title, uniqueness: true
has_many :category_sizes
has_many :categories, through: :category_sizes
end
Category_size model:
class CategorySize < ActiveRecord::Base
belongs_to :category
belongs_to :size
end
Schema:
ActiveRecord::Schema.define(version: 20150920013947) do
create_table "categories", force: :cascade do |t|
t.string "name"
t.string "ancestry"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "categories", ["ancestry"], name: "index_categories_on_ancestry"
create_table "category_sizes", force: :cascade do |t|
t.integer "category_id"
t.integer "size_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "category_sizes", ["category_id"], name: "index_category_sizes_on_category_id"
add_index "category_sizes", ["size_id"], name: "index_category_sizes_on_size_id"
create_table "items", force: :cascade do |t|
t.string "title"
t.decimal "price"
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.integer "category_id"
end
add_index "items", ["user_id", "created_at"], name: "index_items_on_user_id_and_created_at"
add_index "items", ["user_id"], name: "index_items_on_user_id"
create_table "sizes", force: :cascade do |t|
t.text "title"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "taggings", force: :cascade do |t|
t.integer "tag_id"
t.integer "taggable_id"
t.string "taggable_type"
t.integer "tagger_id"
t.string "tagger_type"
t.string "context", limit: 128
t.datetime "created_at"
end
add_index "taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true
add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context"
create_table "tags", force: :cascade do |t|
t.string "name"
t.integer "taggings_count", default: 0
end
add_index "tags", ["name"], name: "index_tags_on_name", unique: true
create_table "users", force: :cascade do |t|
t.string "username"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "remember_digest"
t.boolean "admin", default: false
t.string "activation_digest"
t.boolean "activated", default: false
t.datetime "activated_at"
t.string "reset_digest"
t.string ">"
t.datetime "reset_sent_at"
t.string "avatar_file_name"
t.string "avatar_content_type"
t.integer "avatar_file_size"
t.datetime "avatar_updated_at"
t.text "description"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
end
Controller:
class CategoriesController < ApplicationController
before_action :set_category, only: [:show]
before_action :admin_user, only: [:destroy, :index, :edit, :show]
def index
#categories = Category.all
end
def show
#tags = Item.where(category_id: #category.id).tag_counts_on(:tags)
if params[:tag]
#items = Item.tagged_with(params[:tag])
else
#items = Item.where(category_id: #category.id).order("created_at DESC")
end
end
def new
#category = Category.new
end
def edit
#category = Category.find(params[:id])
end
def create
#category = Category.new(category_params)
if #category.save
redirect_to #category
flash[:success] = "You have created a new category"
else
flash[:danger] = "Your category didn't save"
render "new"
end
end
def update
#Cateogry = Category.find(params[:id])
if #Cateogry.update(category_params)
redirect_to #Cateogry
flash[:success] = 'Category was successfully updated.'
else
render "edit"
end
end
def destroy
Category.find(params[:id]).destroy
flash[:success] = "Category deleted"
redirect_to categories_path
end
private
def set_category
#category = Category.find(params[:id])
end
def category_params
params.require(:category).permit(:name, :parent_id)
end
# Confirms an admin user.
def admin_user
redirect_to(root_url) unless current_user.try(:admin?)
end
end
Here is what happens when in console:
2.1.2 :026 > c = Category.last
Category Load (0.3ms) SELECT "categories".* FROM "categories" ORDER BY "categories"."id" DESC LIMIT 1
=> #<Category id: 57, name: "Test20", ancestry: "20", created_at: "2015-09-23 12:35:14", updated_at: "2015-09-23 12:35:14">
2.1.2 :027 > c.sizes
Size Load (0.2ms) SELECT "sizes".* FROM "sizes" INNER JOIN "category_sizes" ON "sizes"."id" = "category_sizes"."size_id" WHERE "category_sizes"."category_id" = ? [["category_id", 57]]
Here is what happens on form submit in server log:
Started POST "/categories" for ::1 at 2015-09-23 22:37:28 +1000
Processing by CategoriesController#create as HTML
Parameters: {"utf8"=>"β", "authenticity_token"=>"4pMZ9PUr5yTSCNRiQeATljZsOIDeQCwhQPy9djEbAmejntpb8/DkK20JrMUeZkStsB5UU6YhbtExGwDKs7tT2Q==", "category"=>{"name"=>"test21", "sizes"=>"6", "parent_id"=>"20"}, "commit"=>"Create Category"}
Unpermitted parameter: sizes
Category Load (0.1ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = ? LIMIT 1 [["id", 20]]
(0.1ms) begin transaction
SQL (0.3ms) INSERT INTO "categories" ("name", "ancestry", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["name", "test21"], ["ancestry", "20"], ["created_at", "2015-09-23 12:37:28.927360"], ["updated_at", "2015-09-23 12:37:28.927360"]]
(1.0ms) commit transaction
Redirected to http://localhost:3000/categories/58
Completed 302 Found in 5ms (ActiveRecord: 1.5ms)
Started GET "/categories/58" for ::1 at 2015-09-23 22:37:28 +1000
Processing by CategoriesController#show as HTML
Parameters: {"id"=>"58"}
You need to allow the sizes in the params of the controller like this:
def category_params
params.require(:category).permit(:name, :parent_id, size_ids: [])
end
In your form you should probably change:
<%= f.select(:sizes, Size.all.map {|s| [s.title, s.id]}, :multiple => true) %>
to
<%= f.association :sizes %>
Simple form should then do the magic. See also: https://github.com/plataformatec/simple_form#associations for more information.
As an aside, I felt it apt to highlight that you may be able to use has_and_belongs_to_many to make this work in its current form. It works very similarly to has_many :through except it doesn't have a join model:
#app/models/category.rb
class Category < ActiveRecord::Base
has_and_belongs_to_many :sizes
end
#app/models/size.rb
class Size < ActiveRecord::Base
has_and_belongs_to_many :categories
end
This will mean you'll have to drop your category_sizes table and replace it with categories_sizes with category_id | size_id columns:
This is only recommended if you don't want to include any further information in your join model. For example, if you wanted to include stock levels or something, the join model of has_many :through would be vital; not as you have it now.
It will also allow you to call:
#category = Category.find params[:id]
#category.sizes #-> collection of sizes for category.
--
Form
HABTM also would make the form much simpler:
<%= simple_form_for(#category) do |f| %>
<%= f.input :name %>
<%= f.collection_select :sizes, Size.all, :id, :name, { multiple: true } %>
<%= f.collection_select :parent_id, Category.order(:name), :id, :name, {prompt: "Select Parent ID If Applicable"},include_blank: true %>
<%= f.submit %>
<% end %>
#app/controllers/categories_controller.rb
class CategoriesController < ApplicationController
def create
#category = Category.new category_params
end
private
def category_params
params.require(:category).permit(:etc, :etc, :sizes)
end
end
I am using rails 4, and I want to show monthly ticket generated through pie charts. Can anybody help?
Schema:
create_table "tickets", force: :cascade do |t|
t.integer "store_id"
t.integer "vendor_id"
t.datetime "ticket_date"
t.datetime "deadline"
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "ticket_no"
t.string "vendor_name"
t.string "user_id"
t.integer "issue_id"
t.integer "category_id"
t.boolean "completed", default: false
end
My ticket.rb contains:
def self.category_count(user,category)
if user.vendor.nil?
category.tickets.where('store_id = ?', user.store.id)
else
category.tickets.where('vendor_id = ?', user.vendor.id)
end
end
def self.ticket_count(user,ticket)
if user.vendor.nil?
ticket.tickets.where('store_id = ?', user.store.id)
else
ticket.tickets.where('vendor_id = ?', user.vendor.id)
end
end
view:
tickets:
index.html.erb file is:
<% data = {} %>
<% for category in Category.all %>
<% data[:"#{category.name}"] = Ticket.category_count(current_user,category).count %>
<% end %>
<%= pie_chart(data) %>
I would probably start by moving this into a view helper method such as:
def pie_chart_data(user)
Hash[*Category.all.map { |c| [c.name, Ticket.category_count(user, c).count] }]
end
You could then call this in your view
<%= pie_chart(pie_chart_data(current_user)) %>
I have 3 tables called pins (posts), genres (categories) and genre_pin (many to many between pins and genres because pins can have many genres).
I am trying to build the create form, which will allow the user to select as many genres as they want on a pin and save / create. The create works just fine without error, but it is not saving any relationship records to the genre_pin table. I'm pretty sure I may be missing something very obvious here.
I am using the terms 'pins' and 'genres', but you can think of these as the same as 'posts' and 'categories'.
Pin Model
class Pin < ActiveRecord::Base
belongs_to :user
belongs_to :type
has_many :replies
has_many :genre_pins
has_many :genres, :through => :genre_pins
accepts_nested_attributes_for :genres, allow_destroy: true, reject_if: proc { |genre| genre[:id].blank? }
validates :user_id, presence: true
validates :type_id, presence: true
validates :title, presence: true
validates :description, presence: true
end
Genre Model
class Genre < ActiveRecord::Base
has_many :genre_pins
has_many :pins, :through => :genre_pins
validates :title, presence: true
end
GenrePin Model
class GenrePin < ActiveRecord::Base
belongs_to :pin
belongs_to :genre
validates_uniqueness_of :pin_id, :scope => :genre_id
end
Next, in my pin#new view, I have the following form:
Pin New View
<%= form_for :pin, url: pins_path do |f| %>
<div class="small-12 columns">
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<div class="small-12 columns">
<%= f.label :description %>
<%= f.text_area :description, :rows => 10 %>
</div>
<div class="small-12 columns">
<%= f.label :type %>
<%= f.collection_select :type_id, Type.order(:title), :id, :title, include_blank: false %>
</div>
<div class="small-12 columns">
<%= f.label :genres %>
<%= f.collection_select :genre_ids, Genre.order(:title), :id, :title, {}, { :multiple => true } %>
</div>
<div class="small-12 columns">
<%= f.submit "Create Pin", :class => "button" %>
</div>
<% end %>
The view works fine and gets me the available genres from the genres table. However, when I go to save the form, the record (pin) is created, but the relationship with the genres is not. What am I missing here? No errors are thrown, it's just that nothing happens. I am assuming Rails should be creating these PinGenre records through a pins association with genre.
Pin Controller (create)
def create
#pin = Pin.new(pin_params)
#pin.user_id = current_user.id
#pin.save
if #pin.save
flash[:success] = "Pin successfully created"
redirect_to #pin
else
flash[:warning] = #pin.errors.full_messages.to_sentence
redirect_to :action => "new"
end
end
private
def pin_params
params.require(:pin).permit(:title, :description, :type_id, :genre_ids)
end
Are there further steps I need to make here? Do the records need to be explicitly created by myself?
Thanks for your help and patience. I hope I've made this read clearly.
Michael.
Schema.rb
ActiveRecord::Schema.define(version: 0) do
create_table "genre_pins", force: true do |t|
t.integer "pin_id"
t.integer "genre_id"
end
create_table "genres", force: true do |t|
t.string "title", null: false
end
create_table "pins", force: true do |t|
t.integer "user_id", null: false
t.string "title", limit: 160, null: false
t.text "description", null: false
t.decimal "latitude", precision: 10, scale: 8
t.decimal "longitude", precision: 11, scale: 8
t.datetime "created_at"
t.datetime "updated_at"
t.datetime "deleted_at"
t.integer "type_id", null: false
end
create_table "profiles", force: true do |t|
t.integer "user_id", null: false
t.string "first_name", null: false
t.string "last_name", null: false
t.string "gender"
t.string "email"
t.text "bio"
t.decimal "latitude", precision: 10, scale: 8
t.decimal "longitude", precision: 11, scale: 8
t.string "image", limit: 2000
t.datetime "updated_at"
t.datetime "created_at"
t.datetime "deleted_at"
end
create_table "replies", force: true do |t|
t.integer "pin_id", null: false
t.integer "user_id", null: false
t.text "reply", null: false
t.datetime "updated_at"
t.datetime "created_at"
t.datetime "deleted_at"
end
create_table "saves", force: true do |t|
t.integer "pin_id", null: false
t.integer "user_id", null: false
t.datetime "updated_at"
t.datetime "created_at"
t.datetime "deleted_at"
end
create_table "settings", force: true do |t|
t.integer "user_id", null: false
t.boolean "show_email", null: false
t.boolean "show_telephone", null: false
t.integer "timezone", null: false
t.datetime "updated_at"
t.datetime "created_at"
t.datetime "deleted_at"
end
create_table "types", force: true do |t|
t.string "title", null: false
end
create_table "users", force: true do |t|
t.string "provider", limit: 45, null: false
t.string "provider_id", limit: 45, null: false
t.string "oauth_token", limit: 1000
t.datetime "oauth_expires_at"
t.datetime "updated_at"
t.datetime "created_at"
t.datetime "deleted_at"
end
end
Generated View Source
<form accept-charset="UTF-8" action="/pins" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="β" /><input name="authenticity_token" type="hidden" value="3VO3kVR/62JFf6dJs+yyPTPvYJb3fsUSLRFljSE0Jdk=" /></div>
<div class="small-12 columns">
<label for="pin_title">Title</label>
<input id="pin_title" name="pin[title]" type="text" />
</div>
<div class="small-12 columns">
<label for="pin_description">Description</label>
<textarea id="pin_description" name="pin[description]" rows="10">
</textarea>
</div>
<div class="small-12 columns">
<label for="pin_type">Type</label>
<select id="pin_type_id" name="pin[type_id]"><option value="2">Available</option>
<option value="1">Wanted</option></select>
</div>
<div class="small-12 columns">
<label for="pin_genres">Genres</label>
<input name="pin[genre_ids][]" type="hidden" value="" /><select id="pin_genre_ids" multiple="multiple" name="pin[genre_ids][]"><option value="5">Ambient</option>
<option value="4">Electronic</option>
<option value="2">Indie</option>
<option value="3">Jazz</option>
<option value="6">Metal</option>
<option value="1">Rock</option></select>
</div>
<div class="small-12 columns">
<input class="button" name="commit" type="submit" value="Create Pin" />
</div>
</form>
You need to tell your controller that 'genre_ids` is expected to be an array:
params.require(:pin).permit(:title, :description, :type_id, genre_ids: [])
However I expect you will get another error (unknown attribute gener_ids). Let me know if that happen.
I have a form with :
<%= f.collection_select :carburants_id_eq_all, Carburant.order(:name), :id, :name, {}, { multiple: true } %>
When I select only 1 Carburant, it's render me the result normaly, but if I select more than 1 Carburant, the result is empty.
Here's my form in 'index.html.erb' :
<%= search_form_for #search do |f| %>
<%= f.label :title_cont, "Name contains" %>
<%= f.text_field :title_cont %>
<%= f.collection_select :carburants_id_eq_all, Carburant.order(:name), :id, :name, {}, { multiple: true } %>
<%= f.submit "Search" %>
<% end %>
My index action in my 'post_controller.rb':
def index
#search = Post.search(params[:q])
#posts = #search.result(distinct: true)
end
My post.rb, carburant.rb and category.rb model :
class Category < ActiveRecord::Base
attr_accessible :carburant_id, :post_id
belongs_to :carburant
belongs_to :post
end
class Post < ActiveRecord::Base
attr_accessible :content, :title, :carburant_ids
has_many :categories
has_many :carburants, through: :categories
end
class Carburant < ActiveRecord::Base
attr_accessible :name
has_many :categories
has_many :posts, through: :categories
end
My DB shema :
ActiveRecord::Schema.define(:version => 20130114004613) do
create_table "carburants", :force => true do |t|
t.string "name", :limit => nil
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "categories", :force => true do |t|
t.integer "post_id"
t.integer "carburant_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "posts", :force => true do |t|
t.string "title"
t.text "content"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "recherches", :force => true do |t|
t.string "title"
t.integer "carburant_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
end
Thank you by advance.
EDIT 1:
I tried replacing :carburants_id_eq_all by :carburants_id_eq_any in my form just to test, and it work perfecly with the EQ_ANY predicate, but still not with the EQ_ALL.