I'm in the process of building my first rails app and am running into some trouble. This is my context:
I have created an api/v1/orders controller that accepts a JSON message from a third party containing an order with multiple line items. In this controller, I create the order and the multiple line items in one go, by using accepts_nested_attributes_for :line_items, allow_destroy: true.
Now, after creating the order and line items I want to create shipments. My initial thought is to create a method in the Order model because all the shipments for a specific order should be created at the same time after the order is inserted. Now how do I access the order attributes in the model?
In my controllers this is my order#create
order = Order.new(
shopify_order_id: params[:id],
shopify_created_at: params[:created_at],
shopify_order_name: params[:name],
#etc etc
line_items_attributes: create_line_items(params[:line_items])
)
After the order.save, I would like to call order.assign_shipments. How do I access the attributes that I set in my orders controller though?
In my order model I want to do something like:
def self.assign_shipments
order.line_items.each do |line_item|
# check line item params
# create_shipments based on line item params
end
end
But, I don't know how I can access the attributes (line items) of the order? Are these even available in the model or are these only available in the instance of my orders controller (and thus should I create the shipments in my orders controller)?
Using self. in front of the method declaration makes it a Class method. You want an instance method, since you're calling assign_shipments on an instance of Order. Then in the method use self to refer to the instance itself.
def assign_shipments
self.line_items.each do |line_item|
# check line item params
# create_shipments based on line item params
end
end
Related
When creating app I had model Order with attribute delivery_option. After some time, I had to create "higher level" order to group orders and created MainOrder.
Now, Order delegates :delivery_option to MainOrder, but about 70% of MainOrders has delivery_option == nil because where created during migration, just to cover Orders from past. I couldn't fill up main_order.delivery_option = order.delivery_option because there can be many orders in one main_order, each with different :delivery_option.
Is it possible to somehow access order.delivery_option without hitting order.main_order.delivery_option?
code looks like this:
class MainOrder
has_many :orders
end
class Order
belongs_to :main_order
delegates :delivery_option, to: main_order, allow_nil: true
end
You can use the attributes method returning a hash with attribute values:
order = Order.first
# fetches delivery_option from the main_order
order.delivery_option
# returns value stored in orders table belonging to the order
order.attributes["delivery_option"]
# you can also use
order.attribute(:delivery_option)
I have the following hash holding a user's name and items_sold:
{"dave"=>9, "steve"=>20}
I created it in my Rawdatum Index action through:
#consolidated_rawdata = Rawdatum.all.group(:user).sum(:items_sold)
What I want to do now is to save both objects from that hash (dave & steve) with a single action (a single click on a button) to a table called reports with two columns: user and items_sold like so:
How can I achieve that?
Don't know if that counts as a single action, but you can iterate through the hash like this:
#consolidated_rawdata.each {|name, items_sold| Report.create(user:
name, items_sold: items_sold)}
if a action is just a single click then its really simple. We can just loop threw the hash and save the record
I would put this in a model like Reports
class Report < ApplicationRecord
#Report.create_consolidated_rawdata
def self.create_consolidated_rawdata
#consolidated_rawdata = Rawdatum.all.group(:user).sum(:items_sold)
#consolidated_rawdata.do |name, items_sold|
Report.create(user: name, items_sold: items_sold)
end
end
end
I hope that this works
In my project, I have a model called PaymentCondition and another called PaymentPortion.
PaymentCondition has_many payment_portions and PaymentPortion belongs_to payment_condition.
When I create a new PaymentCondition, I have this method that creates n payment_portions. Being n the value of a :amount attribute from PaymentCondition.
If I create a new PaymentCondition with amount: 2, for instance, I'd have 2 payment_portions.
So far, so good.
My problem:
I'm using a nested form to this view, so I can edit everything at once.
PaymentCondition has a attribute called catchments.
PaymentPortion has a attribute called catchment.
Before submiting this form, I'd like to check if the sum of PaymentPortion.catchment is equal to PaymentCondition.catchments. If not, I must raise an error.
As for now, I can't get the new values of PaymentCondition before saving it...
I'm doing this inside payment_conditions_controller:
before_update :check_catchments
def check_catchments
errors.add(:catchments, "Values must check") unless catchments_check? || new_record?
end
def catchments_check?
catchment == portion_catchments
end
def portion_catchments
payment_portions.sum(:catchments)
end
Using sum(), I get only the values that are on the database. What I need are the values that are being send...
Is there a way to do so?
Thanks in advance.
May be working directly in the controller is an option? You can use methods like 'build' or 'first_or_initialize' to get\create AR objects. And then just validate them in the controller and save the data if necessary.
I have an extra position attribute on my many-to-many link table. Its purpose is to define hand-ordered position of an Item in a Group. The link model is called ItemGroupMembership.
I am allowing the user to edit this by jQuery UI sortables in a Backbone application.
What is the correct way to update this attribute. Do I treat the link table like a regular model and have a item_group_memberships_controller?
How are Item's assigned to Groups in the first place?
This should be done in the item_group_memberships_controller.
So,
class ItemGroupMembershipsController < Applicationontroller
def create
#create the relationship, passing in :position
end
def update
#update the relationship by updating position
end
end
Hey,
Not a Rails noob but this has stumped me.
With has many through associations in Rails. When I mass assign wines to a winebar through a winelist association (or through) table with something like this.
class WineBarController
def update
#winebar = WineBar.find(params[:id])
#winebar.wines = Wine.find(params[:wine_bar][:wine_ids].split(",")) // Mass assign wines.
render (#winebar.update_attributes(params[:wine_bar]) ? :update_success : :update_failure)
end
end
This will delete every winelist row associated with that winebar. Then it finds all of the wines in wine_ids, which we presume is a comma separated string of wine ids. Then it inserts back into the winelist a new association. This would be expensive, but fine if the destroyed association rows didn't have metadata such as the individual wine bar's price per glass and bottle.
Is there a way to have it not blow everything away, just do an enumerable comparison of the arrays and insert delete whatever changes. I feel like that's something rails does and I'm just missing something obvious.
Thanks.
Your problem looks like it's with your first statement in the update method - you're creating a new wine bar record, instead of loading an existing record and updating it. That's why when you examine the record, there's nothing showing of the relationship. Rails is smart enough not to drop/create every record on the list, so don't worry about that.
If you're using the standard rails setup for your forms:
<% form_for #wine_bar do |f| %>
Then you can call your update like this:
class WineBarController
def update
#winebar = WineBar.find(params[:id])
render (#winebar.update_attributes(params[:wine_bar]) ? :update_success : :update_failure)
end
end
You don't need to explicitly update your record with params[:wine_bar][:wine_ids], because when you updated it with params[:wine_bar], the wine_ids were included as part of that. I hope this helps!
UPDATE: You mentioned that this doesn't work because of how the forms are setup, but you can fix it easily. In your form, you'll want to rename the input field from wine_bar[wine_ids] to wine_bar[wine_ids_string]. Then you just need to create the accessors in your model, like so:
class WineBar < ActiveRecord::Base
def wine_ids_string
wines.map(&:id).join(',')
end
def wine_ids_string= id_string
self.wine_ids = id_string.split(/,/)
end
end
The first method above is the "getter" - it takes the list of associated wine ids and converts them to a string that the form can use. The next method is the "setter", and it accepts a comma-delimited string of ids, and breaks it up into the array that wine_ids= accepts.
You might also be interested in my article Dynamic Form Elements in Rails, which outlines how rails form inputs aren't limited to the attributes in the database record. Any pair of accessor methods can be used.