I need assign to events. I have user edit with checkboxes, where i choose event and after click on save i'd like add insert to database with event_id and user_id. I don't know if is it good idea, but if somebody have better ideas please give me a piece of advice.
My view, On this moment i only know to which event i'm assign:
<% Event.all.each do |event| -%>
<tr>
<td><%= label_tag :event_ids, event.name -%></td>
<td><%= check_box_tag :event_ids, event.id, #user.event.include?(event), :name => 'user[event_ids][]' -%></td>
</tr>
<% end -%>
My schema:
create_table "users", force: true do |t|
t.string "name"
t.string "email"
end
create_table "events", force: true do |t|
t.string "name"
t.datetime "event_date"
t.string "organizator"
t.string "email"
end
create_table "bookings", force: true do |t|
t.integer "user_id"
t.integer "event_id"
end
My models:
class User < ActiveRecord::Base
has_many :bookings
has_many :events, :through => :bookings
class Booking < ActiveRecord::Base
belongs_to :user
belongs_to :event
class Event < ActiveRecord::Base
has_many :bookings
has_many :users, :through => :bookings
Where you have User > Booking > Event, I have Gallery > Entry > Photo.
In the controller for edit:
#photo_ids = []
#gallery.entries.each do |entry|
#photo_ids << entry.photo.id
end
The pertinent part of _form.html.haml is:
- #photos.in_groups_of(15, false) do |group|
%tr
- group.each do |photo|
%td
= label_tag("photo_ids_#{photo.id}", image_tag(photo.image.url(:thumb)))
= raw('<br/>')
= check_box_tag "photo_ids[]", photo.id, #photo_ids.include?(photo.id), :id => "photo_ids_${photo.id}"
And when saving in the controller:
if #gallery.save
photo_ids = params[:photo_ids]
photo_ids ||= []
#gallery.entries.each |entry|
entry_photo_id_s = entry.photo_id.to_s
# if already in gallery and checked, keep it, and remove from list to add
if photo_ids.include?(entry_photo_id_s)
photo_ids.delete(entry_photo_id_s)
else
entry.destroy
end
# if not already in gallery, add an entry
photo_ids.each do |photo_id|
entry = Entry.new
entry.gallery_id = #gallery.id
entry.photo_id = photo_id
entry.save
end
end
blah, blah, blah (render, redirect, whatever)
end
With a little translation of entities (and from HAML to ERB), that should work for your User > Booking > Event.
Related
Model
class Assembly < ApplicationRecord
belongs_to :book
has_and_belongs_to_many :parts
end
Join table
class CreateJoinTableAssemblyPart < ActiveRecord::Migration[7.0]
def change
create_join_table :assemblies, :parts, id: false do |t|
t.index [:assembly_id, :part_id]
t.index [:part_id, :assembly_id]
end
end
end
Schema
create_table "assemblies_parts", id: false, force: :cascade do |t|
t.integer "assembly_id", null: false
t.integer "part_id", null: false
t.integer "assemblies"
t.integer "parts"
end
Controller
def create
#assembly = Assembly.new(assembly_params)
def assembly_params
params.require(:assembly).permit(:book_id, :part_id)
_Form.html
<div class="form-inputs">
<%= f.association :book %>
<%= f.association :parts %>
</div>
This way saves only the book_id
I need to save the part_id but it doesn't save and it doesn't even give an error
You need to whitelist the correct param key and an array of permitted scalar values - not a single value:
def assembly_params
params.require(:assembly)
.permit(:book_id, part_ids: [])
end
Check the logs for the correct param key to use.
Also remove the assemblies and parts columns from the assemblies_parts table. You don't need them and it just seems like an open invitation for bugs.
I have setup has_many and has_many :through association between a Order,User,Product and Order_detail model as a join table.
Models:
class Order < ActiveRecord::Base
has_many :order_details
belongs_to :user
has_many :products, through: :order_details
end
class OrderDetail < ActiveRecord::Base
belongs_to :order
belongs_to :product
end
class Product < ActiveRecord::Base
has_many :order_details
has_many :orders, through: :order_details
end
class User < ActiveRecord::Base
has_many :orders
end
How to save automatically for join table order_details.
Now data save only to order table.
Need to save all products to order_tables for current order and user
class OrdersController < ApplicationController
before_action :authenticate_user!
def index
#order = Order.all
end
def new
# #order = Order.new
#order = current_user.orders.new
end
def create
#order = current_user.orders.new(order_params)
#order.date = Date.today.to_s
if #order.save
# i think this is bad wrong to implementation of functional)
# params[:product_id].each do |detail_product_id|
# #order.order_details.product_id = detail_product_id
# #order.order_details.user_id = current_user
# #order.order_details.save
flash[:success] = "Order was successfully submitted"
redirect_to new_order_path
else
render :new
end
end
private
def order_params
params.require(:order).permit(:date, :product_id => [])
end
end
My schema:
create_table "order_details", force: true do |t|
t.integer "order_id"
t.integer "product_id"
t.integer "quantity"
t.integer "price"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "orders", force: true do |t|
t.integer "user_id"
t.date "date"
end
add_index "orders", ["user_id"], name: "index_orders_on_user_id", using: :btree
create_table "orders_products", id: false, force: true do |t|
t.integer "order_id"
t.integer "product_id"
end
create_table "products", force: true do |t|
t.string "product_name"
t.integer "product_price"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "available_status"
t.string "product_type"
end
In your view, add fields for order_details like:
<%= f.fields_for :order_details do |od| %>
<%= od.label 'your attribute for OrderDetail' %>
<%= # od.text_field 'your attribute' %>
<% end %>
Then in your model, accept nested attributes for order_details
like:
accepts_nested_attributes_for :order_details
These are sample values, you can use this logic with your actual attributes.
In your controller, permit attributes for order_details like:
params.require(:order).permit(:id, :name, order_details: [
#attributes of order details
])
Assuming that product_ids is an array of the product ids that you wish to add to the order, you could try assigning them in the following way and Rails should automagically create those association records for order_details when you then call #order.save
#order.products << Product.find_by_id(product_ids)
Am i right add that rows for controller?
Order_controller:
def create
#order = current_user.orders.new(order_params)
#order.products = Product.find_by_id(order_params[:product_ids])
#order.date = Date.today.to_s
if #order.save
flash[:success] = "Order was successfully submitted"
redirect_to new_order_path
else
render :new
end
end
private
def order_params
params.require(:order).permit(:date, order_details: [:product_id])
end
end
order model:
accepts_nested_attributes_for :order_details
I'm trying to save from 3 different types of products.
But how to take one product id from each part ? Because now I can choose only one product from all
View:
= simple_form_for(#order, html: {:class => 'well form-horizontal', :method => :post, :action=> :create }) do |f|
.col-xs-12.col-sm-6.col-md-8
= render 'shared/error_messages', object: f.object
%br
= simple_fields_for :order_details do |od|
= od.collection_radio_buttons :product_ids, Product.get_first_course, :id, :product_name ,:item_wrapper_class => 'inline'
%hr
= od.collection_radio_buttons :product_ids, Product.get_main_course, :id, :product_name, :item_wrapper_class => 'inline'
%hr
= od.collection_radio_buttons :product_ids, Product.get_drink, :id, :product_name,:item_wrapper_class => 'inline'
%hr
= f.button :submit, class: "btn btn-primary"
I change my association type to HABTM and that's enough for my situation. So..
models:
class Order < ActiveRecord::Base
has_and_belongs_to_many :products
before_destroy { products.clear }
end
class Product < ActiveRecord::Base
has_and_belongs_to_many :orders
end
Order_controller:
def create
order = current_user.orders.new(date: Date.today.to_s)
#order_products = Product.where(id: order_params[:product_ids])
order.products << #order_products
if order.save
#blalblal - sucsess
else
#blabla - alert-notice
end
Hello I have ran into a bit of trouble when create this conditional for a cart I built in my website to display a total when a part is on sale. In my schema I have parts, line_items which has an id of parts and carts, and carts. Parts have a attribute of discount. If a part has a discount it will display the discount and the new price of the part. My line_items has a method called line_item_discount which will create a new sum of the parts if one part includes a discount. Although it displays the part, the discount, and the new price, the cart total is not updating it.
I created a method called total_price_with_discount here
class Cart < ActiveRecord::Base
has_many :order_items
belongs_to :user
has_many :line_items, dependent: :destroy
def add_part(part_id)
current_part = line_items.find_by(part_id: part_id)
if current_part
current_part.quantity += 1
else
current_part = line_items.build(part_id: part_id)
end
current_part
end
def total_price
line_items.to_a.sum { |item| item.total_price}
end
def total_price_with_discount
line_items.to_a.sum { |item| item.total_price.line_item_discount}
end
Now where I am getting stuck is inside the _cart partial I tried to create a conditional where if a part has a discount it will use the total_price_with_discount method but if a part does not have a discount it will use the total_price. I have tried quite a few ways to create the conditional but i keep getting messages like this
for some reason carts has no instance of line_items or parts it appears.
Here are my tables for carts, parts, and line_items
create_table "carts", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.integer "quantity"
t.decimal "subtotal"
end
create_table "parts", force: :cascade do |t|
t.string "name"
t.text "description"
t.integer "category_id"
t.integer "price"
t.boolean "active"
t.integer "discount"
t.string "image"
t.integer "quantity"
end
create_table "line_items", force: :cascade do |t|
t.integer "part_id"
t.integer "cart_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "quantity", default: 1
end
my parts model
class Part < ActiveRecord::Base
has_many :order_items
has_many :line_items
before_destroy :ensure_not_referenced_by_any_line_item
def ensure_not_referenced_by_any_line_item
if line_items.empty?
return true
else
errors.add(:base, 'Line Items present')
return false
end
end
def subtotal
parts.collect { |part| part.valid? ? (part.quantity * part.unit_price) : 0}.sum
end
def apply_discount
price - (discount.to_f/100 * price)
end
end
my line_items model
class LineItem < ActiveRecord::Base
belongs_to :part
belongs_to :cart
def total_price
part.price * quantity
end
def line_item_discount
part.price - (part.discount.to_f/100 * part.price) * quantity
end
end
and here is the partial view thats throwing the error
<h2>Your Cart</h2> <table>
<%= render(cart.line_items) %>
<tr class="total_line">
<td colspan="2">Total</td>
<%unless cart.line_items.part.discount?%>
<td class="total_cell"><%= number_to_currency(cart.total_price) %></td>
<%end%>
<%if cart.line_items.part.discount?%>
<td class="total_cell"><%= number_to_currency(cart.total_price_with_discount) %></td>
<%end%>
</tr>
</table>
<%= button_to 'Empty cart', cart, method: :delete, data: { confirm: 'Are you sure?' } %>
Thanks for any help and advice with this one
To the Cart model I would add:
has_many :parts, through: :line_items
Add a scope to the Part model:
scope :with_discounts, -> { where.not(discount: nil) }
Then change the view to:
<td class="total_cell">
<%if cart.parts.with_discount.any?%>
<%= number_to_currency(cart.total_price_with_discount) %>
<%else%>
<%= number_to_currency(cart.total_price) %>
<%end%>
</td>
Update: Instead of the above, I believe the code below is more efficient, but I'll present both options and let you pick.
Below we'll always use line_item_discount in the total_price method within the LineItem model:
def total_price
(part.price * quantity) - line_item_discount
end
def line_item_discount
return 0 if part.discount.blank?
(part.discount.to_f/100 * part.price) * quantity
end
Then you don't even need the if statement within the view, total_price will work either way:
<td class="total_cell"><%= number_to_currency(cart.total_price) %></td>
You can then remove the total_price_with_discount method from the Cart model.
We can also tweak the total_price method within the Cart model:
(Works with either code choice)
def total_price
line_items.sum(:total_price)
end
In your view you're calling cart.line_items.part but line_items is a collection of multiple LineItem objects, .part is not a valid method on that collection object.
As you can see in your error, the part method is missing for ActiveRecord::Associations::CollectionProxy.
You should create a scope on LineItem like:
scope :with_discounted_part, { joins(:part).where.not(part: { discount: nil }) }
And then in your view you can do:
<% if cart.line_items.with_discounted_part %>
Having an invoice model which belongs to user, and a user table that associates a user as a particular role or multiple roles:
class CreateInvoices < ActiveRecord::Migration
def self.up
create_table :invoices do |t|
t.string :po_number
t.datetime :invoice_date
t.datetime :date_received
t.datetime :date_approved
t.text :clerk_note
t.integer :clerk_id
t.integer :approver_id
t.text :approver_note
end
end
class Invoice < ActiveRecord::Base
belongs_to :user
end
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :username
t.string :email
t.boolean :account_clerk
t.boolean :approver
t.boolean :admin
end
end
class User < ActiveRecord::Base
has_many :invoices
end
In the invoice record, how do I assign an clerk_id and approver_id depending on the role set in the user model? Each invoice will have a clerk and approver, but both are users.
Likewise, how do I assign the clerk_note to the clerk_id and the approver_note to the approver_id? I'm assuming in this case, I can just refer to current_user, since the user logged in will be the one making the note, right?
I'm not sure what this is called, so in my Googling, I didn't know what to look for... thanks in advance for your help.
To answer your original question using your current models you can just create your view to display different fields based on the role of the user (this assumes the logged in user is in an instance variable named #user).
<% if (#user.approver) then %>
<%= f.text_area :approver_note %>
<% else %>
<%= f.text_area :clerk_note %>
<% end %>
Then in your controller you can set the value of clerk id or approver id again depending on the logged in user.
if (#user.approver) then
#invoice.approver_id = #user.id
else
#invoice.clerk_id = #user.id
end
I'm trying to figure out if there is a way to do this in Rails more efficiently.
There is kind of a long setup for the question, so please bear with me.
Let's say I have models Customer, Phone, Address
Here are sample migrations to give you an idea:
class CreatePhones < ActiveRecord::Migration
def self.up
create_table :phones do |t|
t.integer :country_prefix, :limit => 3
t.integer :area_prefix, :limit => 5
t.integer :number, :limit => 7
t.integer :category_id
t.references :phonable, :polymorphic => true
t.timestamps
end
end
end
class CreateAddress < ActiveRecord::Migration
def self.up
create_table :addresses do |t|
t.string :address_line_1
t.string :address_line_2
t.string :address_line_3
t.string :city
t.string :state
t.string :zip
t.string :country
t.string :attn
t.integer :category_id
t.references :addressable, :polymorphic => true
t.timestamps
end
end
end
class CreateCategories < ActiveRecord::Migration
def self.up
create_table :categories do |t|
t.string :name
t.string :code
t.integer :category_id # Every subcategory has a category: i.e. phone has work, fax,mobile
t.timestamps
end
end
end
class CreateCustomers < ActiveRecord::Migration
def self.up
create_table :customers do |t|
t.string :code , :limit => 20 , :null => false
t.string :name , :null => false
t.string :billing_name
t.integer :preferred_shipping_method_id
end
end
Here are models and relations:
class Customer < ActiveRecord::Base
belongs_to :preferred_shipping_method , :class_name => "Category", :foreign_key => :preferred_shipping_method_id
has_many :addresses, :as => :addressable, :include => :category, :dependent => :destroy
has_many :phones, :as => :phonable, :include => :category, :dependent => :destroy
end
class Category < ActiveRecord::Base
has_many :addresses
has_many :phones
has_many :customer_by_shipping_methods, :class_name => "Customer", :foreign_key => :preferred_shipping_method_id
has_many :subcategories, :class_name => "Category", :foreign_key => :category_id
belongs_to :category, :class_name => "Category"
end
class Address < ActiveRecord::Base
belongs_to :category
belongs_to :addressable, :polymorphic => true
end
class Phone < ActiveRecord::Base
belongs_to :category
belongs_to :phonable, :polymorphic => true
end
Here is a question.
Let's say I have a customer record with a bunch of phone (mobile, work) and addresses (billing, shipping).
old = Customer.where(:code => "ABC").first
Then I'm creating or importing (from a legacy DB) another customer object
new = Customer.new
new.code = "ABC"
new.phones.build(:number => "12345567")
etc.
Then I want to compare old customer info to the new customer info and based on that update old customer info.
Like so :
if old.eql?(new) # this should compare not only, name & code and such but also polymorphic associations
old.update_with(new) # this should update old info attributes with new if there is new info, or if update / add to one of the associations
old.save #
else
new.save
end
So the question is is there any CONVENTIONAL way in Rails 3 to do what I describe in comments?
Right now I'm overriding hash & eql? methods which is fine for comparison. But to update each attribute and each associated object and its attributes, is getting kind of involved. I was wondering if there is an easier way to do this then my way:
class Customer < ActiveRecord::Base
def hash
%{#{ name }#{ code }}.hash # There is a lot more here of course
end
def eql?(other)
hash == other.hash
end
def update_with(other)
name = other.name
code = other.code
etc ....
end
end
Ok it doesn't look like there is a standard solution so here is something I came up if anybody else is looking for it.
You can rename methods anyway you like, just put this in you lib folder in some .rb file. ( don't forget to mention it in environment.rb like so require 'custom_comparisons'
/lib/custom_comparisons.rb
module ActiveRecord
class Base
def comparison_hash
h = ""
self.attributes.each_pair do |key, value|
unless ["id", "updated_at", "created_at"].include?(key)
h << "#{value}"
end
end
h.hash
end
def eql_to?(other)
comparison_hash == other.comparison_hash
end
def differences_from?(other)
h = {}
self.attributes.each_pair do |key, value|
unless self.method_missing(key) == other.method_missing(key)
h[key.to_sym] = [self.method_missing(key), other.method_missing(key)]
end
end
h
end
end
end
This can be cleaned up a bit and I need to add association drill down but the solution is there.
This does the comparison and the shows the differences between objects. Now I can update attributes that need to be updated. Will add update method and drill down tomorrow.