I'm trying to make a project like cafe search service based on social network, and I want to sort Cafe array by points which other users gave.
class Cafe < ActiveRecord::Base
belongs_to :user
has_many :posts, dependent: :destroy
has_many :tags, dependent: :destroy
has_many :payments, dependent: :destroy
has_many :payinfos, dependent: :destroy
mount_uploader :image,CafeimageuploaderUploader
mount_uploader :thumnail,CafeimageuploaderUploader
geocoded_by :address
after_validation :geocode
def avg
total = 0
posts.each do |c|
total += c.score
end
if posts.count == 0
0
else
total.to_f / posts.count
end
end
end
this is Cafe model, 'avg' is point average that users gave.
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :content , null: false, default: ""
t.string :image
t.string :address , null: false, default: "위치정보 없음"
t.string :hashstr
t.datetime :writtentime
t.integer :user_id
t.integer :cafe_id , null: false, default: 0
t.integer :score, default:0
t.timestamps null: false
end
end
end
Here's post columns.
What I want to do is sort new Cafe array by this avg action.(is it called model action, right??)
give me some advice, thank you.
I would add an attribute average_score to cafe model and a after_save callback method to post model. If a post will be safed you take all posts of a cafe (by the foreign key), calculate the average score and save it to the cafe or you trigger a method of the cafe model to do that. So you can sort your cafes easily.
Let use db query, it is more efficient. (assume Cafe's table name is cafes)
Cafe.joins("LEFT OUTER JOIN (SELECT cafe_id, AVG(score) AS avg_score
FROM posts
GROUP BY cafe_id
) AS temp
ON cafes.id = temp.cafe_id
").order('temp.avg_score DESC NULLS LAST')
Ideally, we calculate the average score in a temporary table first then join with Cafe via cafe_id. After that, we can easily use order with avg_score.
Because we are using LEFT OUTER JOIN, the avg_score of cafe which doesn't have any post will be NULL, so we use NULLS LAST to ensure that cafe will be in the end.
Related
Hi Im creating an ec site in my rails.
My migration: (Item) has :name and :price. (Basket_Item) has :item_id(fk), :basket_id(fk) and :quantity.
The system User will add some items to their basket. So Basket_items is JOIN Table between (Item) and (Basket) see like below.
What I want to do:
Get a price of Item and get a quantity from Basket_Items which is selected by user. Then I want to create #total_price = item_price * item_quantity.
Can anyone help me to create the #total_price please.
This is my a try code but it doesn't work on rails console.
Basket_items
class CreateBasketItems < ActiveRecord::Migration[5.2]
def change
create_table :basket_items do |t|
t.references :basket, index: true, null: false, foreign_key: true
t.references :item, index: true, null: false, foreign_key: true
t.integer :quantity, null: false, default: 1
t.timestamps
end
end
end
///
Items
class CreateItems < ActiveRecord::Migration[5.2]
def change
create_table :items do |t|
t.references :admin, index: true, null: false, foreign_key: true
t.string :name, null: false, index: true
t.integer :price, null: false
t.text :message
t.string :category, index: true
t.string :img
t.string :Video_url
t.text :discription
t.timestamps
end
end
end
///
This is my try a code but it doesn't work on rails console.
basket = current_user.prepare_basket
item_ids = basket.basket_items.select(:item_id)
items = basket.items.where(id: item_ids)
items_price = items.select(:price)
items_quantity = basket.basket_items.where(item_id: item_ids).pluck(:quantity)
def self.total(items_price, items_quantity)
sum(items_price * items_quantity)
end
#total_price = basket.total(items_price, item_quantity)
You provided only migration files, so my answer will be based on some assumptions:
So Basket_items is JOIN Table between (Item) and (Basket) - taking into account the logic of baskets and items, it means that you have many-to-many relation between Item & Basket through BasketItem as follow:
# basket.rb
class Basket < ApplicationRecord
belongs_to :user
has_many :basket_items
has_many :items, through: :basket_items
end
#item.rb
class Item < ApplicationRecord
has_many :baskets_items
has_many :baskets, through: :baskets_items
end
#basket_item.rb
class BasketItem < ApplicationRecord
belongs_to :basket
belongs_to :item
end
I'm not sure what prepare_basket on user instance do, just make sure that you get the right basket from this method.
With this configuration the total price can be calculated with one request as follow:
#total_price = basket.items.sum('items.price * basket_items.quantity')
or define it inside a model:
# basket.rb
class Basket < ApplicationRecord
belongs_to :user
has_many :basket_items
has_many :items, through: :basket_items
def total_price
items.sum('items.price * basket_items.quantity')
end
end
basket = get_user_basket # find basket you gonna work with
#total_price = basket.total_price
Create some basket, items, and basket_items (this one will be created automatically if you create an item with basket.items.create(params)) in console and investigate the resulting SQL query:
SELECT SUM(items.price * basket_items.quantity) FROM "items" INNER JOIN "basket_items" ON "items"."id" = "basket_items"."item_id" WHERE "basket_items"."basket_id" = ?
Read more about has_many :through association in Rails.
I am trying to come up with a query for a many-to-many relationship with filtering for a certain field in the many-to-many table while ordering by a field in an associated table.
How do I get all the active firm_emps of a specific firm and order the firm_emps by user's name?
user.rb
Class User < ApplicationRecord
has_many :firm_emps, :dependent => :destroy
has_many :firms, through: :firm_emps
end
user.rb migration file
...
t.string :name
t.boolean :active
...
firm_emp.rb
Class FirmEmp < ApplicationRecord
belongs_to :firm
belongs_to :user
end
firm_emp.rb's migration file
...
t.belongs_to :user, index: true
t.belongs_to :firm, index: true
t.boolean :admin, default: false
t.boolean :active, default: true
...
firm.rb
Class Firm < ApplicationRecord
has_many :firm_emps, dependent: :destroy
has_many :users, through: :firm_emps
end
Firm.rb's migration file
...
t.string :full_name
t.boolean :active
...
I've tried the following queries in rails console:
f = Firm.first
f.users.where(active: true).order('users.name asc')
# But this filters on User's table field active: true and not the FirmEmps table field active: true
f.users.joins(:firmemps).where(active: true).order('users.name asc')
# Just doesn't work
f.firm_emps.active.order('firm_emps.active')
# But i can't order by user's field 'name'
EDIT:
#PragyaSriharsh's and #ArunEapachen's answers worked.
Try it:
f.users.joins(:firm_emps).where('firm_emps.active=?', true).order('users.name asc')
If it doesn't work use sort_by method.
Try the following.
f = Firm.first
f.users.where('firm_epms.active = ?' , true).order('users.name asc')
I'm building a tech-specific pricegrabber-like web app, and I have a model that carries params that are common in all products. This model is called Product. Then I have one model for each type of product that I'm going to work with, for example, I'm now trying to build the first specific model, which is Videocard. So, the Product model always must have one Specific model, in this case Product-Videocard.
At this moment I'm stuck finding a way to make a product and a specific model always come tied together whenever I reach to them, be it in an index view, show view, form_for, a search, etc. But I can't picture in my head how a form will create an item and its specifications and insert a foreign key into another model with only one submit request.
Below are both models and the migrations for each:
class Product < ApplicationRecord
#belongs_to :productable, :polymorphic => true
has_one :videocard, dependent: :destroy
# Comment for this Stackoverflow question: the way I'm thinking I
# should have to make tons of has_one associations, for the other
# products. Is there a DRY way to do this?
has_many :prices, through: :stores
validates :platform, presence: { message: "should be specified." }
validates :name, presence: { message: "should be specified." }
validates_associated :videocard
end
class Videocard < ApplicationRecord
belongs_to :product
end
Migrations (shortened to make this question as clear as possible):
class CreateProducts < ActiveRecord::Migration[5.0]
def change
create_table :products do |t|
t.references :productable, polymorphic: true, index: true
t.string :name
t.string :image
t.string :partnum
t.string :manufacturer
t.string :platform #mobile, desktop, server, laptop
t.timestamps
end
end
end
class CreateVideocards < ActiveRecord::Migration[5.0]
def change
create_table :videocards do |t|
t.references :product, index: true
t.integer :memory
t.string :interface
# [...lots of attributes...]
t.integer :displayport
t.integer :minidisplayport
t.integer :tdp
t.timestamps
end
end
end
Also how can I make it so that Product only needs one has_one association, instead of using multiple ones. Remember that Videocard will have one type of specification, Memory will have other, and so on.
I created a new model called 'hashtags' and a new table in my database
Here's the schema.db
create_table "hashtags", :force => true do |t|
t.string "hashtags"
t.integer "post_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
hashtags.rb
class Hashtags < ActiveRecord::Base
attr_accessible :hashtags, :post_id
end
def create
hashtag_regex = /\b#\w\w+/
#post = current_user.posts.build(params[:post])
#post.hashtags = #post.text.scan(hashtag_regex)
end
Inside the post model, this is what I've added
class Post < ActiveRecord::Base
attr_accessible :content
belongs_to :user
has_many :hashtags, dependent: :destroy
For all posts where there's a hashtag (or could be 2+ hashtags), I want to list them in descending order by date in a new page called '/hashtags'.
So this is how I want it to show in view
4/30/2013
#tag3 #tag2 by user2
#tag1 by user5
4/29/2013
#tagz by user10
#tagx #tagy by user3
4/25/2013
#tagz #tagy #tagx by user2
Inside views\static_pages\hashtags.html.erb
I'm trying to create this view, but how could I best go about this?
To save the hash tags you need a before save callback method, and get rid of that logic in the controller.
class Post < ActiveRecord::Base
after_save :process_hashtags
def process_hashtags
hashtag_regex = /\B#\w\w+/
text_hashtags = text.scan(hashtag_regex)
text_hashtags.each do |tag|
hashtags.create name: tag
end
end
end
This callback will iterate through the array of hashtags that it finds in the text, and create a Hashtag model and associate it with the Post model.
Let me know if it works, as it might need some adjustments.
This is how you would dot it in your Model: For
class Post < ApplicationRecord
has_many :hashtags, dependent: :destroy
after_commit :process_hashtags
def process_hashtags
hashtag_regex = (/#\w+/)
text_hashtags = content.to_s.scan(hashtag_regex)
text_hashtags.each do |name|
HashTag.create name: name, address: user.country
end
end
end
Model for # for hashTags
class HashTag < ApplicationRecord
belongs_to :post
end
I'm not sure what's going on here. I have a scope I'm trying to create that works with my association:
class Subscription < ActiveRecord::Base
belongs_to :subscriber, :class_name => "User"
belongs_to :subscribable, :polymorphic => true
end
create_table :products do |t|
t.string :name
t.decimal :price
t.decimal :cost_per_unit
t.integer :user_id
end
create_table :subscriptions do |t|
t.string :name
t.decimal :price
t.decimal :cost_per_unit
t.integer :subscriber_id
t.integer :subscribable_id
t.string :subscribable_type
end
class Product < ActiveRecord::Base
has_many :subscriptions, :as => :subscribable, :dependent => :destroy
def self.lower_prices
Product.includes(:subscriptions).
where("products.price < subscriptions.price OR products.cost_per_unit < subscriptions.cost_per_unit" )
end
end
I'm trying to compare the lower price of the Product to the Subscription but this gives me the error:
ActiveRecord::StatementInvalid in Pages#subscribed_products
PGError: ERROR: missing FROM-clause entry for table "subscriptions"
LINE 1: ... WHERE (user_id != 2) AND (products.price < subscripti...
^
: SELECT COUNT(*) FROM "products" WHERE (user_id != 2) AND (products.price < subscriptions.price OR products.cost_per_unit < subscriptions.cost_per_unit)
What's wrong here?
The includes method doesn't do exactly what you think. Substitute joins for includes and it should Do What You Mean:
Product.joins(:subscriptions).
where("products.price < subscriptions.price OR products.cost_per_unit < subscriptions.cost_per_unit" )
or perhaps:
Product.includes(:subscriptions).joins(:subscriptions).
where("products.price < subscriptions.price OR products.cost_per_unit < subscriptions.cost_per_unit" )
joins translates to a JOIN in the resulting SQL query, so you can perform WHERE clauses on the joined table. include just asks Active Record to perform another query to select all the related records in the given table. If you do both together, Active Record creates a (rather long) all-in-one that both joins the two tables and uses the results to create both sets of objects.