missing attribute error Rails - ruby-on-rails

I cant seem to get the result of a scope to display in my view as i get this error message.I am trying to get all of the memberships amounts for the day added up and displayed as a total
missing attribute: membership_id
My Models and scope
class Member < ActiveRecord::Base
belongs_to :membership
accepts_nested_attributes_for :membership
attr_accessible :membership_id, :forename, :middlename, :surname, :house_no, :house_name, :street, :town, :postcode, :home_tel, :mobile_tel, :work_tel, :email, :start_date, :expiry_date
scope :new_memberships_cash_today, ->() {
joins(:membership).where(:start_date => Date.today).select('ROUND(SUM(memberships.cost), 2)')
}
end
class Membership < ActiveRecord::Base
has_many :members, :dependent => :destroy
attr_accessible :membership_type, :cost
end
And then my view
columns do
#Total amount in £ for New Memberships today
column do
panel "Cash Today", :class => 'NewAmountMemberships' do
table_for Member.new_memberships_cash_today do
column 'Total cash' do |c|
c.membership.map { |e| [e.cost, e.id] }
end
end
end
end
end
After some reading it would seem that there may be an issue with my select call in the scope, as i need to specify all of the models attributes to make a successful call with Active Record?
As i am performing a sum within the select i am unsure how to add more attributes, if this is even the case
Any help appreciated
i have run the scope in the the console and this is what is returned
Member Load (0.1ms) SELECT ROUND(SUM(memberships.cost), 2) FROM `members` INNER JOIN `memberships` ON `memberships`.`id` = `members`.`membership_id` WHERE `members`.`start_date` = '2013-12-13'
=> #<ActiveRecord::Relation [#<Member id: nil>]>

I wouldn't do this in a scope, but in a helper method. That way you can grab the associated records and just call your method to return the total.
Something along the lines of this:
def membership_sum(memberships = [])
sum = 0
memberships.each { |membership| sum += membership.cost }
sum.round
end
Now, store the associated records in a #memberships variable (from within your controller) and then, in your view, use <%= membership_sum(#memberships) %>

Related

Rails 5 is inserting instead of updating when changing the value of attribute in the join table

I have a HABTM relationship using a has_many :through association. I'm having trouble updating the attribute in the join table, because instead of updating the record it just inserts a new record in the table, creating duplicates.
I tried using the UNIQUE constraint when creating the index and adding a validation, but now when I update a record I get a validation error or the error:
ActiveRecord::RecordNotUnique in BatchesController#update
Mysql2::Error: Duplicate entry
To provide some context:
I have 4 tables: manufacturing_orders, order_products, batches and batches_order_products (join table).
A manufacturing_order have many order_products.
Also a manufacturing_order have many batches.
Batches have many order_products.
When I create a batch I copy all the order_products that belongs to the same manufacturing_order, and in the form I can assign a quantity for each of then.
So creating looks like working fine, but when I update any quantity it just inserts the whole relation again instead of updating the existing one.
Model manufacturing_order.rb:
class ManufacturingOrder < ApplicationRecord
has_many :batches, inverse_of: :manufacturing_order, dependent: :destroy
has_many :order_products, inverse_of: :manufacturing_order, dependent: :destroy
accepts_nested_attributes_for :order_products
accepts_nested_attributes_for :batches
end
Model order_product.rb:
class OrderProduct < ApplicationRecord
belongs_to :manufacturing_order
has_many :batches_order_products
has_many :batches, :through => :batches_order_products
end
Model batch.rb:
class Batch < ApplicationRecord
belongs_to :manufacturing_order
has_many :batches_order_products
has_many :order_products, :through => :batches_order_products
accepts_nested_attributes_for :batches_order_products
end
Model batches_order_product.rb:
class BatchesOrderProduct < ApplicationRecord
belongs_to :batch
belongs_to :order_product
validates :batch_id, uniqueness: { scope: :order_product_id }
end
Controller batches_controller.rb:
class BatchesController < ApplicationController
def new
manufacturing_order = ManufacturingOrder.find(params[:manufacturing_order_id])
order_products = manufacturing_order.order_products
#batch = Batch.new({
manufacturing_order: manufacturing_order,
order_products: order_products
})
end
def create
#batch = Batch.new(load_params)
if #batch.save
flash[:notice] = crud_success
redirect_to action: :index
else
flash[:error] = #batch.errors.full_messages.to_sentence
render action: :new
end
end
def edit
#batch = Batch.find(params[:id])
end
def update
#batch = Batch.find(params[:id])
if #batch.update_attributes(load_params)
flash[:notice] = crud_success
redirect_to action: :index
else
flash[:error] = #batch.errors.full_messages.to_sentence
render action: :edit
end
end
private
def load_params
params.require(:batch)
.permit(:name,
:date,
:manufacturing_order_id,
:status,
order_products: [],
order_products_ids: [],
batches_order_products_attributes: [:id, :quantity, :order_product_id]
)
end
end
This is the form in batches:
= bootstrap_form_for([#batch.manufacturing_order, #batch]) do |f|
= f.hidden_field :manufacturing_order_id
= f.text_field :name, label: 'Name'
= f.text_field :date
table
thead
tr
th= "Product"
th= "Quantity"
tbody
= f.fields_for :batches_order_products do |bop|
= bop.hidden_field :order_product_id
tr
td
= bop.object.order_product.name
td
= bop.text_field :quantity
= f.submit 'Save'
Any help will be very much appreciated. Thanks!
UPDATE:
These are the params passed when submitting the edit form. Any clue?
{"utf8"=>"✓",
"_method"=>"patch",
"batch"=>
{"manufacturing_order_id"=>"8",
"name"=>"MAS",
"date"=>"07/05/2020",
"batches_order_products_attributes"=>
{"0"=>{"order_product_id"=>"12", "quantity"=>"77777777", "id"=>""},
"1"=>{"order_product_id"=>"13", "quantity"=>"9.0", "id"=>""},
"2"=>{"order_product_id"=>"14", "quantity"=>"7.0", "id"=>""}}},
"commit"=>"Guardar",
"manufacturing_order_id"=>"8",
"id"=>"7"}
EDIT 2: I updated the nested form to include the id in a hidden field like this:
= f.fields_for :batches_order_products do |bop|
= bop.hidden_field :order_product_id
= bop.hidden_field :id, value: #batch.id
= bop.object.order_product.name
= bop.text_field :quantity, label: ''
BUT now Rails complains of this when updating:
ActiveRecord::StatementInvalid in BatchesController#update
Mysql2::Error: Unknown column 'batches_order_products.' in 'where clause': SELECT `batches_order_products`.* FROM `batches_order_products` WHERE `batches_order_products`.`batch_id` = 9 AND `batches_order_products`.`` IN ('9', '9', '9', '9', '9')
I don't know why Rails adds that last weird part in SQL query.
So I've finally figured it out.
The problem was that the join table needed an ID column to reference to. The table had an index of batch_id and order_product_id but for some reason it didn't work and ActiveRecord was looking for an ID. Adding it solved the problem.
Thanks to #max for giving some points to look at.
class AddIndexToBatchesOrderProductsJoinTable < ActiveRecord::Migration[5.2]
def change
# add_index :batches_order_products, [:batch_id, :order_product_id], unique: true
add_column :batches_order_products, :id, :primary_key
end
end

How to search in has_many association using Sunspot/Solr?

Here is what my initial searchable block looks like in my User model:
searchable do
text :name
integer :sport_ids, multiple: true do
sports.map(&:id)
end
integer :position_ids, multiple: true do
positions.map(&:id)
end
integer :city_id
integer :school_id
string :state
end
How do I search by has_many associations? I need it to return each Athlete who has a specified ID in their sports or sport_positions. So if someone selects "Basketball" from a dropdown, ID of 2 is passed to my search method and it needs to return Athletes who have sport_id of 2 in their collection of sport_ids. Here is how sports and sport_positions are declared in the User model:
has_and_belongs_to_many :positions, :class_name => "SportPosition", :join_table => "user_sport_positions", :uniq => true
has_many :sports, :through => :user_sports, order: "user_sports.created_at", class_name: "Sport"
has_many :user_sports
:::EDIT:::
This worked for a minute after I reindexed, then all of a sudden I started getting this error:
Sunspot::UnrecognizedFieldError (No field configured for Athlete with name 'sport_ids'):
app/models/search.rb:12:in `block in execute'
here is my Search model:
class Search < ActiveRecord::Base
attr_accessible :coach_id, :sport_id, :gpa_min, :gpa_max, :sport_position_id,
:classification, :city_id, :state, :school_id, :athlete_name
belongs_to :coach
def self.execute(params, page = nil)
Sunspot.search(Athlete) do |query|
query.with :public, true
query.with :removed_from_listing, false
query.fulltext params[:athlete_name] unless params[:athlete_name].blank?
query.with :sport_ids, params[:sport_id] unless params[:sport_id].blank?
query.with :position_ids, params[:sport_position_id] unless params[:sport_position_id].blank?
query.with(:state).equal_to(params[:state]) unless params[:state].blank?
query.with(:classification).equal_to(params[:classification]) unless params[:classification].blank?
query.with :city_id, params[:city_id] unless params[:city_id].blank?
query.with :school_id, params[:school_id] unless params[:school_id].blank?
query.with(:current_gpa).between(params[:gpa_min]..params[:gpa_max]) unless params[:gpa_min].eql?("0.0") && params[:gpa_max].eql?("5.0")
query.paginate page: page unless page.blank?
end
end
end
NOTE: To make this even more strange, I have a field called "recruit_year" that is an integer attribute. I was getting the same error on this field saying "No field configured" blah blah. That error usually only happens on text fields if you try to do a comparison like equal_to or treat it like a string.
Help?
This works fine, the problem was STI. I had an Athlete block that was overriding the User block.

Rails - Query One Model from within a Different Model with a has_many & belongs_to relationship

I'm not entirely clear on how to query a models data from within a different model in Rails. The models have a has_many & belongs_to relationship. The 2 models are "Gear" and "line_item". Gear has_many line_items and LineItem belongs_to Gear.
What I'm trying to do is query all the line_items in the database that belong to a Gear object that have a NULL or Blank cart_id (this is one of the fields) and then calculate it's availability. Since in this case the Gear is rented out at certain dates and times that are stored in a line_item (start_date, end_date, start_hour, end_hour) ..Having not done a lot of advanced quering in Ruby I Googled around and now I'm not sure if how I should use
Enumerable in the Gear Model with something like this:
line_items.inject(0) {|line_item| line_item.where('line_items.cart_id' => NULL }
Or if I could use a scope in the Gear Model like this:
scope :availablegear, lambda { includes(:line_items).where('line_items.cart_id' => nil) }
I know the syntax on both is probably not correct so I could use some direction on what to use and how to use it.
Thanks.
MY Project is using Rails 3.2.0, Ruby 1.9.4 & MySQL
EDIT with Suggested Answer
class Gear < ActiveRecord::Base
attr_accessible :title, :size, :price, :sub_category_id, :user_id, :image, :image_a, :image_b, :image_c, :image_d, :image_e, :image_f, :image_g, :image_h, :image_i, :remote_image_url, :color, :year, :latefee, :cancellation, :minrental, :policy, :about, :address, :city, :state, :zip, :country, :latitude, :longitude, :gmaps
...some code omitted for brevity
has_many :line_items
scope :availablegear, joins(:line_items).where(:line_items => {:cart_id => nil})
...some code omitted for brevity
end
Now when I boot up Rails console and I do a g = Gear.find(4) and then do g.availablegear I get the following error: NoMethodError: undefined method `availablegear' for #
I think the query you're looking for is
Gear.joins(:line_items).where(:line_items => {:cart_id => nil})
You can put it in a scope in the Gear class:
class Gear< ActiveRecord::Base
scope :available, joins(:line_items).where(:line_items => {:cart_id => nil})
end
You can find some more help in the Rails Query Guide (see 11.3 for joining with conditions).
If you're trying to get line items back:
class Gear < ActiveRecord::Base
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :gear
scope :available, where(:cart_id => nil)
end
Then if you have a gear, you can call
gear.line_items.available
which will return line items that belong to gear and have no cart_id.

Rails Format Collection_Select Based on two relationships

I'm trying to figure out how to construct a collection_select to include two relationships. Here are my models:
class Country < ActiveRecord::Base
has_many :companies, :dependent => :destroy
end
class Company < ActiveRecord::Base
belongs_to :country
has_many :departments, :dependent => :destroy
end
class Department < ActiveRecord::Base
belongs_to :company
end
When I create a new company I use the following to show a select box based on the relationship.
<%= collection_select(:company, :country_id, Countries.all, :id, :name, :prompt => 'Please select country') %>
But for the departments I'd like to have a select which let's the user select it's company from a select which also includes the companies country, formatted in the following way:
Company 1 - Country 1
Company 2 - Country 1
If i use the following I will only get a list of all the companies which I'd like to be able to see from the list which country they are from.
<%= collection_select(:device, :cabinet_id, Cabinet.all, :id, :name, :prompt => 'Please select cabinet') %>
Is there a way for rails to pull the information for the country into a select and append the entry with it's parent country?
I hope I've worded this question correctly! Sorry if it isn't clear.
Even if #jvnil solution works, I think you should avoid putting this logic in your view.
Instead, you could create an instance method in your Company model and use it in your select.
In your model :
class Company< ActiveRecord::Base
def name_for_select
name + " - " + country.name
end
end
And in your view :
<%= collection_select(:department, :company_id, Company.all, :id, :name_for_select %>
Use
UPDATE: move logic code to model
# company.rb
def company_with_country
"#{name} - #{country.name}" # this is better than using `string + string` since it creates only 1 string
end
# view
collection_select :department, :company_id, Company.includes(:country).all, :id, :company_with_country
UPDATE: faster version because it only uses needed columns
# controller
#companies = Company.joins(:country)
.select('companies.id, companies.name, countries.name AS country_name')
.map { |c| ["#{c.name} - #{c.country_name}", c.id] }`
# view
select :department, :company_id, #companies

ActiveRecord query on model methods

If I have the following code:
class User < ActiveRecord::Base
has_many :problems
attr_accessible :email, :password, :password_confirmation, :remember_me, :first_name, :last_name, :location, :address
attr_internal_accessor :user_address
def user_address
self.address
end
end
class Problem < ActiveRecord::Base
belongs_to :user
validates_presence_of :user_id, :title, :description, :tags
delegate :address, :to => :user, :prefix => true
end
When I try to do this in AR this is what the call looks like:
Problem Load (0.2ms) SELECT "problems".* FROM "problems" ORDER BY problems.user_address asc LIMIT 20 OFFSET 0
SQLite3::SQLException: no such column: problems.user_address: SELECT "problems".* FROM "problems" ORDER BY problems.user_address asc LIMIT 20 OFFSET 0
Completed 500 Internal Server Error in 173ms
It gives me an error that it is not a column which is true, however it generates data just like active record would.
How can I search the output if this function as if it was a native active record column?
The way i usually do this is by using the model you want to return.
So if its addresses you want, something like:
def user_address
Address.joins(:users).where(:user_id => user.id)
end
This way you get an AR relation object back and you can chain them.
The method user_address is supposed to be used in the code (mostly views), not to be passed to AR.
AR would require things to be more understood by the DB.
To DB sort (order) using the User#address column:
#Rails 3
p = Problem.includes(:user).order("users.address ASC").first
p.user_address
#Rails 2
p = Problem.find(:first, :include => :user, :order => "users.address ASC")
p.user_address
It might also be wise to check if a user exist for a problem when
def user_address
self.user.try(:address) #just in case a problem doesn't have an associated user
end

Resources