please help solve the problem.
table posts
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "body"
end
table tags
create_table "tags", force: :cascade do |t|
t.string "tagname"
end
joined table
create_table "posts_tags", id: false, force: :cascade do |t|
t.integer "post_id"
t.integer "tag_id"
end
model Post
class Post < ActiveRecord::Base
has_and_belongs_to_many :tags
end
model Tag
class Tag < ActiveRecord::Base
has_and_belongs_to_many :posts
end
I need to remove some of the association table posts_tags.
I get from the form set id tags:
[3, 18, 21]
Here is my controller that handles this set id_tags:
def update
if #post.update_attributes(post_params)
#add_new_tags(#post)
p '------------------1'
p params['delete_tags']
p '------------------2'
destroy_tags(params['delete_tags'], #post)
flash[:success] = t :post_updated
redirect_to user_post_path(#user, #post)
else
flash[:error] = t :post_not_updated
render 'edit'
end
end
private
def destroy_tags(tags,post)
tags.each do |tag|
p '=================='
p tag
tag_del = post.tags.find_by_post_id(:post_id => post.id)
if teg_del
post.tags.delete(tag_del)
end
end
end
def post_params
params.require(:post).permit(:delete_tags)
end
as a result, I get the following error message:
undefined method `find_by_post' for #<Tag::ActiveRecord_Associations_CollectionProxy:0x007f097ed78ac8>
the console displays the following message:
Processing by PostsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"jfNoFJVn2dDqIwIdK/SWerFKIPFJ9bTYdWjy4QFBFL8gHrO7TnzhBq4Mcw+uDyDn9atLEAmfcPdlmHBVHMSDHQ==", "post"=>{"title"=>"Corrupti.ggh", "body"=>"Suscipit ut odit labore fugiat quia aliquam."}, "tagnames"=>"", "delete_tags"=>["3", "18", "21"], "commit"=>"Сохранить Post", "locale"=>"ru", "user_id"=>"24", "id"=>"359"}
Post Load (0.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT 1 [["id", 359]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 24]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = ? LIMIT 1 [["remember_token", "15166203712e74cc4638f34991c141f85c04a0e0"]]
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", "24"]]
(0.1ms) begin transaction
(0.0ms) commit transaction
"------------------1"
["3", "18", "21"]
"------------------2"
"=================="
"3"
Completed 500 Internal Server Error in 6ms (ActiveRecord: 0.5ms)
Since you have a join table, the tags table itself doesn't have a post_id, so that find_by won't work. It isn't needed though because post.tags would retrieve all the tags connected to that post anyway.
You get a lot of the functionality for handling habtm relationships and forms out of the box with rails using things like collection_check_boxes where I don't think you need the extra destroy tags method unless I'm misunderstanding what you are trying to accomplish. You might consider adding/removing tag associations to posts like this:
Post - has_and_belongs_to_many :tags
Tag - has_and_belongs_to_many :posts
Post Update Form:
<legend>Tags</legend>
<div class="form-group">
<%= f.collection_check_boxes(:tag_ids, Tag.all, :id, :name) %>
</div>
Make sure to allow :tag_ids => [] in your controller's post_params
The will allow you to update the tags belonging to the post by adding any new tags that were checked and removing any tags that were unchecked in the form and you can have normal create and update controller actions without need for the extra destroy_tags method.
Related
i am building an eCommerce app in rails which has two products Chairs and Bookcabinets, i have created two different controllers and models for chairs and bookcabinets, it all worked now i wanted the user to be able to add these products to a cart so i created a ordering system from scratch by watching a tutorial, it worked for chairs but when i add bookcabinets to shopping_cart model it gives me an error: Couldn't find Chair with 'id'=
Server Log
Started POST "/cart/items" for 127.0.0.1 at 2018-09-26 14:16:43 +0530
(0.7ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
Processing by OrderItemsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"wGAl7gZGXipKt7EzzZT1LuBgn2k8KnlaPagp0cQ3l6pIkr6mx8MdJAuhgkY7EEttrHjTSSpcRjqe0qZ0a2hrAA==", "bookcabinet_id"=>"1", "quantity"=>"40"}
Chair Load (0.8ms) SELECT "chairs".* FROM "chairs" WHERE "chairs"."id" = ? LIMIT ? [["id", nil], ["LIMIT", 1]]
Completed 404 Not Found in 106ms (ActiveRecord: 3.8ms)
ActiveRecord::RecordNotFound (Couldn't find Chair with 'id'=):
app/models/shopping_cart.rb:20:in `add_item'
app/controllers/order_items_controller.rb:8:in `create'
My Order_items_controller
class OrderItemsController < ApplicationController
def index
#items = current_cart.order.items
end
def create
current_cart.add_item(
chair_id: params[:chair_id],
bookcabinet_id: params[:bookcabinet_id],
quantity: params[:quantity]
)
redirect_to cart_path
end
def destroy
current_cart.remove_item(id: params[:id])
redirect_to cart_path
end
end
My Order_controller
class OrdersController < ApplicationController
def new
#order = current_cart.order
end
end
** My Order model**
class Order < ApplicationRecord
has_many :items, class_name: 'OrderItem'
end
My Order_item Model
class OrderItem < ApplicationRecord
belongs_to :order
belongs_to :chair
belongs_to :bookcabinet
end
My Shopping_Cart Model
class ShoppingCart
delegate :sub_total, to: :order
def initialize(token:)
#token = token
end
def order
#order ||= Order.find_or_create_by(token: #token) do |order|
order.sub_total = 0
end
end
def items_count
order.items.sum(:quantity)
end
def add_item(chair_id:, bookcabinet_id:, quantity: 1)
chair = Chair.find(chair_id),
bookcabinet = Bookcabinet.find(bookcabinet_id)
order_item = order.items.find_or_initialize_by(
chair_id: chair_id,
bookcabinet_id: bookcabinet_id
)
order_item.price = chair.price
order_item.price = bookcabinet.price
order_item.quantity = quantity
ActiveRecord::Base.transaction do
order_item.save
update_sub_total!
end
end
def remove_item(id:)
ActiveRecord::Base.transaction do
order.items.destroy(id)
update_sub_total!
end
end
private
def update_sub_total!
order.sub_total = order.items.sum('quantity*price')
order.save
end
end
My Create_order migration
class CreateOrders < ActiveRecord::Migration[5.1]
def change
create_table :orders do |t|
t.string :first_name
t.string :last_name, null: false
t.decimal :sub_total, precision: 15, scale: 2, null: false
t.timestamps
end
end
end
my CreateOrderItems migration
class CreateOrderItems < ActiveRecord::Migration[5.1]
def change
create_table :order_items do |t|
t.belongs_to :order, null: false
t.belongs_to :chair, null: false
t.belongs_to :bookcabinet
t.integer :quantity, null: false
t.decimal :price, precision: 15, scale: 2, null: false
t.timestamps
end
end
end
Routes
get '/cart', to: 'order_items#index'
resources :order_items, path: '/cart/items'
get '/cart/checkout', to: 'orders#new', as: :checkout
patch '/cart/checkout', to: 'orders#create'
Add To Cart Form
<div class="button my-2">
<%= form_tag order_items_path do %>
<%= hidden_field_tag :bookcabinet_id, #bookcabinet.id %>
<%= number_field_tag :quantity, 1 %>
<button type="submit" class="btn cart"><i class="d-inline fa fa-cart-arrow-down" aria-hidden="true"></i><p class="d-inline">Add To Cart</p></button>
<% end %>
Updated Server Log
Started POST "/cart/items" for 127.0.0.1 at 2018-09-27 00:24:13 +0530
(0.6ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
Processing by OrderItemsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"gqwsV8Z/0p74Y8WPLMmfk9vmoSPCFpP+lAtVxxH3KSvWUzYYIFtbQRkAzM5yh5HS/wAzelr90LJW64joFUpGwg==", "bookcabinet_id"=>"1", "quantity"=>"40"}
Bookcabinet Load (0.7ms) SELECT "bookcabinets".* FROM "bookcabinets" WHERE "bookcabinets"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Order Load (0.9ms) SELECT "orders".* FROM "orders" WHERE "orders"."token" = ? LIMIT ? [["token", "20897ec5db2636f5"], ["LIMIT", 1]]
OrderItem Load (0.5ms) SELECT "order_items".* FROM "order_items" WHERE "order_items"."order_id" = ? AND "order_items"."chair_id" IS NULL AND "order_items"."bookcabinet_id" = ? LIMIT ? [["order_id", 4], ["bookcabinet_id", 1], ["LIMIT", 1]]
(0.2ms) begin transaction
Bookcabinet Load (0.3ms) SELECT "bookcabinets".* FROM "bookcabinets" WHERE "bookcabinets"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
(0.4ms) SELECT SUM(quantity*price) FROM "order_items" WHERE "order_items"."order_id" = ? [["order_id", 4]]
(0.2ms) commit transaction
Redirected to http://localhost:3000/cart
Completed 302 Found in 557ms (ActiveRecord: 10.4ms)
Started GET "/cart" for 127.0.0.1 at 2018-09-27 00:24:14 +0530
Processing by OrderItemsController#index as HTML
Order Load (0.5ms) SELECT "orders".* FROM "orders" WHERE "orders"."token" = ? LIMIT ? [["token", "20897ec5db2636f5"], ["LIMIT", 1]]
Rendering order_items/index.html.erb within layouts/application
(0.4ms) SELECT SUM("order_items"."quantity") FROM "order_items" WHERE "order_items"."order_id" = ? [["order_id", 4]]
OrderItem Load (1.1ms) SELECT "order_items".* FROM "order_items" WHERE "order_items"."order_id" = ? [["order_id", 4]]
Rendered order_items/index.html.erb within layouts/application (15.0ms)
CACHE (0.1ms) SELECT SUM("order_items"."quantity") FROM "order_items" WHERE "order_items"."order_id" = ? [["order_id", 4]]
Rendered layouts/_header.html.erb (22.0ms)
Rendered layouts/_footer.html.erb (1.9ms)
Completed 200 OK in 1313ms (Views: 1304.9ms | ActiveRecord: 2.1ms)
You are searching for chair item every time you call add_item method so its throwing an error as you are not passing chair id so you need to modify the method as below
def add_item(chair_id:, bookcabinet_id:, quantity: 1)
chair = Chair.find(chair_id) if chair_id
bookcabinet = Bookcabinet.find(bookcabinet_id) if bookcabinet_id
order_item = order.items.find_or_initialize_by(
chair_id: chair_id,
bookcabinet_id: bookcabinet_id
)
order_item.price = chair.price if chair
order_item.price = bookcabinet.price
order_item.quantity = quantity
ActiveRecord::Base.transaction do
order_item.save
update_sub_total!
end
end
Please feel free to ask if there are any questions regarding the same
I have a problem with my association in ruby on rails.
I have the association :
class Play < ApplicationRecord
has_many :program_plays
has_many :programs, through: :program_plays
end
class ProgramPlay < ApplicationRecord
belongs_to :program
belongs_to :play
end
class Program < ApplicationRecord
has_many :program_plays
has_many :plays, through: :program_plays
accepts_nested_attributes_for :plays
end
When i create a Program i want to associate one or several Play. The Play are create beforehand. The Play have got many attributes whose :title and :id of course.
The program_controller :
class Admin::ProgramsController < Admin::AdminController
def index
#programs = Program.all
end
def new
#program = Program.new
#program.plays.build
end
def create
#program = Program.new(program_params)
if #program.save
redirect_to admin_programs_path, notice: ''
else
end
end
private
def set_program
#program = Program.find(params[:id])
end
def program_params
params.require(:program).permit(:start_date, plays_attributes: [:title])
end
end
The form of Program (with the simple_form gem) :
<%= simple_form_for [:admin, #program] do |f| %>
<%= f.error_notification %>
<%= f.association :plays %>
<%= f.input :start_date, as: :datetime, minute_step: 15, label: 'Heure' %>
<%= f.button :submit, 'Poster', class: 'button red-full' %>
<% end %>
Now when i want to display the Program in my index, i also want to display the title of associated Play. But when i write :
program.plays.title
an error tell me that title is not a method of program... Why ? I don't understand. The association is real because when i puts in the rails console :
program.plays.to_a
i have :
Play Load (5.4ms) SELECT "plays".* FROM "plays" INNER JOIN "program_plays" ON "plays"."id" = "program_plays"."play_id" WHERE "program_plays"."program_id" = $1 [["program_id", 21]]
=> []
Can you see the problem ? I look for it for a longtime ago... I am desperate...
UPDATE :
In the rails console when i do program.plays.first.title :
Play Load (0.5ms) SELECT "plays".* FROM "plays" INNER JOIN "program_plays" ON "plays"."id" = "program_plays"."play_id" WHERE "program_plays"."program_id" = $1 ORDER BY "plays"."id" ASC LIMIT $2 [["program_id", 22], ["LIMIT", 1]]
NoMethodError: undefined method `title' for nil:NilClass
When i do :
program.plays.each do |play|
play.title
end
The result is :
Play Load (0.5ms) SELECT "plays".* FROM "plays" INNER JOIN "program_plays" ON "plays"."id" = "program_plays"."play_id" WHERE "program_plays"."program_id" = $1 [["program_id", 22]]
=> []
Here we can see that title is empty...
UPDATE 2 :
create_table "plays", force: :cascade do |t|
t.string "title"
t.text "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "program_plays", force: :cascade do |t|
t.bigint "program_id"
t.bigint "play_id"
t.index ["play_id"], name: "index_program_plays_on_play_id"
t.index ["program_id"], name: "index_program_plays_on_program_id"
end
create_table "programs", force: :cascade do |t|
t.datetime "start_date"
end
Maybe the problem is here...
UPDATE 3 :
> program = Program.new
=> #<Program:0x00000000068ce8e8 id: nil, start_date: nil>
[15] pry(main)> program.id = 1
=> 1
> program.plays.build(Play.find_by(id: 10))
Play Load (6.2ms) SELECT "plays".* FROM "plays" WHERE "plays"."id" = $1 LIMIT $2 [["id", 10], ["LIMIT", 1]]
ArgumentError: When assigning attributes, you must pass a hash as an argument.
from /home/coeurcoeur/.rvm/gems/ruby-2.5.0/gems/activemodel-5.1.4/lib/active_model/attribute_assignment.rb:28:in `assign_attributes'
> program.save
(0.1ms) BEGIN
(0.3ms) ROLLBACK
=> false
The associations looks good. the problem that you have is that you are calling program.plays.title but have in mind that program.plays will have a collection of plays and not just one so you can't call directly the title there.
it depends on what you are trying to do, for example if you want the title of the first play, you can do something like program.plays.first.title
or if you want to print all the plays title you can do something like
program.plays.each do |play|
play.title
end
and I am assuming program is filled with an existing program in your code
Another problem that you have is that you are not permitting the program_id on the controller so when you create the plays, the attribute program_id that has the association isn't saved because isn't permitted, so just add program_id on your params method like this
def program_params
params.require(:program).permit(:start_date, plays_attributes: [:title, :program_id])
end
I have this model:
class Book < ApplicationRecord
has_many :pages, dependent: :destroy
end
And this one:
class Page < ApplicationRecord
belongs_to :book
end
The migration for the Book is:
class CreateBooks < ActiveRecord::Migration[5.0]
def change
create_table :books do |t|
end
end
end
And the migration for Page is:
class CreatePages < ActiveRecord::Migration[5.0]
def change
create_table :pages do |t|
t.references :book, index: true, null: false
end
end
add_foreign_key :pages, :books, on_delete: :cascade
end
Additionally I got some seeds:
Book.create!(
pages: [
Page.new,
Page.new,
Page.new
]
)
rake db:migrate, rake db:seed and all that jazz. I jump into rails c:
Book.first
Book Load (0.1ms) SELECT "books".* FROM "books" ORDER BY "books"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<Book id: 1>
Cool....now?
Page.count
(0.3ms) SELECT COUNT(*) FROM "pages"
=> 3
Makes total sense. Next:
Book.first.destroy
Book Load (0.2ms) SELECT "books".* FROM "books" ORDER BY "books"."id" ASC LIMIT ? [["LIMIT", 1]]
(0.1ms) begin transaction
Page Load (0.1ms) SELECT "pages".* FROM "pages" WHERE "pages"."book_id" = ? [["book_id", 1]]
SQL (0.1ms) DELETE FROM "pages" WHERE "pages"."id" = ? [["id", 1]]
SQL (0.0ms) DELETE FROM "pages" WHERE "pages"."id" = ? [["id", 2]]
SQL (0.0ms) DELETE FROM "pages" WHERE "pages"."id" = ? [["id", 3]]
SQL (0.1ms) DELETE FROM "books" WHERE "books"."id" = ? [["id", 1]]
Yay! Almost there...after seeding again I do this:
Book.first.delete
Book Load (0.1ms) SELECT "books".* FROM "books" ORDER BY "books"."id" ASC LIMIT ? [["LIMIT", 1]]
SQL (144.0ms) DELETE FROM "books" WHERE "books"."id" = ? [["id", 2]]
WTF?
Page.count
(0.1ms) SELECT COUNT(*) FROM "pages"
=> 3
I know delete does not trigger callbacks, so that dependent: :destroy won't help me here. But the foreign key? Hello? I want referential integrity in my database level!! What am I doing wrong? I've tried more things, like moving the on_delete: :cascade to the field definition:
def change
create_table :pages do |t|
t.references :book, index: true, null: false
end
end
But...nope, same result. I've searched and read the ActiveRecord documentation twice, and a few other questions in SO pointed me to my current setup (which is not the project I'm working on, but rather a newly generated one with the same basic configuration to replicate the error - yea, it fails there too), but I just can't put my finger on what's wrong. Perhaps it's just too late and I'm getting too tired. Help? Does Rails even support this? I'm using v5, far, far ahead of 4.2 where the constraints at db level were integrated. My db/schema.rb looks like this:
ActiveRecord::Schema.define(version: 20160218232358) do
create_table "books", force: :cascade do |t|
end
create_table "pages", force: :cascade do |t|
t.integer "book_id", null: false
t.index ["book_id"], name: "index_pages_on_book_id"
end
end
No trace of foreign keys?
For you're testing you are probably using SQLite, here only mysql, mysql2 and postgres are mentioned therefore I think rails does not support foreign keys on SQLite: http://edgeguides.rubyonrails.org/4_2_release_notes.html#foreign-key-support
It is also stated in another so post: https://stackoverflow.com/a/28801481/4560144
please help solve the problem. i try set the permissions after install 'cancan' and 'cancancan' gems.
schema.rb:
create_table "roles", force: :cascade do |t|
t.string "name"
end
create_table "roles_users", id: false, force: :cascade do |t|
t.integer "role_id"
t.integer "user_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
................................................................
t.datetime "created_at"
t.datetime "updated_at"
end
models:
class User < ActiveRecord::Base
has_and_belongs_to_many :roles
def role?(role)
return !!self.roles.find_by_name(role.to_s.camelize)
end
end
class Role < ActiveRecord::Base
has_and_belongs_to_many :users
end
app/models/ability.rb:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.role? :admin
can :manage, :all
elsif user.role? :manager
can :manage, :review
cannot :manage, :user
elsif user.role? :user
cannot :manage, :all
end
end
end
i filled my roles table follow values:
id name
0 user
1 manager
2 admin
i filled my join table 'roles_users' follow values:
role_id user_id
2 2
1 3
0 1
but after run application permissions is no effect. the problem is that managers can change info for all users. it is not right. please help to fix it
ps:
my user controller:
class UserController < ApplicationController
load_and_authorize_resource
end
after manager change info about user via adminpanel, console output follow:
Started GET "/admin/users/1" for 127.0.0.1 at 2015-09-19 20:53:47 +0300
Processing by Admin::UsersController#show as HTML
Parameters: {"id"=>"1"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1 [["id", 3]]
(0.1ms) SELECT COUNT(*) FROM "active_admin_comments" WHERE "active_admin_comments"."resource_type" = ? AND "active_admin_comments"."resource_id" = ? AND "active_admin_comments"."namespace" = ? [["resource_type", "User"], ["resource_id", "1"], ["namespace", "admin"]]
CACHE (0.0ms) SELECT COUNT(*) FROM "active_admin_comments" WHERE "active_admin_comments"."resource_type" = ? AND "active_admin_comments"."resource_id" = ? AND "active_admin_comments"."namespace" = ? [["resource_type", "User"], ["resource_id", "1"], ["namespace", "admin"]]
Rendered /home/kalinin/.rvm/gems/ruby-2.0.0-p598/bundler/gems/activeadmin-893b46c6530c/app/views/active_admin/resource/show.html.arb (316.7ms)
Completed 200 OK in 321ms (Views: 318.9ms | ActiveRecord: 0.3ms)
You don't need that .camelize call in your role? method, since all your roles in db are stored in lower-case (manager) and not in camelCase (ManagerOfTheApplication).
It seems your Admin::UsersController is located in active_admin. Try to enable active_admin and can_can integration:
config.authorization_adapter = ActiveAdmin::CanCanAdapter
Look into the link above on the details.
I'm experiencing some pretty slow load times with one of the pages of my Rails app. I have made some headway in my attempts to improve performance, but not as much as I had hoped. I'm wondering if I'm doing something silly to cause myself this grief or if anyone could offer me advice to better optimize my models/migrations/queries/views. I'm running Rails 3.1 and using PostgreSQL. This is what I've tried so far:
added includes(:items => :category) to the controller's query, as an attempt to fetch all the ItemCategories in advance, rather than running additional queries when outfits_helper requires them.
rendering partials as collections, rather than iterating through arrays/relations manually
added indexes to tables that are being accessed by the problematic controller action / view
outfits table: indexes for user_id and category_id
items table: indexes for user_id and category_id
outfit_items table: indexes for item_id, outfit_id, and [outfit_id, item_id]
Below are the relevant parts of my code:
Controller
# outfits_controller.rb
NUM_OUTFITS_PER_PAGE = 8
def other_users_outfits
# using Kaminari for pagination
#outfits = Outfit.does_not_belong_to_user(current_user).in_category_with_id(category_id).includes(:items => :category).page(params[:page]).per(NUM_OUTFITS_PER_PAGE)
end
Models
# outfit.rb
has_many :items, :through => :outfit_items
belongs_to :category, :class_name => 'OutfitCategory'
scope :does_not_belong_to_user, proc {|user| where('user_id != ?', user.id) }
scope :in_category_with_id, proc {|cat_id|
if cat_id.blank?
scoped
else
where(:category_id => cat_id)
end
}
.
# outfit_item.rb
belongs_to :item
belongs_to :outfit
.
# item.rb
has_many :outfit_items
has_many :outfits, :through => :outfit_items
belongs_to :category, :class_name => 'ItemCategory'
.
# item_category.rb
has_many :items, :foreign_key => 'category_id'
.
outfit_category.rb
has_many :outfits, :foreign_key => 'outfit_id'
Migrations
# create_outfits.rb
def self.up
create_table :outfits do |t|
t.column :category_id, :integer
t.column :user_id, :integer
t.timestamps
end
add_index :outfits, :category_id
add_index :outfits, :user_id
end
.
# create_outfit_items.rb
def self.up
create_table :outfit_items, :id => false do |t|
t.column :item_id, :integer
t.column :outfit_id, :integer
t.timestamps
end
add_index :outfit_items, :item_id
add_index :outfit_items, :outfit_id
add_index :outfit_items, [:outfit_id, :item_id]
end
.
# create_items.rb
def self.up
create_table :items do |t|
t.column :category_id, :integer
t.column :user_id, :integer
t.timestamps
end
add_index :items, :category_id
add_index :items, :user_id
end
.
# create_item_categories.rb
def self.up
create_table :item_categories do |t|
t.column :name, :string
t.column :category_type, :string
t.timestamps
end
end
.
# create_outfit_categories.rb
def self.up
create_table :outfit_categories do |t|
t.column :name, :string, :limit => 100, :null => false
t.timestamps
end
end
Views
# other_users_outfits.html.haml
= render :partial => 'other_users_outfit', :collection => outfits, :as => :outfit
.
# _other_users_outfit.html.haml
.outfit
.primary-items
= render :partial => 'other_users_outfit_primary_item', :collection => primary_outfit_items_top_to_bottom(outfit), :as => :item
.secondary-items
= render :partial => 'other_users_outfit_secondary_item', :collection => secondary_outfit_items(outfit), :as => :item
.
# _other_users_outfit_primary_item.html.haml
= image_tag item.image_url(:outfit_item), :class => 'primary-outfit-item'
.
# _other_users_outfit_secondary_item.html.haml
= image_tag item.image_url(:baby_thumb), :class => 'secondary-outfit-item'
Helper
# outfits_helper.rb
def primary_outfit_items_top_to_bottom(outfit)
primary_items = []
primary_items.push(outfit_item_in_category(outfit, 'Tops'))
primary_items.push(outfit_item_in_category(outfit, 'Full-lengths'))
primary_items.push(outfit_item_in_category(outfit, 'Bottoms'))
primary_items.push(outfit_item_in_category(outfit, 'Footwear'))
primary_items.compact
end
def secondary_outfit_items(outfit)
outfit.items.select{|item| item.category.category_type == 'secondary' }
end
def outfit_item_in_category(outfit, cat_name)
outfit.items.select{|item| item.category.name == cat_name }.first
end
Console Output
User Load (1.0ms) SELECT DISTINCT users.id, users.* FROM "users" WHERE "users"."id" = 3 LIMIT 1
(0.5ms) SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM "outfits" WHERE (user_id != 3) LIMIT 8 OFFSET 0) subquery_for_count
Outfit Load (0.7ms) SELECT DISTINCT outfits.id, outfits.* FROM "outfits" WHERE (user_id != 3) ORDER BY outfits.created_at DESC LIMIT 8 OFFSET 0
OutfitItem Load (0.6ms) SELECT "outfit_items".* FROM "outfit_items" WHERE "outfit_items"."outfit_id" IN (28, 27, 26, 25, 24, 23, 22, 21)
Item Load (2.2ms) SELECT DISTINCT items.id, items.*, "items".* FROM "items" WHERE "items"."id" IN (18, 20, 23, 7, 6, 30, 4, 1, 17, 5, 15, 12, 9, 29, 10, 19, 3, 8, 13) ORDER BY items.created_at DESC
ItemCategory Load (0.5ms) SELECT "item_categories".* FROM "item_categories" WHERE "item_categories"."id" IN (4, 6, 2, 1, 3, 7)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (1.2ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.1ms)
Rendered outfits/_other_users_outfit_primary_item.html.haml (2.8ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (1.4ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.9ms)
Rendered outfits/_other_users_outfit_primary_item.html.haml (1.2ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.3ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.9ms)
Rendered outfits/_other_users_outfit_primary_item.html.haml (2.7ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.8ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.7ms)
Rendered outfits/_other_users_outfit_primary_item.html.haml (1.2ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (1.1ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.3ms)
Rendered outfits/_other_users_outfit_primary_item.html.haml (2.2ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.8ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.3ms)
Rendered outfits/_other_users_outfit_primary_item.html.haml (3.4ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.3ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.0ms)
Rendered outfits/_other_users_outfit_primary_item.html.haml (2.6ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.3ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.3ms)
Rendered outfits/_other_users_outfit_primary_item.html.haml (2.1ms)
Rendered outfits/_other_users_outfit.html.haml (56.3ms)
(0.5ms) SELECT COUNT(*) FROM "outfits" WHERE (user_id != 3)
Rendered outfits/other_users.html.haml within layouts/application (2073.5ms)
Role Load (0.4ms) SELECT "roles".* FROM "roles" WHERE "roles"."name" = 'admin' LIMIT 1
(0.4ms) SELECT 1 FROM "roles" INNER JOIN "roles_users" ON "roles"."id" = "roles_users"."role_id" WHERE "roles_users"."user_id" = 3 AND "roles"."id" = 1 LIMIT 1
Rendered layouts/_top_nav.html.haml (1.0ms)
(0.6ms) SELECT COUNT(*) FROM "items" WHERE "items"."user_id" = 3
Rendered layouts/_points_display.html.haml (5.7ms)
Outfit Load (0.6ms) SELECT DISTINCT outfits.id, outfits.* FROM "outfits" WHERE "outfits"."user_id" = 3 ORDER BY outfits.created_at DESC
OutfitCategory Load (0.3ms) SELECT "outfit_categories".* FROM "outfit_categories"
.
Any help would be much appreciated!
The only thing I see that could stand improvement is the code in the helper. Is there a reason why you're doing this:
def outfit_item_in_category(outfit, cat_name)
outfit.items.select{|item| item.category.name == cat_name }.first
end
instead of pushing this code to a scope in the Item model? If you have a lot of items, you're basically doing a SELECT * on them and then filtering in Ruby.