Here are my models:
class OrderItem < ApplicationRecord
belongs_to :order
belongs_to :product
end
class Order < ApplicationRecord
has_many :order_items
end
Here is my controller:
def index
orders = Order.where(nil)
render json: {'orders': orders}, include:
['order_items'], status: :ok
end
I want to also include the product in the order_items. How can I achieve this to get the following JSON:
{
"id": 2,
"order_items": [
{
"id": 1,
"product": {
"name": "abc"
},
}
]
},
You can reach this with changing
include: ['order_items']
to
include: ['order_items', 'order_items.product'].
More details you can get here.
I have been able to solve this by changing include: ['order_items'] to include: {'order_items': {include: 'product'}}
Related
I have 2 models like:
class Father < ActiveRecord::Base
has_many :children
accepts_nested_attributes_for :children
end
class Child < ActiveRecord::Base
belongs_to :father
validate :validate_money
def validate_money
children = Child.where(id: self.father.id)
sum_of_children_pocket_money = my_func # function for getting sum of all pocket money of all children
if sum_of_children_pocket_money > self.father.money
errors.add(:pocket_money, "My error message!!!")
end
end
end
and initially I have:
and when pass passengers_attributes to update customer (id=1) like
{
id: 1,
name: "Father 1",
money: 1000,
children: [
{
name: "Child 1",
id: 1,
pocket_money: 500
}
]
}
and then I am sending for update:
{
id: 1,
name: "Father 1",
money: 2000,
children: [
{
name: "Child 1",
id: 1,
pocket_money: 1500
}
]
}
So I am getting an error in my validation because it starts sum_of_children_pocket_money > self.father.money comparison and sum_of_children_pocket_money is equal to 1500 but self.father.money still 1000. How can I fix this?
Moving the validation logic from Child to Father should solve the problem.
class Father < ActiveRecord::Base
has_many :children
accepts_nested_attributes_for :children
validate :validate_money
def validate_money
children = Child.where(id: id)
sum_of_children_pocket_money = my_func # function for getting sum of all pocket money of all children
if sum_of_children_pocket_money > money
errors.add(:pocket_money, "My error message!!!")
end
end
end
class Child < ActiveRecord::Base
belongs_to :father
end
I have a problem with Mismatch types in my API everytime I want to create a new item.
It happens in 2 places:
when I try to POST a new item
POST http://localhost:8060/datasets/
{
"data": {
"type": "datasets",
"attributes": {
"doi": "10.5259/2008120816KAKA",
"version": 0,
"is-active": "1",
"datacentre": 201
}
}
Response
"errors": [
{
"title": "Internal Server Error",
"detail": "Internal Server Error",
"code": "500",
"status": "500",
"meta": {
"exception": "Datacentre(#23863440) expected, got 201 which is an instance of Fixnum(#4211740)",
"backtrace": [
"/usr/local/rvm/gems/ruby-2.3.3/gems/activerecord-5.1.1/lib/active_record/associations/association.rb:239:in `raise_on_type_mismatch!'",
"/usr/local/rvm/gems/ruby-2.3.3/gems/activerecord-5.1.1/lib/active_record/associations/belongs_to_association.rb:11:in `replace'",
"/usr/local/rvm/gems/ruby-2.3.3/gems/activerecord-5.1.1/lib/active_record/associations/singular_association.rb:15:in `writer'",
"/usr/local/rvm/gems/ruby-2.3.3/gems/activerecord-5.1.1/lib/active_record/associations/builder/association.rb:119:in `datacentre='"
I can see it happening when accessing the relationships of an item. For example this is a a GET response:
GET http://localhost:8060/datasets/
{
"data": [
{
"id": "5",
"type": "datasets",
"links": {
"self": "http://localhost:8060/datasets/5"
},
"attributes": {
"created": "2011-03-02T16:41:20.000Z",
"doi": "10.5259/20070410230000/HTTP://www.STAFFSPASTTRACK.ORG.UK/EXHIBIT/CRIMEANDPUNISHMENT/IMAGEPAGE/ZERO.HTM",
"version": 0,
"is-active": "",
"updated": "2011-03-02T16:41:20.000Z",
"datacentre": "#<Datacentre:0x000000033389b8>",
"deposited": "2012-07-31T09:12:37.000Z"
},
"relationships": {
"datacentre": {
"links": {
"self": "http://localhost:8060/datasets/5/relationships/datacentre",
"related": "http://localhost:8060/datasets/5/datacentre"
}
}
}
}
You can see how the datacentre (foreign_key) attribute is presented as an object type of the Model Datacentre. If I try to access the relantship to that model http://localhost:8060/datasets/5/datacentre I get the following:
{
* errors: [
* {
* title: "Internal Server Error",
* detail: "Internal Server Error",
* code: "500",
* status: "500",
* meta: {
* exception: "Unknown source type #<Datacentre id: 10015, comments: "", contact_email: "andrew.jackson#bl.uk", contact_name: "Andy Jackson", created: "2010-12-14 22:05:32", doi_quota_allowed: 50000, doi_quota_used: 17, domains: "webarchive.org.uk", is_active: "\x01", name: "Web Archive Programme at BL", password: "98583c1bf114bfe80105f906d800e05325307f06b93ccee6b6...", role_name: "ROLE_DATACENTRE", symbol: "BL.WAP", updated: "2012-02-17 11:49:01", version: 2, allocator: 106, experiments: nil>",
* backtrace: [
* "/usr/local/rvm/gems/ruby-2.3.3/gems/jsonapi-resources-0.9.0/lib/jsonapi/resource_serializer.rb:267:in `top_level_source_key'",
* "/usr/local/rvm/gems/ruby-2.3.3/gems/jsonapi-resources-0.9.0/lib/jsonapi/resource_serializer.rb:47:in `block in serialize_to_hash'",
* "/usr/local/rvm/gems/ruby-2.3.3/gems/jsonapi-resources-0.9.0/lib/jsonapi/resource_serializer.rb:47:in `map'",
* "/usr/local/rvm/gems/ruby-2.3.3/gems/jsonapi-resources-0.9.0/lib/jsonapi/resource_serializer.rb:47:in `serialize_to_hash'",
* "/usr/local/rvm/gems/ruby-2.3.3/gems/jsonapi-resources-0.9.0/lib/jsonapi/response_document.rb:109:in `results_to_hash'",
* "/usr/local/rvm/gems/ruby-2.3.3/gems/jsonapi-resources-0.9.0/lib/jsonapi/response_document.rb:12:in `contents’”,
I think the problem is that the API is expecting the wrong type of object. It’s expecting Datacentre when it should be expecting DatacentreResource.
My setup is as follows:
I have a Legacy database that doesn’t follow the ActiveRecord conventions for naming tables and foreign_keys.
Tables are singular and foreign_keys do not have the _id suffix.
The tables/models in which I have the problem have one_to_many relationship.
The relationship being a datacentre has_many datasets.
I am using jsonapi-resources and rails 5 api-only.
Datacentre Model
class Datacentre < ApplicationRecord
self.table_name = "datacentre"
alias_attribute :allocator_id, :allocator
has_and_belongs_to_many :prefixes, class_name: 'Prefix', join_table: "datacentre_prefixes", foreign_key: :prefixes, association_foreign_key: :datacentre
belongs_to :allocator, class_name: 'Allocator', foreign_key: :allocator
has_many :datasets
end
Dataset Model
class Dataset < ApplicationRecord
self.table_name = "dataset"
alias_attribute :datacentre_id, :datacentre
belongs_to :datacentre, class_name: 'Datacentre', foreign_key: :datacentre
end
Datacentre Resource
class DatacentreResource < JSONAPI::Resource
model_name 'Datacentre'
model_hint model: Datacentre
attributes :comments, :contact_email, :contact_name, :created, :doi_quota_allowed, :doi_quota_used, :domains, :is_active, :name, :password, :role_name, :symbol,
:updated, :version, :experiments, :allocator
has_many :datasets
has_many :prefixes
has_one :allocator, class_name: 'Allocator', foreign_key: :allocator
end
Dataset Resource
class DatasetResource < JSONAPI::Resource
model_name 'Dataset'
model_hint model: Dataset
attributes :created, :doi, :version, :is_active, :updated, :datacentre
attribute :deposited
has_one :datacentre, class_name: "Datacentre", foreign_key: :datacentre
end
So far I have got around the first problem (i.e. accessing the relationship) by modifying the methods for datacentre and its alias datacentre_id in the Dataset Resource
def datacentre(context=nil)
DatacentreResource.find_by_key(#model.datacentre.id)
end
def datacentre_id()
#model.datacentre.id
end
But this doesn’t solve the POST problem.
I think this is the thing that cause the error
in
class Dataset < ApplicationRecord
self.table_name = "dataset"
alias_attribute :datacentre_id, :datacentre
belongs_to :datacentre, class_name: 'Datacentre', foreign_key: :datacentre
end
datacentre_id and datacentre are aliased so it gives back and object datacentre back in response instead of datacentre_id
probably it will wokr if you remove this line
I am currently calling an API to get a publisher's Books. I am then grouping each book into different their respective story Arcs. Everything works great and I am able to retrieve the JSON in my app.
But, I'd like to change my current JSON response and remove the JSON root arcs:
Does anyone know how I can remove the JSON root and specify which book attributes I want?
Also, would a :has many => through association help simplify this and group things how i want?
This is my code.
Models
class Publisher < ActiveRecord::Base
has_many :books
end
class Arc < ActiveRecord::Base
has_many :books
end
class Book < ActiveRecord::Base
belongs_to :arc
belongs_to :book
end
Current Json
{
"arcs": [
{
"books": [
{
"arc_id": 1,
"created_at":"2014-12-27T20:54:46.518Z",
"id": 311,
"publisher_id": 7,
"updated_at":"2015-06-04T20:55:28.190Z"
}
],
"id": 1,
"name": "One-Shot"
}
]
}
How can I change it to this?
[
{
"books": [
{
"arc_id": 1,
"id": 311,
"publisher_id": 7
}
],
"id": 1,
"name": "One-Shot"
}
]
Controller
def index
#publisher = Publisher.find(params[:publisher_id])
#books = #publisher.books.order("positioning")
#results = {arcs: []}
#books.group_by(&:arc).each do |arc, books|
#results[:arcs] << {
id: arc.id,
name: arc.name,
books: books
}
end
respond_to do |format|
format.html
format.json { render :json => #results.to_json }
end
end
I have also tried using rabl in the link below, but it's not working..
https://stackoverflow.com/questions/30660136/include-group-by-parent-model-json-rabl
you could just render back without the arcs e.g.
format.json { render :json => #results[:arcs].to_json }
That being said you could also just change the controller method as well to this but you will have to change how the html response handles #results:
def index
#publisher = Publisher.find(params[:publisher_id])
#books = #publisher.books.order("positioning")
#results = #books.group_by(&:arc).map do |arc, books|
{
id: arc.id,
name: arc.name,
books: books
}
end
respond_to do |format|
format.html
format.json { render :json => #results.to_json }
end
end
This will also give you the desired result because map in this case will just return an Array of the Hashes you have designed.
I just got an issue after I've done a migration for a table, I've added owner field to the project table, but it is not appear when I request it (project).
Expected response after migration:
{
projects: [
{ id: 1, name: "First proj", owner: 1 },
{ id: 2, name: "Second proj", owner: 1 }
]
}
I got:
{
projects: [
{ id: 1, name: "First proj" },
{ id: 2, name: "Second proj" }
]
}
Here is my migration file
class AddOwnerRefToProjects < ActiveRecord::Migration
def change
add_reference :projects, :owner, index: true
end
end
My projects model
class Project < ActiveRecord::Base
belongs_to :owner, :class_name => :User
end
You need to add the new attribute in your json serializer.
Lets say I have a model.
Passengers belongs to Flights.
Flights belongs to Trips
class Trip < ActiveRecord::Base
has_many :flights, :dependent => :destroy, :order => "order_num ASC"
end
class Flight < ActiveRecord::Base
belongs_to :trip, touch: true
has_many :passengers, :dependent => :destroy
end
class Passenger < ActiveRecord::Base
belongs_to :flight, touch: true
end
And I'm getting this sent back to the rails app. (when the user calls save).
*The top level is the trip
{
name: 'Hello Trip',
date: '2013-08-12',
flights: [
{
id: 1
depart_airport: 'RDU',
arrive_airport: 'RDU',
passengers: [
{
user_id: 1,
request: true
}
]
},
{
depart_airport: 'RDU',
arrive_airport: 'RDU',
passengers: [
{
user_id: 1,
request: true
},
{
user_id: 2
request:true
}
]
}
]
}
Right now I'm getting the saved json in and manually looping through the flights to see if there is an id. If there is i'm updating it. If not I'm creating a new one. Then adding the passengers.
I'm wondering if there is an automatic format that Rails takes that can do all the saving for me. I know when you submit a nested form it creates a similar pattern, and adds a _destroy property and the id is a timestamp if it's just created. Would the JSON saving be similar to that?
Thanks for any help!
Yes, you should be able to use accepts_nested_attributes_for here.
You'll need to enable accepts_nested_attributes_for on your models, e.g.,
class Trip < ActiveRecord::Base
has_many :flights, :dependent => :destroy, :order => "order_num ASC"
accepts_nested_attributes_for :flights, :allow_destroy => true
attr_accessible :flights_attributes
end
You'll also need to ensure that your JSON response uses keys that Rails will recognize. You can either modify the JSON response or do something like:
response = JSON.parse(json_string)
response[:flights_attributes] = response.delete(:flights)
# ...
Then you can just do
Trip.create(response)
You'll want to ensure that everything is created/updated as expected. For more on accepts_nested_attributes_for, see the documentation: http://apidock.com/rails/ActiveRecord/NestedAttributes/ClassMethods/accepts_nested_attributes_for.
I think accepts_nested_attributes_for is convenient, but note that there are some that think it should be deprecated (e.g., here: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/, see here for a response: https://stackoverflow.com/a/17639029/1614607).