I have a Rails 5 app and want to assign some Date (not Datetime) columns of an Advert model.
The model schema is as follows, including 2 Date columns - 'start' and 'end'.
create_table "adverts", force: :cascade do |t|
t.integer "user_id"
t.integer "product_id"
t.date "start"
t.date "end"
t.boolean "expired", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
The Advert controller also permits update of these columns,
def advert_params
params.require(:advert).permit(:user_id, :product_id, :start, :end, :expired)
end
I then have a method in the controller that is intended to update an advert.
The route is
post 'update_advert', :on => :collection
The advert record is pulled / created using find_or_initialize.
def update_advert
#advert = Advert.find_or_initialize_by(user_id: advert_params[:user_id], product_id: advert_params[:product_id])
logger.info "checkpoint1"
logger.info advert_params.inspect
logger.info "checkpoint2"
...
end
This gives the following log output,
I, [2020-05-19T12:32:46.947732 #13110] INFO -- : [e741ba19] checkpoint1
I, [2020-05-19T12:32:46.948986 #13110] INFO -- : [e741ba19] <ActionController::Parameters {"user_id"=>1, "product_id"=>1000, "expired"=>false} permitted: true>
I, [2020-05-19T12:32:46.949151 #13110] INFO -- : [e741ba19] checkpoint2
Note the start and end columns are missing, which therefore prevents me from going on to update them.
If I 'show' the record via the standard Rails web interface, I see the fields as expected.
If I pull the record via the JSON api, I see the expected fields.
{"id":4,"user_id":1,"product_id":1000,"start":"2020-05-18","end":"2020-06-18","expired":false}
My question is - why are the start and end columns not returned as part of the find_or_initialize response?
Thanks for reading.
Related
I have a Rails app (rails v6.0.3, ruby 2.7.1) that is using the Noticed gem to send notifications. I have the following model configuration:
class Vendor < ApplicationRecord
has_noticed_notifications
end
The has_noticed_notifications is, as described in their README, a "Helper for associating and destroying Notification records where(params: {param_name.to_sym => self})"
So when I create a Notification like so...
VendorAddedNotification.with(
vendor: vendor,
data_source: "user",
).deliver(some_user) # => Notification inserted!
I expect to be able to find the Notifications that reference the vendor, using the Noticed method, like so:
vendor = Vendor.find ...
vendor.notifications_as_vendor # => Expected: [ Notification#123 ]
However, the input is always an empty array (Actual => [])
I looked at their source code and it looks like notifications_as_vendor is the following query:
Notification.where(params: { :vendor => self }) # where self = an instance of the Vendor model
However, that doesn't seem to work, and I'm not sure if it's supposed to or not. I tried running a simpler query to see if it worked ...
Notification.where(params: { :data_source => "user" })
But that did not work either. However, when I ran the same query with a different signature, it did:
Notification.where("params->>'data_source' = ?", "user")
So my question is-- is this Notified's mistake, or am I missing something in my configuration? I'm using PSQL for this, here is the relevant schema:
...
create_table "notifications", force: :cascade do |t|
t.string "recipient_type", null: false
t.bigint "recipient_id", null: false
t.string "type", null: false
t.jsonb "params"
t.datetime "read_at"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["read_at"], name: "index_notifications_on_read_at"
t.index ["recipient_type", "recipient_id"], name: "index_notifications_on_recipient_type_and_recipient_id"
end
...
And here are the related models:
class VendorAddedNotification < Noticed::Base
deliver_by :database
param :vendor
param :data_source
end
class Notification < ApplicationRecord
include Noticed::Model
belongs_to :recipient, polymorphic: true
end
Thank you in advance!
I've found why it's not working, it seems to be an issue with Notified.
In plain SQL I ran:
# PLAIN SQL
select "params" from "notifications" limit 1
Which returns the notification's params (returned notifcation's id=77)
# PLAIN SQL Result
"{""added_by"": {""_aj_globalid"": ""gid://stack-shine/WorkspaceMember/269""}, ""data_source"": ""user"", ""_aj_symbol_keys"": [""workspace_vendor"", ""data_source"", ""added_by""], ""workspace_vendor"": {""_aj_globalid"": ""gid://stack-shine/WorkspaceVendor/296""}}"
Now in Rails when I do
vendor = Notification.find(77).params[:vendor]
vendor.notifications_as_vendor.to_sql
The result is ...
"SELECT \"notifications\".* FROM \"notifications\" WHERE \"notifications\".\"params\" = '{\"vendor\":{\"_aj_globalid\":\"gid://stack-shine/Vendor/296\"},\"_aj_symbol_keys\":[\"vendor\"]}'"
... the extracted params from that query are:
'{\"vendor\":{\"_aj_globalid\":\"gid://stack-shine/Vendor/296\"},\"_aj_symbol_keys\":[\"vendor\"]}'
So ... In the database, the serialized params are A, but Rails is search for B:
# A: `params` In the database
"{""added_by"": {""_aj_globalid"": ""gid://stack-shine/WorkspaceMember/269""}, ""data_source"": ""user"", ""_aj_symbol_keys"": [""vendor"", ""data_source"", ""added_by""], ""vendor"": {""_aj_globalid"": ""gid://stack-shine/Vendor/296""}}"
# B: `params` Searched with by Rails
"{\"vendor\":{\"_aj_globalid\":\"gid://stack-shine/Vendor/296\"},\"_aj_symbol_keys\":[\"vendor\"]}"
Clearly this query could not work because the params in the database are not the params being search by Rails.
The notification, in the database, has extra parameters on top of "vendor" ("data_source" and "added_by") that are not being search up by the Vendor. Is this why it returns nothing?
For now, I'll simply the look up the notifications myself by storing the vendor_id in params and doing something like Notification.where("params >> vendor_id = ?", 123)
I have part of a rails application where a user will create a recipe that will be saved in their "cookbook". Other users will be able to take recipes from other users. So there will be an aspect in the application that shows who created the recipe.
Schema for a Recipe
create_table "recipes", force: :cascade do |t|
t.string "recipe_name"
t.string "description"
t.integer "calories"
t.integer "carbs"
t.integer "fats"
t.integer "protein"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Where I am having trouble is displaying the recipe's creator.
def show
#user = current_user
#recipe = Recipe.find_by(params[:id])
creator = User.find_by(params[#recipe.user_id])
#creator = creator.first_name
end
So for right now I have two user's John (Id: 1) and Alex (Id:2). When I have Alex make a recipe and I put a pry under #recipe I get a user_id of 2 when I call #recipe.user_id.
However, when I put the pry under creator and call creator I get the user_id of 1 and I get John. I believe something is wrong with how I am trying to find the user using the user_id in #recipe. I was wondering if anyone know what I am doing wrong or if I need to add more information. Thanks.
This:
User.find_by(params[#recipe.user_id])
Doesn't make sense for a couple of reasons:
find_by expects a hash-like structure. Something like: User.find_by(id: xxx)
params[#recipe.user_id] doesn't make sense because that's going to be something like: params[1] which is not what you want.
This:
#recipe = Recipe.find_by(params[:id])
Also suffers from the malformed find_by.
So, try something like:
def show
#user = current_user
#recipe = Recipe.find(params[:id])
creator = #recipe.user
#creator = creator.first_name
end
This, naturally, assumes you have your association between Receipt and User set up correctly (i.e., using belongs_to).
I'm using Rails to build a simple web app.
I have a form asking users to select a vice category (e.g.: smoking, shopping, drinking coffee, etc.) and how much they spend (value) per day, in euros (unit).
Schema:
create_table "vices", force: :cascade do |t|
t.string "category"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.float "value"
t.string "unit"
t.string "user"
t.string "hobbies"
end
After submitting the form I dynamically show a message:
<%= "Stop #{#vice.category} and you will save #{#vice.value * 365} #{#vice.unit} per year!" %>
Second line:
<%= "Here's what you can buy instead:" %>
After this, I want to show Amazon products that they can buy according to their hobbies.
For example: if you stop smoking you will save 2000 euros a year. Here are drones you can buy instead. And then I show drones from Amazon.
I have been trying to connect to Amazon Product Advertising API using amazon-ecs gem but no luck showing it so far.
I then have a show.json.builder file with this:
json.partial! "vices/vice", vice: #vice
Amazon::Ecs.configure do |options|
options[:AWS_access_key_id] = '[my access key]'
options[:AWS_secret_key] = '[my secret key]'
options[:associate_tag] = '[my associate tag]'
end
res = Amazon::Ecs.item_search('#{#vice.hobbies}', {:response_group => 'Medium', :sort => 'salesrank'})
Is this correct?
How can I show the Amazon results in show.hmtl.erb
This is my first question here on Stack OVerflow. Let me know what else I need to post to expand on the explanation.
Thanks!
So, I'm using Rails 4, and I have an enum column on my "Sales_Opportunity" object called pipeline_status - this enables me to move it through a sales pipeline (e.g. New Lead, Qualified Lead, Closed deal etc). This all works fine. I'm able to find the number of sales_opportunities that a company has by status through using the following:
<%= #company.sales_opportunities.where(pipeline_status: 3).count %>
This all works fine. What I want to do is to find all sales_opportunities that have the pipeline_status of "closed_won" (enum value of 4 in my app) and sum the value of each won deal (so I can represent the total value of the customer based on the deals that are won in the system). A Sales_Opportunity in my model has a sale_value field, so I tried:
<%= #company.sales_opportunities.where(pipeline_status: 4).each.sale_value.sum %>
which returns the following error:
undefined method `sale_value' for #<Enumerator:0x007f9b87a9d128>
This is probably a trivial error but I can't for the life of me figure out what's going on. Is there where statement returning the enumerator or the sales_opportunity objects with that enumerator? Any help would be gratefully appreciated.
If it helps here are the fields in my sales_opportunities table:
create_table "sales_opportunities", force: true do |t|
t.datetime "close_date"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "pipeline_status", default: 0
t.string "opportunity_name"
t.integer "company_id"
t.decimal "sale_value", precision: 15, scale: 2, default: 0.0
end
A Sales_opportunity belongs_to a Company Object and a User Object, if that makes any difference.
use aggregate function sum
<%= #company.sales_opportunities.where(pipeline_status: 4).sum(:sale_value) %>
Other possibility is to use
<%= #company.sales_opportunities.where(pipeline_status: 4).pluck(:sale_value).reduce(0, :+) %>
I have just changed a column (called time) from t.string to t.datetime and then dropped and re-created the database and run the migration.
I have a script set to run every minute that scrapes information from a remote website and then adds the information to new records based on the time column that I adjusted to be a datetime rather than string.
# Add each row to a new call record
page = agent.page.search("table tbody tr").each do |row|
next if (!row.at('td'))
time, source, destination, duration = row.search('td').map{ |td| td.text.strip }
call = Call.find_or_create_by_time(time)
call.update_attributes({:time => time, :source => source, :destination => destination, :duration => duration})
end
Since changing the time column to integer the script doesn't seem to be importing any information at all. I wondered if there is an extra step that I need to do to make this work again?
My schema looks like this:
create_table "calls", :force => true do |t|
t.string "source"
t.string "duration"
t.datetime "time"
t.string "destination"
t.string "recording"
t.string "cost"
t.datetime "created_at"
t.datetime "updated_at"
end
In this part
time, source, destination, duration = row.search('td').map{ |td| td.text.strip }
call = Call.find_or_create_by_time(time)
you get time variable as a string, and trying to find_by it. I think smth like
call = Call.find_or_create_by_time(Time.parse(time))
should do the trick