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.
Related
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')
Our Rails app works with the following models:
class User < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :calendars, through: :administrations
end
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
end
class Calendar < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :users, through: :administrations
end
And here are our migrations:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email
...
t.integer :total_calendar_count
t.integer :owned_calendar_count
t.timestamps null: false
end
end
end
class CreateAdministrations < ActiveRecord::Migration
def change
create_table :administrations do |t|
t.references :user, index: true, foreign_key: true
t.references :calendar, index: true, foreign_key: true
t.string :role
t.timestamps null: false
end
end
end
class CreateCalendars < ActiveRecord::Migration
def change
create_table :calendars do |t|
t.string :name
t.timestamps null: false
end
end
end
When a new #calendar is created, we need to increment :total_calendar_count and :owner_calendar_count by one in the User table.
We tried this in the CalendarsController:
class CalendarsController < ApplicationController
def create
#calendar = current_user.calendars.create(calendar_params)
current_user.total_calendar_count += 1
current_user.owned_calendar_count += 1
current_user.administrations.find_by(calendar_id: #calendar.id).update(role: 'Creator')
...
end
But it does not seem to update :total_calendar_count and :owner_calendar_count by one in the User table.
Are we missing a step here? Should we use an update action instead?
The actual problem in your code is that you don't then save the user.
So you update the counter... but this changes it on the local instance... and then after the controller action is done the change you made just disappears.
if you wanted to keep your code the way it is, you could do:
current_user.save
at the end.
but I'd advise you to look into the counter_cache, because it's the Rails way.
Also I'll point out that you haven't checked that the calendar successfully got created, before incrementing that counter... it's possible that it could fail a validation and not really have been created... you need to check for that first.
I have a best idea to solve your problems is as below....
Create a method that will call on the creating of calendar with the callbacks of model like as below...
Add the below inside the calendar model just after the validation and ORM relations
after_create :increment_counter
def increment_counter
calendar_user = self.user
calendar_user.update(:total_calendar_count += 1, :owned_calendar_count += 1 )
end
With the above code you don't need to do anything. It will increment the counter of calendar on every new entry of calendar.
I have 3 tables: proposals, items/proposals (items is nested inside proposals) and invoices.
I want to create invoices for those items in the proposals that got approved. How would the associations for these look like? Also, how would I set up the invoices form to choose only those items that got approved by the client?
Consider creating two different line items models for Proposal and Invoice.
class Proposal < ActiveRecord::Base
has_many :proposal_line_items
end
class ProposalLineItem < ActiveRecord::Base
belongs_to :proposal
end
class Invoice < ActiveRecord::Base
has_many :invoice_line_items
end
class InvoiceLineItem < ActiveRecord::Base
belongs_to :invoice
end
You can consider having an "approved" attribute in proposal line items. In the invoice form, you can show proposal line items approved by the client.
The suggestion of having separate line items for Proposal and Invoice is based on ERP data modeling principles to maintain the integrity of Invoice.
Update
For example here are the sample migrations for the models suggested
class CreateProposalLineItems < ActiveRecord::Migration
def change
create_table :proposal_line_items do |t|
t.references :proposal, index: true, foreign_key: true
t.string :name
t.integer :approved
t.timestamps null: false
end
end
end
class CreateProposals < ActiveRecord::Migration
def change
create_table :proposals do |t|
t.string :name
t.timestamps null: false
end
end
end
class InvoicesController < ActionController
def new
#approved_items = Proposal.find(params[:proposal_id]).proposal_line_items.where(:approved => 1)
end
end
You can iterate over the #approved_items in your view and display it to users.
V
I have a Contract model that has_many Task_Orders. I am trying to render a view where if I click "Show" for Contract line item, it will display a list of Task_Orders that belong to that Contract.
Here is my Contract schema:
create_table "contracts", force: true do |t|
t.string "contractId"
t.string "contractName"
Here is my Task Order schema:
create_table "task_orders", force: true do |t|
t.integer "contract_Id", limit: 255
t.string "task_orderId"
t.string "task_orderName"
Here is my Contract model:
class Contract < ActiveRecord::Base
has_many :task_orders
end
Here is my Task Order model:
class TaskOrder < ActiveRecord::Base
belongs_to :contract
end
I am not entirely sure how to work with the controller and view to make it happen.... please help. I am using Rails 4.0
Thank you.
foreign_key
Firstly, you need to ensure your foreign_keys are assigned for your associations:
#app/models/task_order.rb
Class TaskOrder < ActiveRecord::Base
belongs_to :contract, primary_key: "contractID", foreign_key: "contract_Id"
end
#app/models/contract.rb
Class Contract < ActiveRecord::Base
has_many :task_orders, primary_key: "contractID", foreign_key: "contract_Id"
end
--
Controller
This should allow you to call the required data from your controller:
#app/controllers/contracts_controller.rb
Class ContractsController < ApplicationController
def show
#contract = Contract.find params[:id]
end
end
#app/views/contracts/show.html.erb
<% for order in #contract.task_orders do %>
<%= order.id %>
<% end %>
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