I'm not sure what I'm missing, but the following code below isn't saving to my database. I'm using rails 5.1.4 and ruby 2.4.1. I have no controller or views and using Mysql if that's of any help.
Model
class Agent < ApplicationRecord
json = JSON.parse('{"Agents":[{"firstName":"John","lastName":"Smith","id":"57fa5f47-8851-11e7-b391-02cbcf8dd991"},{"firstName":"Alice","lastName":"Thompson","id":"77eccb07-101d-11e7-83be-02e5025d7d75"}]}')
json['Agents'].each do |data|
Agent.create(
id: data['id'],
first_name: data['firstName'],
last_name: data['lastName']
)
end
end
Schema
create_table "agents", id: :string, limit: 36, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.string "first_name"
t.string "last_name"
end
As Gokul M pointed out, firstName and lastName need to be snake-cased to match your schema. However, your code still will not work, because :id is a protected attribute in ActiveRecord models. Meaning that it can’t be mass-assigned. You can however assign it normally. i.e model.id=foo
So you could try something like
json['Agents'].each do |data|
agent = Agent.new
agent.id = data['id']
agent.first_name = data['firstName']
agent.last_name = data['lastName']
agent.save
end
Though, I haven’t tested this code.
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 a column that is supposed to be a string. In schema.rb it looks something like this:
create_table "users", force: :cascade do |t|
t.string "login_token", default: "xxxxx", null: false
end
But if I try to update the column, the DB accepts integers and automatically converts them to strings for some reason.
user = User.first.update(login_token: 1)
#=> true
user.login_token
#=> "1"
Why is this, and is it possible to add any restrictions to the DB or validations in Rails to prevent this kind of typecasting?
Works the other way around too. If you have an integer column and pass a string, rails tries to convert it. Very useful when you, say, create a record from an html form (most everything comes as a string from the browser).
user_params # => { "age" => "20" }
u = User.create(user_params)
u.age # => 20
It's a feature/convention. I wouldn't fight it, if I were you.
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 have a table called 'products' (model is Product) and I can't access the :uuid attribute when running in migration context. The migration itself does not change the structure but accesses and creates new objects to populate the DB.
This is a snippet of the schema.rb prior to the migration:
create_table "products", force: :cascade do |t|
t.string "title"
t.string "description"
t.string "price"
t.uuid "uuid"
end
The Product object is defined as follows:
class Product < ActiveRecord::Base
end
Now when running in rails console / code this works fine:
p = Product.create!(:uuid => xxx)
puts p.uuid # => xxx
puts p.inspect # => Product(title: string, description: string, price: string, uuid: uuid)
However while running in the migration context - the same code raises an exception:
p = Product.create!(:uuid => xxx)
puts p.uuid # => undefined method `uuid' for <Product:0x007fea6d7aa058>/opt/rubies/2.2.0/lib/ruby/gems/2.2.0/gems/activemodel-4.2.3/lib/active_model/attribute_methods.rb:433
puts p.inspect # => Product(title: string, description: string, price: string)
The uuid attribute is missing! What's wrong?
put
Product.reset_column_information
before your Product.create line.
The schema of the model is generally refreshed after the migration. Therefore, even if the uuid field is created, the model doesn't have knowledge of it yet.
You can force a refresh by using
Product.reset_column_information
However, the issue in your code reveals you are probably using the migration feature to create records inside the migration itself. This is generally not recommended as the migrations are designed to change the schema of your database, not the data.
You should use create a specific rake task to modify the data and run the task after the migration is completed, for example from the console.
I have imported a .csv file of 10,000 locations and I need to loop through the database and geocode_by a few fields rather than the usual "geocode_by :address"
I am using the geocoder gem.
My database scheme looks like this
create_table "locations", :force => true do |t|
t.string "Address"
t.string "City"
t.string "State"
t.string "Zip"
t.float "latitude"
t.float "longitude"
t.datetime "created_at"
t.datetime "updated_at"
end
Can I do this in a controller action rather than on validation?
Should I do something like this:
def index
#locations = Location.all
#locations.each do |l|
new_address = "#{l.Address} #{l.City} #{l.State}"
geocode_by = :new_address
end
end
But yea, any help is greatly appreciated, thank you!
I was able to get things working with this controller code:
def index
if params[:search].present?
#locations = Location.near(params[:search], 20, :order => :distance)
else
#locations = Location.all
#locations.each do |l|
if l.latitude.nil?
new_location = "#{l.HP_Address_1} #{l.HP_City} #{l.HP_State}"
s = Geocoder.search(new_location)
l.latitude = s[0].latitude
l.longitude = s[0].longitude
l.save
end
end
end
end
It loops through every entry in the database, and if the latitude and longitude values have not been encoded it calls the correct geocoder function, and stores the returned lat / long values in the database.
I have 9k entries in my database, so I can only encode 2k per day due to the limit on the google maps api. But for folks used to encoding entries during creating using geocoded_by in their models, this will work in controllers.
This is the kind of thing I'd do with workers (delayed job or resque), since it might take some time to complete. But I'm not sure I understood the question, so this might not be the kind of answer you're expecting.
There is a rake task for this if you want to do every record in a class.
rake geocode:all CLASS=YourModel sleep=0.25