Obtaining total for associated columns - ruby

I have 4 models, User, ImageSize, Frame and CartItem. The CartItem model holds all the id's for the 3 prior models, so
class CartItem < ActiveRecord::Base
belongs_to :image_size
belongs_to :user
belongs_to :frame
end
create_table "cart_items", force: :cascade do |t|
t.integer "user_id"
t.integer "image_size_id"
t.integer "frame_id"
end
class User < ActiveRecord::Base
has_many :cart_items
end
My first issue here is this doesn't seem correct, as in my association setup, but for now ill address that another time.
A Frame and ImageSize both have a price column and what I am trying to achieve is to get the sum of the price for Frame and ImageSize for the current user so that Ii can show a subtotal for the user that collects the sum for all cartitems
How can I write this query or collect this data? Here is what I have managed to put together, but surely there's an easier way?
def show
#frame_total = CartItem.frame_price_total(current_or_guest_user)
#image_size_total = CartItem.image_size_price_total(current_or_guest_user)
#subtotal = CartItem.subtotal(#frame_total, #image_size_total)
end
class CartItem < ActiveRecord::Base
def self.frame_price_total(u)
#price_array = []
user = includes(:frame).where(user_id: u)
user.each do |f|
#price_array << f.frame.price
end
#price_array.sum
end
def self.image_size_price_total(u)
#price_array = []
user = includes(:image_size).where(user_id: u)
user.each do |f|
#price_array << f.image_size.price
end
#price_array.sum
end
def self.subtotal(image_size_total, frame_size_total)
total = image_size_total + frame_size_total
BigDecimal.new(total).to_s
end
end

It look good but have N+1 query problem
def show
#user_frames = CartItem.includes(:frame).where(user_id: current_user)
frame_array = []
#user_frames.each do |f|
frame_array << f.frame.price
end
#frame_total = frame_array.sum
end

Related

Best way to count ActiveRecord child custom properties?

If I have a shopping cart, and in it is produce (where each produce has a custom expired property), what is the best way to count the expired property? -- is there a better way than this count_expired?
class Cart < ActiveRecord::Base
has_many :produce, :dependent => :delete_all
def count_expired
count = 0
self.produces.each do | produce |
if produce.expired
count ++
end
end
return count
end
end
class Produce < ActiveRecord::Base
belongs_to :cart, class_name: "Cart", touch: true
def expired
return <true or false logic>
end
end

Active Record has_many :messages not saving records properly

I'm building a sinatra app with Active record. The idea is to essentially have a custom email app. Here I have the models User and Message. A User has_many :messages and a Message belongs_to :user. This may be where I have the issue. I also have it set up for a Message belongs_to :user and has_many :users.
here are the models
Now when I create a message in the action controller I am attempting to use the shove methods to put the new message in a user's messages array. If I attempt to "share" this message with multiple users at once with all the user's id's in params( #user = User.find(id) and then user.messages << #new_message) the last user will have the message stored in it's .messages array. However only the last one to be iterated.
class Message < ActiveRecord::Base
belongs_to :user
has_many :users
end
class User < ActiveRecord::Base
validates :username, presence: true, uniqueness: true
has_secure_password
has_many :messages
end
The idea is the writer "owns" the message but can share it with many users. Here are the tables
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :username
t.string :email
t.string :password_digest
end
end
end
class CreateMessages < ActiveRecord::Migration
def change
create_table :messages do |t|
t.string :message
t.string :author
t.integer :user_id
t.integer :user_ids
t.integer :share_id
t.string :title
t.timestamps
end
end
end
# action controller
new_params = {}
new_params[:message] = params["message"]
new_params[:title] = params["title"]
new_params[:author] = params["author"]
new_params[:user_id] = params["user_id"]
#message = Message.create(new_params)
# #share = Share.create
# #message.share_id = #share.id
response.map do |x|
x.messages << #message
x.save!
end
#all = User.all
#user = User.find_by(username: #message.author)
erb :"/user/sent"
I am fairly sure this is because my associations are not set up properly.
You need to have has-and-belongs-to-many relationship between users and messages to implement sharing multiple messages to multiple users. Create an additional record, e.g. MessageShare and do has many to it from both sides:
class MessageShare < ActiveRecord::Base
belongs_to :user
belongs_to :message
end
class Message < ActiveRecord::Base
has_many :message_shares
end
class User < ActiveRecord::Base
has_many :message_shares
end
message_shares table should have user_id and message_id integer columns.

How to access other table's attributes using associations

"Hello everyone" I'm practicing a little bit with rails right now and I'm having issues understanding how to access data with associations. Currently I have 3 tables, Doctors, Patients and Appointments.
class CreateDoctors < ActiveRecord::Migration
def change
create_table :doctors do |t|
t.string :name
t.timestamps null: false
end
end
end
class CreatePatients < ActiveRecord::Migration
def change
create_table :patients do |t|
t.string :name
t.timestamps null: false
end
end
end
class CreateAppointments < ActiveRecord::Migration
def change
create_table :appointments do |t|
t.date :date_appointment
t.references :doctor, index: true, foreign_key: true
t.references :patient, index: true, foreign_key: true
t.timestamps null: false
end
end
end
With this I'm trying to create a form to insert record on Appointments table, this way I can access and get the names for either doctors or patients after submitting the form.
<%= form_for(#appointment) do |f| %>
<%= f.label :date, "Choose a date for your appointment" %>
<%= f.collection_select(:doctor_id, Doctor.all, :id, :name, prompt: true) %>
<%= f.submit %>
<% end %>
The data is being inserted, but when I call the Show "Template" is giving me an error saying that the parameter for doctor in "Nil" even though it has a value.
<%= #appointment.doctor.name %>
And here's the appointment controller data
class AppointmentsController < ApplicationController
def index
end
def show
end
def new
#appointment = Appointment.new
end
def create
#appointment = Appointment.new(appointment_params)
if #appointment.save
redirect_to #appointment
else
render "New"
end
end
private
def appointment_params
params.require(:appointment).permit(:date, :doctor_id, :patient_id)
end
def find_appointment
#appointment = Appointment.find(params[:id])
end
end
What I want to do is basically be able to render or show the names of doctors or patients according to the value of the attributes on the row that is being created after creating a new row with the form.
class Appointment < ActiveRecord::Base
belongs_to :doctor
belongs_to :patient
end
class Doctor < ActiveRecord::Base
has_many :appointments
has_many :patients, through: :appointments
end
class Patient < ActiveRecord::Base
has_many :appointments
has_many :doctors, through: :appointments
end
Thank you for your time!
Maybe I'm missing something but aren't you forgetting to set the #appointment var?
def show
#appointment = Appointment.find(params['id'])
end
(should work if you have a route for this)

Error on data visualize on Ruby on Rails 4

Sorry for my bad english, but I've this problem, for visualize some data from db I've make a Helper file, for manipulate and set the visualization and one Model for extract the rows from a DB and grouped all by date.
the problem is the Model return 0 (on :price) but the DB is full.
THIS IS THE HELPER:
module RoomsHelper
def rooms_chart_data(start = 3.weeks.ago)
rooms_by_day = Room.total_grouped_by_day(start)
(start.to_date..Date.today).map do |date|
{
reservation: date,
price: rooms_by_day[date].try(:total_price) || 0
}
end
end
end
THIS IS THE MODEL:
class Room < ActiveRecord::Base
def self.total_grouped_by_day(start)
rooms = where(reservation: start.beginning_of_day..Time.zone.now)
rooms = rooms.group("date(reservation)")
rooms = rooms.select("sum(price) as total_price, reservation")
rooms.group_by { |o| o.reservation.to_date }
end
end
AND I PASTE THE OUTPUT WRONG:
<div id="year_chart" data-rooms="[{"reservation":"2015-07-22","price":0},
{"reservation":"2015-07-23","price":0},{"reservation":"2015-07-24","price":0},{"reservation":"2015-07-25","price":0},{"reservation":"2015-07-26","price":0},{"reservation":"2015-07-27","price":0},{"reservation":"2015-07-28","price":0},{"reservation":"2015-07-29","price":0},{"reservation":"2015-07-30","price":0},{"reservation":"2015-07-31","price":0},{"reservation":"2015-08-01","price":0},{"reservation":"2015-08-02","price":0},{"reservation":"2015-08-03","price":0},{"reservation":"2015-08-04","price":0},{"reservation":"2015-08-05","price":0},{"reservation":"2015-08-06","price":0},{"reservation":"2015-08-07","price":0},{"reservation":"2015-08-08","price":0},{"reservation":"2015-08-09","price":0},{"reservation":"2015-08-10","price":0},{"reservation":"2015-08-11","price":0},{"reservation":"2015-08-12","price":0}]"
THIS IS THE DB TABLE:
class CreateRooms < ActiveRecord::Migration
def change
create_table :rooms do |t|
t.integer :identity_room
t.integer :price
t.integer :guest
t.datetime :reservation
t.timestamps null: false
end
end
end
AFTER ADD .to_s
module RoomsHelper
def rooms_chart_data(start = 3.weeks.ago)
rooms_by_day = Room.total_grouped_by_day(start)
(start.to_date..Date.today).map do |date|
{
reservation: date,
price: rooms_by_day[date.to_s].try(:total_price) || 0
}
end
end
end
You're converting the date to a string in the group(date(reservation)) and then trying to look up items in the hash with the string coerced date instead, e.g. rooms_by_day[date.to_s] rather than rooms_by_day[date]

Rails -- ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column:

Having major Rails issues currently. I'm creating a School-Dashboard app that takes an xref table called Enrollments that relates Courses and Students.
Any time I try to update a grade for an Enrollment, I constantly get this line
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: UPDATE
"enrollments" SET "grade" = ?, "updated_at" = ? WHERE "enrollments"."" IS NULL
This line does not appear when I update attributes for Courses or Students. Only for the :grade attribute in Enrollment.
For some reason, it isn't being read properly, even though it is a legitimate attribute in my db for Enrollment (check out my schema).
I do all of my preliminary work in the rails sandbox.
Using ruby 2.1.1, Rails 4.1.0.rc1
I'd really love some help here.
Here are my corresponding models
class Student < ActiveRecord::Base
has_many :enrollments
has_many :courses, through: :enrollments
end
class Course < ActiveRecord::Base
has_many :enrollments
has_many :students, through: :enrollments
end
class Enrollment < ActiveRecord::Base
belongs_to :student
belongs_to :course
end
The controllers:
Students
class StudentsController < ApplicationController
def index
#students = Student.all
end
def new
#student = Student.new
end
def show
end
def update
#student.update_attributes(student_params) ? redirect_to #student : render 'edit'
end
def create
#student = Student.new(student_params)
#student.save ? redirect_to #student : render 'new'
end
def destroy
end
def edit
end
private
def student_params
params.require(:student).permit(:first_name, :last_name, :student_number, :email)
end
end
Courses
class CoursesController < ApplicationController
def index
#courses = Course.all
end
def new
#course = Course.new
end
def show
end
def update
#course.update_attributes(course_params) ? redirect_to #course : render 'edit'
end
def create
#course = Course.new(course_params)
#course.save ? redirect_to #course : render 'new'
end
def destroy
end
def edit
# code here
end
private
def course_params
params.require(:course).permit(:course_name, :course_number)
end
end
Enrollments
class EnrollmentsController < ApplicationController
attr_accessor :course_id, :student_id, :grade
def index
#enrollments = Enrollment.all
end
def new
#enrollment = Enrollment.new
end
def create
#enrollment = Enrollment.new(enrollment_params)
#enrollment.save ? redirect_to #enrollment : render 'new'
end
def update
#enrollment.update_attributes(enrollment_params) ? redirect_to #enrollment : render 'edit'
end
def show
end
def destroy
#enrollment.destroy
end
def edit
# code here
end
private
def enrollment_params
params.require(:enrollment).permit(:course_id, :student_id, :grade)
end
end
And finally my schema.rb
ActiveRecord::Schema.define(version: 20140417152720) do
create_table "courses", force: true do |t|
t.string "course_name"
t.integer "course_number"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "enrollments", id: false, force: true do |t|
t.integer "course_id", null: false
t.integer "student_id", null: false
t.decimal "grade", precision: 5, scale: 2
t.datetime "created_at"
t.datetime "updated_at"
end
# noinspection RailsParamDefResolve
add_index "enrollments", ["course_id", "student_id"], name: "index_enrollments_on_course_id_and_student_id"
# noinspection RailsParamDefResolve
add_index "enrollments", ["student_id", "course_id"], name: "index_enrollments_on_student_id_and_course_id"
create_table "students", force: true do |t|
t.string "first_name"
t.string "last_name"
t.string "email"
t.integer "student_number"
t.datetime "created_at"
t.datetime "updated_at"
end
end
Looks like I figured it out on my own!
So here's a bit of rails convention that needs to be addressed. The problem was with my database setup for 'Enrollments'. When I run the command
rails g migration CreateJoinTableEnrollments course student
Rails does too much work for me in my migration file (except for the table name and grade, I added that)
class CreateJoinTableEnrollments < ActiveRecord::Migration
def change
create_join_table :courses, :students, table_name: :enrollments, id: false, force: true do |t|
t.index [:course_id, :student_id], null: false
t.index [:student_id, :course_id], null :false
t.decimal :grade, precision: 5, scale: 2
t.timestamps
end
end
end
In reality, I didn't need any of that. In order to manipulate specific data in a row for Enrollments, there has to be an identifier for that row. With id: false, force: true that option got nullified. I also simplified things with the indexing. I just created regular old columns instead. Now my migration file looks like this.
class CreateJoinTableEnrollments < ActiveRecord::Migration
def change
create_table :enrollments do |t|
t.integer :course_id, null: false
t.integer :student_id, null: false
t.decimal :grade, precision: 5, scale: 2
t.timestamps
end
end
end
And with that, no issues! I've just been breaking my head over that for the past 2 days. Hope this helps anyone else who has this issue.

Resources