Rails save multiple records from hash - ruby-on-rails

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

Related

Use new attribute values to validate before_update

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.

How to create multiple models from one controller

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

need to generate an object on create that contains the id

so i need to create an image that belongs to my model (string with the url of the image) in the models create method.
the problem is, that this image is a QR-Code that should contain the url of the object that gets created.
but the URL (of course) is unknown in the create method because no id exists at that point for the given object.
any ideas how to solve this problem?
I don't see an obvious way of doing this, beyond using a non id column within the URL (e.g. make a call to generate a UDID/GUID, and use that in the url http://mysite.com/obj/#{udid}), or saving in two stages, using the after_create callback to set the image once the record has been saved:
class MyModel < ActiveRecord::Base
after_create :set_image
def set_image
if image_attribute == nil
image_attribute = generate_a_qr_code(self)
self.save
end
end
end
Use two-pass saving :-)
def create
model = Model.new params[:model]
if model.save
# at this point you have an id
model.qr = generate_qr model
model.save
# proceed as usual
end
end
This is for traditional databases with auto-increment column as primary key. In some databases, keys are generated using sequences that you can query to get a new value (before saving your object). In some databases (MongoDB) keys can be generated on the client completely.

Conditional model updating -- Rails 3.1

I have two tables:
stores
raw_stores_data
The raw_stores_data is received from a third party daily.
I'd update certain fields of the stores model if those fields have been modified for that record in raw_stores_data.
Currently I have a bunch of conditional statements that check each of those fields. Is there any better way to code this?
new_data = raw_stores_data.all.select do |item|
item.store_id.present?
end
new_data.each do |item|
if item.field1 != item.stores.field1
...
...
...
# update record with hash of fields to update created above
end
You could add an association and special mutators to the 'raw' model that know how manipulate the 'stores' object. This serves to keep the model code in the model. Thin controller, comprehensive models, etc.
class Store < ActiveRecord::Base
has_one :raw_stores_data
end
class RawStoresData < ActiveRecord::Base
belongs_to :store
def field1=(value)
store.field1 = value
store.save!
field1 = value
end
end
I'm hand waving at some of the details, and you might want to reverse the direction of the association or make it go both directions.
EDIT:
You would use this as such:
raw_data = RawStoreData.find(param[:id]) # or new or however you get this object
raw_data.field1 = param[:field1]
The act of assigning will use the 'field1=' method, and make the change to the associated store object. If you're worried about saving unnecessarily, you could conditionalize in that method to only save if the value changed.
I hope this is clearer.

has_many through blowing away the association's metadata on mass association

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.

Resources