I'm trying to create a rake task that retrieves data from an API. However the data that I need has data from different models.
Here is my error:
[
{
"id": 3,
"name": "Precision Agriculture",
"topics_owners_ids": "#<User::ActiveRecord_Associations_CollectionProxy:0x000000095fa498>",
"keywords_list": null,
"keywords": [
],
"organizations_list": null,
"organizations": [
],
"social_groups_list": null,
"feeds_list": null,
"articles_list": null,
"people": null
}
]
Here is my rake task:
namespace :fieldfacts do
desc "Export Topics"
task :export_topics => :environment do
out = []
File.open("public/topics.json","w") do |f|
Topic.all.each do |topic|
api = TopicsService.new()
topic_api = api.get(topic.topic_api_id)
out << {
'id' => topic.id,
'name' => topic.name,
'topics_owners_ids' => topic.users.id,
'keywords_list' => topic_api.keywords_list,
'keywords' => topic_api.keywords,
'organizations_list' => topic_api.organizations_list,
'organizations' => topic_api.organizations,
'social_groups_list' => topic_api.social_groups_list,
'feeds_list' => topic_api.feeds_list,
'articles_list' => topic_api.articles_list,
'people' => topic_api.people
}
end
f.write(JSON.pretty_generate(out))
end
end
end
As you can see, the topics_owners_ids is not returning the correct data. I'm not sure how to fetch it.
topic.users will give you the collection object and you need to select ids. Try
topic.users.pluck(:id)
Related
I used to code below to deserialize the JSON API data send from client,
def action_record_params
ActiveModelSerializers::Deserialization.jsonapi_parse!(params)
end
When I pass the following data from the client, the deserializer cannot see relationships attributes.
Client send parameter
params = {"data": {"type": "action_record", "attributes": {"value": ""}}, "relationships": {"card": {"data": {"type": "card", "id": "#{card.id}"}}}}
Server deserialized data
{:value=>""}
How to deserialize parameters with relationships using ActiveModelSerializers?
Base on AMS documentation Deserialization section, which can be found below
https://github.com/rails-api/active_model_serializers/blob/master/docs/general/deserialization.md
The relationships can be extracted by via the option only: [:relatedModelName]. only is act as a whitelist in this case.
Sample Data
document = {
'data' => {
'id' => 1,
'type' => 'post',
'attributes' => {
'title' => 'Title 1',
'date' => '2015-12-20'
},
'relationships' => {
'author' => {
'data' => {
'type' => 'user',
'id' => '2'
}
},
'second_author' => {
'data' => nil
},
'comments' => {
'data' => [{
'type' => 'comment',
'id' => '3'
},{
'type' => 'comment',
'id' => '4'
}]
}
}
}
}
AMS deserialization with options
ActiveModelSerializers::Deserialization
.jsonapi_parse(document, only: [:title, :date, :author],
keys: { date: :published_at },
polymorphic: [:author])
Output hash
# {
# title: 'Title 1',
# published_at: '2015-12-20',
# author_id: '2',
# author_type: 'user'
# }
i need to do an array of hashes inside of a hash, something like this:
merit_hash => {
students => [
{
"id": id,
"name": name,
subjects => [
{
"id": id,
"grade": grade
},
{
"id": id,
"grade": grade
}
]
},
{
"id": id,
"name": name,
subjects => [
{
"id": id,
"grade": grade
},
{
"id": id,
"grade": grade
}
]
}
]
}
Right now, i just have the array of student hashes, but i dont exactly know how to put the subject array inside of it, im doing this:
merit = {}
merit["students"] = []
students.each do |students|
student_subjects = Array.new
merit["students"].push(
{
"id" => students.id,
"name" => students.name.to_s
selected_batch_subjects.each do |subjects|
grade = FinalGrades.where(batch_subject_id:subjects.id, period_id: period.id, student_id: student.id).first.value
student_subjects.push(
{
"id" => subjects.id,
"grade"=> grade
}
)
end
}
)
end
but throws this error
unexpected '}', expecting keyword_end
when i try to close the student hash... what can i do to make this work? or, whats the best way of implementing this?
Thanks!
Something like this should work:
merit = {}
merit["students"] = []
students.each do |student|
student_information = {"id" => student.id, "name" => student.name.to_s}
student_subjects = []
selected_batch_subjects.each do |subjects|
grade = FinalGrades.where(batch_subject_id:subjects.id, period_id: period.id, student_id: student.id).first.value
student_subjects.push({"id" => subjects.id, "grade" => grade})
end
student_information[:subjects] = student_subjects
merit["students"].push(student_information)
end
The important part is adding each student's subjects to the already existing hash.
Your iterations are not very clear to me but for current loop and array push you could do like this:
merit = {}
merit["students"] = []
students.each do |students|
student_subjects = []
merit["students"] << {
"id" => students.id,
"name" => students.name.to_s
}
selected_batch_subjects.each do |subjects|
grade = FinalGrades.where(batch_subject_id:subjects.id, period_id: period.id, student_id: student.id).first.value
student_subjects << {"id" => subjects.id,"grade"=> grade}
end
end
I need to add commas between my hashes and encapsulate them inside square brackets. Can anyone tell me how?
Here is my code:
namespace :fieldfacts do
desc "Export Topics"
task :export_topics => :environment do
out = []
File.open("public/topics.json","w") do |f|
Topic.all.each do |topic|
api = TopicsService.new()
topic_api = api.get(topic.topic_api_id)
out = {
'id' => topic.id,
'name' => topic.name,
'keywords_list' => topic_api.keywords_list,
'organizations_list' => topic_api.organizations_list,
'social_groups_list' => topic_api.social_groups_list,
'feeds_list' => topic_api.feeds_list,
'articles_list' => topic_api.articles_list,
'people' => topic_api.people
}
f.write(JSON.pretty_generate(out))
end
end
end
end
Here is the output:
{
"id": 3,
"name": "Precision Agriculture",
"keywords_list": null,
"organizations_list": null,
"social_groups_list": null,
"feeds_list": null,
"articles_list": null,
"people": null
}{
"id": 4,
"name": "Backcountry Skiing",
"keywords_list": null,
"organizations_list": null,
"social_groups_list": null,
"feeds_list": null,
"articles_list": null,
"people": null
}
Any help would be appreciated. Thanks!
Your issue here is that you are generating JSON multiple times and then appending it together, rather than generating it once.
Something like this should solve your problem (note the change in location of f.write):
namespace :fieldfacts do
desc "Export Topics"
task :export_topics => :environment do
out = []
File.open("public/topics.json","w") do |f|
Topic.all.each do |topic|
api = TopicsService.new()
topic_api = api.get(topic.topic_api_id)
out << {
'id' => topic.id,
'name' => topic.name,
'keywords_list' => topic_api.keywords_list,
'organizations_list' => topic_api.organizations_list,
'social_groups_list' => topic_api.social_groups_list,
'feeds_list' => topic_api.feeds_list,
'articles_list' => topic_api.articles_list,
'people' => topic_api.people
}
end
f.write(JSON.pretty_generate(out))
end
end
end
We've got a simple api endpoint that works like so:
def index
render json: Country.all
end
This is unfortunately giving us this output:
{
"countries" => [
[0] {
"countries" => {
"id" => 1,
"iso" => "US",
"iso3" => "USA",
"iso_name" => "UNITED STATES",
"name" => "United States of Foo",
"numcode" => 840
}
},
[1] {
"countries" => {
"id" => 2,
"iso" => "CA",
"iso3" => "CAN",
"iso_name" => "CANADA",
"name" => "Canada",
"numcode" => 124
}
}
]
}
Notice that the key for each individual object is the plural form of the key.
However, when we set the endpoint to work like so
def inded
render json: {countries: Country.all}
end
The output looks like so:
{
"countries" => [
[0] {
"country" => {
"id" => 1,
"iso" => "US",
"iso3" => "USA",
"iso_name" => "UNITED STATES",
"name" => "United States of Foo",
"numcode" => 840
}
},
[1] {
"country" => {
"id" => 2,
"iso" => "CA",
"iso3" => "CAN",
"iso_name" => "CANADA",
"name" => "Canada",
"numcode" => 124
}
}
]
}
I.e., correct.
However, setting the key like {countries: Country.all} is bad form, and I'd like to understand why rails is serializing each element with the collection key rather than the object key (that is, why it's plural and not singular for each country).
We have not overridden to_json or any other serialization methods. We are using the default rails model serializer (I tried making an explicit serializer, but there was no change in behavior). I cannot, for the life of me, figure out why it is pluralizing these keys.
Edit: There is even more weirdness. I was incorrect about the explicit serializer, when I set up a serializer (with just the attributes as it normally displays), I get this:
{
"countries" => [
[0] {
"id" => 1,
"iso" => "US",
"iso3" => "USA",
"iso_name" => "UNITED STATES",
"name" => "United States of Foo",
"numcode" => 840
},
[1] {
"countries" => {
"id" => 2,
"iso" => "CA",
"iso3" => "CAN",
"iso_name" => "CANADA",
"name" => "Canada",
"numcode" => 124
}
}
]
}
The first object has no key, and every other one is plural. I tested both in tests, and confirmed by making the actual API call.
I can't find anything that would override this other than an overridden <=> and to_s. These should not affect output in this way?
Mike, I think you copied and pasted something wrong, or you might just be confused.
Both outputs have the same results: Countries (Plural) as the main response, and country(Singular) for each individual inside countries. Or am I misreading?
I think what's happening here is that you're trying to specify the root key in a way that AM::S doesn't support.
Instead of render json: { countries: Country.all }, I'd recommend trying render json: Country.all, root: "countries" (specifying the root key is unnecessary if you're calling the serializer from within CountriesController, otherwise you need it to get the functionality you desire).
See https://github.com/rails-api/active_model_serializers/tree/0-8-stable#arrays for more information.
In my rails application, I have a button which when clicked, copies data from one database and insert it in another.
I am using octopus gem to link my application to 2 databases.
To copy a record from db_A to db_B, I am using the code below:
Octopus.using(:shard_B) do
#book_new_live = Book.create(
:BK_SUB_FK => #book.BK_SUB_FK,
:BK_TITLE => #book.BK_TITLE,
:BK_SOURCE => "",
:BK_PUB => "",
:BK_COVER => "",
:BK_LABEL_PRODUCT => #book.BK_LABEL_PRODUCT,
:BK_FINAL_LABEL => "",
:BK_VISUAL_METHOD => #book.PRB_VISUAL_METHOD,
:BK_DB => "",
:BK_COVERED_REGION => "",
:BK_VERSION_NO => #book.BK_VERSION_NO,
:BK_SEQ_FILE => "",
)do |primary|
primary.BK_ID = #book.BK_ID
end
end
Database 'db_b', to which data in copied, does not accept null values and the columns cannot be null and the default value is 'NONE'.
Also, I am not allowed to modify the structure of the database so that it can accept null values.
If I use the simplified code below, I get an error message which informs me that the columns 'BK_SOURCE', 'BK_PUB', 'BK_COVER'...cannot be null. By default rails is passing null to those columns.
So I have to pass empty strings to the columns which cannot be null.
Octopus.using(:shard_B) do
#book_new_live = Book.create(
:BK_SUB_FK => #book.BK_SUB_FK,
:BK_TITLE => #book.BK_TITLE,
:BK_LABEL_PRODUCT => #book.BK_LABEL_PRODUCT,
:BK_VISUAL_METHOD => #book.PRB_VISUAL_METHOD,
:BK_VERSION_NO => #book.BK_VERSION_NO,
)do |primary|
primary.BK_ID = #book.BK_ID
end
end
Is there a way of preventing rails from passing null values to the columns not mentioned in the above code?
If i understand you correctly, you can just use the or operator to send your values like this:
Octopus.using(:shard_B) do
#book_new_live = Book.create(
:BK_SUB_FK => #book.BK_SUB_FK,
:BK_TITLE => #book.BK_TITLE,
:BK_SOURCE => #book.BK_SOURCE || "NONE",
:BK_PUB => #book.BK_PUB || "NONE",
:BK_COVER => #book.BK_COVER || "NONE",
:BK_LABEL_PRODUCT => #book.BK_LABEL_PRODUCT,
:BK_FINAL_LABEL => "",
:BK_VISUAL_METHOD => #book.PRB_VISUAL_METHOD,
:BK_DB => "",
:BK_COVERED_REGION => "",
:BK_VERSION_NO => #book.BK_VERSION_NO,
:BK_SEQ_FILE => "",
)do |primary|
primary.BK_ID = #book.BK_ID
end
end
By saying that the value to be passed is #book.BK_SOURCE || "NONE", if the attribute is nil, then the string NONE is passed instead.
EDIT
hash = {
:BK_SUB_FK => #book.BK_SUB_FK,
:BK_TITLE => #book.BK_TITLE,
:BK_SOURCE => #book.BK_SOURCE,
:BK_PUB => #book.BK_PUB,
:BK_COVER => #book.BK_COVER,
:BK_LABEL_PRODUCT => #book.BK_LABEL_PRODUCT,
:BK_FINAL_LABEL => #book.BK_FINAL_LABEL,
:BK_VISUAL_METHOD => #book.PRB_VISUAL_METHOD,
:BK_DB => #book.BK_DB,
:BK_VERSION_NO => #book.BK_VERSION_NO
}
hash = hash.delete_if { |k, v| v.nil? }
Octopus.using(:shard_B) do
#book_new_live = Book.create(hash)do |primary|
primary.BK_ID = #book.BK_ID
end
end
Give it a try.