Store multiple objects into one record - ruby-on-rails
I have a angular/rails app in which users can search for a movie, and then add that movie to their watchlist.
This is the create function in angular,
createMovie.create({
release_date: releaseNL.release_date,
imdb_rating: $scope.movieImdbRating.imdbRating,
title: $scope.movieListID.original_title,
image: $scope.movieListID.poster_path,
movie_id: $scope.movieListID.id,
backdrop: $scope.movieListID.backdrop_path,
crew: $scope.movieCrew = response.credits.crew,
cast: $scope.movieCast = response.credits.cast
}).then(init);
And I have the cast and crew data in a scope,
$scope.movieCrew = response.credits.crew
$scope.movieCast = response.credits.cast
These scopes return multple objects, since there are multiple actors etc. involved.
I have a movies_controller.rb that has a create function like this,
def create
#movie = Movie.find_or_create_by movie_params
current_user.movies << #movie
redirect_to :root
current_user.followers.each { |follower| #movie.crete_activity(key: 'movie.create', owner: current_user, recipient: follower) }
end
private
def movie_params
params.require(:movie).permit(
:title,
:image,
:imdb_rating,
:release_date,
:movie_id,
:backdrop
:crew,
:cast
)
end
My movie model looks like this,
create_table "movies", force: :cascade do |t|
t.string "title"
t.string "release_date"
t.string "image"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "movie_id"
t.string "backdrop"
t.string "crew"
t.string "cast"
end
When I run the action the movie record gets created
{"id":26,"title":"The Neon Demon","release_date":"2016-01-21","image":"/gVB7zbrsk9TtLoVI4qob97q9v07.jpg","user_id":null,"created_at":"2016-01-07T12:31:49.956Z","updated_at":"2016-01-07T12:31:49.956Z","movie_id":"301365","backdrop":"/lSojrYBHLwiQQq6xr9Mec9f8wLM.jpg","crew":null,"cast":null}
But the crew and cast values are empty.
I've checked my rails console to see what happens when the movie is created,
Started POST "/movies.json" for 127.0.0.1 at 2016-01-07 13:31:49 +0100
Processing by MoviesController#create as JSON
Parameters: {
"release_date"=>"2016-01-21",
"imdb_rating"=>"N/A",
"title"=>"The Neon Demon",
"image"=>"/gVB7zbrsk9TtLoVI4qob97q9v07.jpg",
"movie_id"=>301365,
"backdrop"=>"/lSojrYBHLwiQQq6xr9Mec9f8wLM.jpg",
"crew"=>[
{"credit_id"=>"54881a95c3a368415c000cde", "department"=>"Directing", id"=>21183, job"=>"Director", name"=>"Nicolas Winding Refn", profile_path"=>"/tFqRY5LgXH6W9GV51xCiJS1y8mB.jpg"},
{"credit_id"=>"54881a9c925141520a000c5b", department"=>"Writing", id"=>21183, job"=>"Screenplay", name"=>"Nicolas Winding Refn", profile_path"=>"/tFqRY5LgXH6W9GV51xCiJS1y8mB.jpg"},
{"credit_id"=>"54881ac8c3a368414d000fd8", department"=>"Writing", ""=>1397141, job"=>"Screenplay", name"=>"Mary Laws", profile_path"=>nil},
{"credit_id"=>"54881ad792514151ff000e94", department"=>"Camera", id"=>63422, job"=>"Director of Photography", name"=>"Philippe Le Sourd", "profile_path"=>nil},
{"credit_id"=>"54881ae5c3a3684148000cc8", department"=>"Sound", id"=>8377, job"=>"Original Music Composer", name"=>"Cliff Martinez",
"profile_path"=>nil},
{"credit_id"=>"56424bf5c3a3686a5800064e", department"=>"Editing", id"=>966286, job"=>"Editor", name"=>"Matthew Newman", profile_path"=>nil}
],
"cast"=>[
{"cast_id"=>5, "character"=>"Jesse", "credit_id"=>"54cd541dc3a3687f8f002102", "id"=>18050, "name"=>"Elle Fanning", "order"=>1, "profile_path"=>"/ouQIwOEVvyDzsU5j0Iw4oRIuGW4.jpg"},
{"cast_id"=>6, "character"=>"Sarah", "credit_id"=>"54cd5425925141475700204d", "id"=>1036288, "name"=>"Abbey Lee", "order"=>2, "profile_path"=>"/tCLVClLA2dB43KdyqJusmcuvvEC.jpg"},
{"cast_id"=>7, "character"=>"", "credit_id"=>"54d3a598c3a3686abf003f08", "id"=>20089, "name"=>"Jena Malone", "order"=>3, "profile_path"=>"/tx5KR6dAhYag3plX7Rdg8t25QrC.jpg"},
{"cast_id"=>8, "character"=>"", "credit_id"=>"54d3a59c9251413fc1003e39", "id"=>6384, "name"=>"Keanu Reeves", "order"=>4, "profile_path"=>"/glCFGnKkX3QWxeLRYUMU1XTESHf.jpg"},
{"cast_id"=>9, "character"=>"", "credit_id"=>"54d3a5a59251413fc1003e3b", "id"=>110014, "name"=>"Christina Hendricks", "order"=>5, "profile_path"=>"/4pAtKssXy84DzJOgl5155KJNS5z.jpg"},
{"cast_id"=>10, "character"=>"", "credit_id"=>"54d3a5ad9251413fd6003e88", "id"=>234982, "name"=>"Bella Heathcote", "order"=>6, "profile_path"=>"/iqk9bZnrkLhIQbHPyrGcMK0Bzni.jpg"}
],
"movie"=>{
"title"=>"The Neon Demon",
"release_date"=>"2016-01-21",
"image"=>"/gVB7zbrsk9TtLoVI4qob97q9v07.jpg",
"movie_id"=>301365,
"backdrop"=>"/lSojrYBHLwiQQq6xr9Mec9f8wLM.jpg",
"crew"=>[
{"credit_id"=>"54881a95c3a368415c000cde", "department"=>"Directing", "id"=>21183, "job"=>"Director", "name"=>"Nicolas Winding Refn", "profile_path"=>"/tFqRY5LgXH6W9GV51xCiJS1y8mB.jpg"},
{"credit_id"=>"54881a9c925141520a000c5b", "department"=>"Writing", "id"=>21183, "job"=>"Screenplay", "name"=>"Nicolas Winding Refn", "profile_path"=>"/tFqRY5LgXH6W9GV51xCiJS1y8mB.jpg"},
{"credit_id"=>"54881ac8c3a368414d000fd8", "department"=>"Writing", "id"=>1397141, "job"=>"Screenplay", "name"=>"Mary Laws", "profile_path"=>nil}, {"credit_id"=>"54881ad792514151ff000e94", "department"=>"Camera", "id"=>63422, "job"=>"Director of Photography", "name"=>"Philippe Le Sourd", "profile_path"=>nil},
{"credit_id"=>"54881ae5c3a3684148000cc8", "department"=>"Sound", "id"=>8377, "job"=>"Original Music Composer", "name"=>"Cliff Martinez", "profile_path"=>nil},
{"credit_id"=>"56424bf5c3a3686a5800064e", "department"=>"Editing", "id"=>966286, "job"=>"Editor", "name"=>"Matthew Newman", "profile_path"=>nil}
],
"cast"=>[
{"cast_id"=>5, "character"=>"Jesse", "credit_id"=>"54cd541dc3a3687f8f002102", "id"=>18050, "name"=>"Elle Fanning", "order"=>1, "profile_path"=>"/ouQIwOEVvyDzsU5j0Iw4oRIuGW4.jpg"},
{"cast_id"=>6, "character"=>"Sarah", "credit_id"=>"54cd5425925141475700204d", "id"=>1036288, "name"=>"Abbey Lee", "order"=>2, "profile_path"=>"/tCLVClLA2dB43KdyqJusmcuvvEC.jpg"},
{"cast_id"=>7, "character"=>"", "credit_id"=>"54d3a598c3a3686abf003f08", "id"=>20089, "name"=>"Jena Malone", "order"=>3, "profile_path"=>"/tx5KR6dAhYag3plX7Rdg8t25QrC.jpg"},
{"cast_id"=>8, "character"=>"", "credit_id"=>"54d3a59c9251413fc1003e39", "id"=>6384, "name"=>"Keanu Reeves", "order"=>4, "profile_path"=>"/glCFGnKkX3QWxeLRYUMU1XTESHf.jpg"},
{"cast_id"=>9, "character"=>"", "credit_id"=>"54d3a5a59251413fc1003e3b", "id"=>110014, "name"=>"Christina Hendricks", "order"=>5, "profile_path"=>"/4pAtKssXy84DzJOgl5155KJNS5z.jpg"},
{"cast_id"=>10, "character"=>"", "credit_id"=>"54d3a5ad9251413fd6003e88", "id"=>234982, "name"=>"Bella Heathcote", "order"=>6, "profile_path"=>"/iqk9bZnrkLhIQbHPyrGcMK0Bzni.jpg"}
]
}
}
As you can see the parameters have the correct values, release_date, title, image, crew, cast etc. But it does not store the objects inside them. Also I'm unsure why there's a movie value in the parameters value that repeats all the data exept for imdbRating.
So my question is, how do I store the objects from crew and cast into the my movie record?
Related
Receiving NoMethodError (undefined method `[]' for nil:NilClass): from webhook callback Rails 6
I built a webhook endpoint for a 3rd party API, but the issue I'm having is the webhook is failing to process some of the attributes that utilize arrays. I can't quite figure out why its not correctly updating/persisting the changes that the webhook is making in the system. How can I fix my webhook endpoint to allow changes to be made? Error Started POST "/gh_webhook" for ..... at 2020-04-06 10:58:02 -0400 Cannot render console from ....! Allowed networks: ..., ::1 Processing by GHController#gh_webhook as HTML Parameters: {"Id"=>"459b58d7-5a9e", "ReportStatus"=>{"Id"=>"7eac420d", "Status"=>"New", "StatusDetails"=>"New", "CheckStatuses"=>[]}, "good_hire"=>{"Id"=>"459b58d7-5a9e", "ReportStatus"=>{"Id"=>"7eac420d", "Status"=>"New", "StatusDetails"=>"New", "CheckStatuses"=>[]}}} Completed 500 Internal Server Error in 3ms (ActiveRecord: 0.0ms | Allocations: 1164) NoMethodError (undefined method `[]' for nil:NilClass): app/controllers/gh_controller.rb:7:in `gh_webhook' GH webhook controller class GHController < ApplicationController skip_before_action :verify_authenticity_token def gh_webhook resp = JSON.parse(request.body.read) report_id = resp["Id"] candidate_first_name = resp["ReportStatus"]["Candidate"]["FirstName"] candidate_last_name = resp["ReportStatus"]["Candidate"]["LastName"] candidate_middle_name = resp["ReportStatus"]["Candidate"]["MiddleName"] candidate_email = resp["ReportStatus"]["Candidate"]["Email"] report_status = resp["ReportStatus"]["Status"] report_status_details = resp["ReportStatus"]["Pending"] report_adverse_action_status = resp["ReportStatus"]["AdverseActionStatus"] report_viewer_url = resp["ReportStatus"]["ReportViewerUrl"] candidate_url = resp["ReportStatus"]["CandidateUrl"] required_report_actions = resp["ReportStatus"]["RequiredReportActions"] check_statuses = resp["ReportStatus"]["CheckStatuses"] sections_containing_alerts = resp["ReportStatus"]["SectionsContainingAlerts"] background_check_report = BackgroundCheckReport.find_by_report_id(report_id) || BackgroundCheckReport.create(report_id: report_id) background_check_report.update(candidate_first_name: candidate_first_name, candidate_last_name: candidate_last_name, candidate_middle_name: candidate_middle_name, candidate_email: candidate_email, report_status: report_status, report_viewer_url: report_viewer_url, candidate_url: candidate_url, report_status_details: report_status_details, sections_containing_alerts: sections_containing_alerts, check_statuses: check_statuses, required_report_actions: required_report_actions, adverse_action_status: report_adverse_action_status) head :ok end end schema create_table "background_check_reports", force: :cascade do |t| t.string "candidate_first_name" t.string "candidate_last_name" t.string "candidate_email" t.string "report_status" t.string "report_status_details" t.string "report_viewer_url" t.string "candidate_url" t.bigint "provider_form_id" t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false t.string "report_id" t.string "candidate_middle_name" t.json "sections_containing_alerts" t.json "required_report_actions" t.json "check_statuses" t.string "adverse_action_status" end
You do not have "Candidate" element in resp["ReportStatus"]. With your json: resp["ReportStatus"] #=> {"Id"=>"7eac420d", "Status"=>"New", "StatusDetails"=>"New", "CheckStatuses"=>[]} resp["Reportstatus"]["Candidate"] #=> nil resp["ReportStatus"]["Candidate"]["FirstName"] #=> rise NoMethodError exception.
There is no "Candidate" key in parameters hash so it is failing. Use .dig method to access value from nested hash or return nil From documentation: h = { foo: {bar: {baz: 1}}} h.dig(:foo, :bar, :baz) #=> 1 h.dig(:foo, :zot) #=> nil UPD. in line 7, 8, 9, 10 you have 'Candidate' key which could exist or could not exist in your hash. replace resp["ReportStatus"]["Candidate"]["FirstName"] with resp.dig('ReportStatus', 'Candidate', 'FirstName') and you will have result if it exists or nil if 'ReportStatus', 'Candidate' or 'FirstName' doesn't exist. Then check whether your variable exist and
Rails how to order column sum query?
I want to ranking presentation. Recommend model has user and count. rank / user_name / recommend_count 1 / A / 4 2 / B / 2 3 / C / 1 code #top_recommenders = Array.new recommends = Recommend.group(:user_id).order("sum_count DESC").limit(10).sum(:count) recommends.each do |recommend| recommend = Recommend.where(user_id: recommend[0]).first recommend.count = recommend[1] #top_recommenders << recommend end schema create_table "recommends", force: :cascade do |t| t.integer "user_id" t.integer "recommender_id" t.integer "count" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["recommender_id"], name: "index_recommends_on_recommender_id" end That worked, but is not perfect. How to query group by 'user' and order 'sum_count'?
#top_recommenders = Recommend.select('recommends.*,SUM(count) as count_total').group(:user_id).order("count_total DESC").limit(10) The sum of count will be stored in the query result as count_total. If you need to store it as each recommend objects count, then map it as below. #top_recommenders.map { |r| r.count = r.count_total }
How to make a request to the grouping of results by date?
I can not write a valid request to obtain such a result (group recording day): date (days) - sum (field1) - sum (field2) date (days) - sum (field1) - sum (field2) etc. My code: document has_many downloads download belongs_to document #downloads = Download .order(created_at: :desc) .includes(:document) .joins(:document) .where('documents.uploaded_by = ?', params[:user_id]) .page(params[:page]) I would be grateful for the help UPD: create_table "document_downloads", force: :cascade do |t| t.integer "document_id", null: false t.integer "payment_sum" t.datetime "created_at", null: false end i want to group like: payment_sum created_at 150 2015-06-27 120 2015-06-27
I need more information about your table schema but this can be helpful.. Download.select('date(days), sum(field1), sum(field2)') .joins(:document) .where('documents.uploaded_by = ?', params[:user_id]) .group('days') .page(params[:page])
How to compare if a record exist with json data type field?
I want to check if a record already exist on database, but I have one json data type field and I need to compare it too. When I try check using exists? I got the following error: SELECT 1 AS one FROM "arrangements" WHERE "arrangements"."deleted_at" IS NULL AND "arrangements"."account_id" = 1 AND "arrangements"."receiver_id" = 19 AND "config"."hardware" = '--- category: mobile serial: ''00000013'' vehicle: ' AND "arrangements"."recorded" = 't' LIMIT 1 PG::UndefinedTable: ERROR: missing FROM-clause entry for table "config" LINE 1: ...id" = 1 AND "arrangements"."receiver_id" = 19 AND "config"."... ^ Code that I using to check if a exists: #arrangement = Arrangement.new({account_id: receiver.account.id, receiver_id: receiver.id, config: params[:config], recorded: true}) if Arrangement.exists?(account_id: #arrangement.account_id, receiver_id: #arrangement.receiver_id, config: #arrangement.config, recorded: #arrangement.recorded) puts 'true' end I already tried: if Arrangement.exists?(#arrangement) puts 'true' end But always return false Table: create_table :arrangements do |t| t.references :account, index: true t.references :receiver, index: true t.json :config, null: false t.boolean :recorded, default: false t.datetime :deleted_at, index: true t.integer :created_by t.timestamps end
You cannot compare jsons. Try to compare some jsons values where("arrangements.config->>'category' = ?", params[:config][:category]) Look in postgresql docs for other JSON functions and operators
This will convert both field(in case it is just json) and the parameter(which will be a json string) to jsonb, and then perform a comparison of everything it contains. def existing_config?(config) Arrangement.where("config::jsonb = ?::jsonb", config.to_json).any? end
How to force a column to only allow number in this fashion: 1, 1.5, 2, 2.5 and so on?
I have a Star model model and included total_stars and average_stars columns to the Post model: create_table "posts", force: true do |t| t.string "title" t.text "content" t.datetime "created_at" t.datetime "updated_at" t.integer "user_id" t.integer "average_stars", default: 0, null: false t.integer "total_stars", default: 0, null: false end def calculate_total_stars if [Post].include?(starable.class) self.starable.update_column(:total_stars, starable.total_stars + self.number) end end def calculate_average_stars if [Post].include?(starable.class) self.starable.update_column(:average_stars, starable.total_stars / starable.stars.count) end end So now the problem is if the average_stars is 3.6 the end result is just 3. I'm not very sure what kind of calculating or approximation is suitable for a five star rating system. But I would like it to go in the following fashion: 1, 1.5, 2, 2.5... Any suggestion of how to modify the average_stars column to achieve that result?
Instead of declaring your average column as an integer declare it as a float (or decimal): t.float "average_stars", default: 0, null: false Then when you're doing your calculation do: def calculate_average_stars if [Post].include?(starable.class) self.starable.update_column(:average_stars, starable.total_stars.to_f / starable.stars.count) end end Which will give you a decimal value instead of a rounded/truncated integer. The .to_f is the important part there. If you want it to be rounded or only have a fixed number of decimal points either use a Decimal column in your migration (which takes a :limit) or do some mathy stuff: ((starable.total_stars.to_f / starable.stars.count) * 100).round / 100.0
def calculate_average_stars if starable.is_a?(Post) exact_average = starable.total_stars.to_f / starable.stars.count rounded_average = exact_average - (exact_average % 0.5) starable.update_column(:average_stars, rounded_average) end end