I'm using active_model_serializers in rails application it's working perfectly fine, but when dealing with associations it's returning all the attributes of the associated model (including the created_at and updated_at) which I don't want to be returned.
class ReservationSerializer < ActiveModel::Serializer
attributes :id, :pnr_no, :train_no, :passenger_name, :from_to,
:travel_class, :cancelled, :travel_time
has_many :reservation_seats
end
...
attributes of reservation are returned, which are fine therefore only
including the relationship attributes i.e for reservation_seats
...
"relationships": {
"reservation-seats": {
"data": [
{
"id": 4,
"reservation-id": 5,
"seat-no": "26",
"position" : "2",
"created-at": "2017-05-27T23:59:56.000+05:30",
"updated-at": "2017-05-27T23:59:56.000+05:30"
}
]
}
I tried creating a new file as well, where I have defined the attributes which needed to returned, but in this case it's just returning type only.
class ReservationSeatSerializer < ActiveModel::Serializer
attributes :id, :seat_no, :position
belongs_to :reservation
end
this is resulting in:
"relationships": {
"reservation-seats": {
"data": [
{
"id": "4",
"type": "reservation-seats"
}
]
}
}
Basically for the association I only want few attributes to be returned.
Thanks
The JSON API spec wants you to reduce the response data and database requests by just including the type and identifier of the relation. If you want to include the related object, you have to include it:
ActiveModelSerializer Example:
render json: #reservation, include: '*'
This includes all the relationships recursively. These related objects will end up in the included array.
Take a look at the JSON API spec and the active_model_serializer docs.
You could try adding a serializer of your association inside of it:
class ReservationSerializer < ActiveModel::Serializer
attributes :id, :pnr_no, :train_no, :passenger_name, :from_to,
:travel_class, :cancelled, :travel_time
has_many :reservation_seats
class ReservationSeatSerializer < ActiveModel::Serializer
attributes :id, :seat_no, :position
end
end
Related
I have the following models:
class Conversation < ApplicationRecord
enum status: [ :active, :archived ]
belongs_to :person
end
class Person < ApplicationRecord
has_many :conversations
end
When I run the following query,
#people = Person
.select('`user`.`name`, `conversations`.`status` AS conversation_status')
.joins(:conversations)
#people.map &:conversation_status
# => [1, 1, 0]
I get the values of conversation_status un-typecasted because the returned records are instances of the Person model, but the enum is defined on Conversation. I was wondering if there was a way to type cast an enum column's value manually. I can do the following to type cast the values:
#people.map do |person|
Conversation.statuses.find { |name, i| i == person.conversation_status }.first
end
# => ['archived', 'archived', 'active']
but it seems a bit awkward. Is there an official/rails/less awkward way of getting there?
Digging into the source code, I found that there is an ActiveRecord::Enum::EnumType instance that can deserialize the value for me, but I did not find any public way to fetch it.
I have a column in my table with JSON data type where I am storing an array.
Migration
class AddEditSummaryToPosts < ActiveRecord::Migration
def change
add_column :posts, :edit_summary, :json
end
end
Post
class Post < ActiveRecord::Base
serialize :edit_summary, JSON
end
This is how the data is stored:
:id => 2,
:edit_summary => [
{
"user_id" => 56,
"date" => "2016-08-09T07:46:04.555-04:00"
},
{
"user_id" => 57,
"date" => "2016-08-08T06:35:44.345-04:00"
},
]
I referred this post and wrote a query which is working fine
SELECT *
FROM posts, json_array_elements(edit_summary) as elem
WHERE elem->>'date' BETWEEN '2016-08-07 00:00:00' AND '2016-08-10 23:59:59';
Now my question is there any way to do same rails way?
You can execute the same query in Rails by providing raw SQL conditions.
However, I feel like edit summaries belong to another relation instead.
A Post has_many EditSummary, benefits:
you'll be able to index dates and FK's.
DB consistency (there's nothing preventing user_id from being a invalid id).
separation
DB validation
I have a service that does a request,
.factory('movieService', ['$http', function($http) {
return {
loadMovies: function() {
return $http.get('/movies_users.json');
}
};
}])
This is the JSON output and is the result of 2 tables being joined. A user table and a movie table. As you can see the users are associated with 1 or more movies.
[
{"id":1,
"email":"peter#peter.nl",
"movies":[
{
"id":4,
"title":"Creed",
movie_id":"312221"
},
{
"id":5,
"title":"Star Wars: Episode VII - The Force Awakens",
"movie_id":"140607"
}
]
},
{"id":2,
"email":"jan#jan.com",
"movies":[
{
"id":4,
"title":"Creed",
movie_id":"312221"
}
]
}
]
And this is the movies_users_controller.rb
def index
movie = Movie.all
render :json => movie.to_json(:include => :users)
end
Is it possible to only show the current user in the JSON output instead of both users?
Is it possible to only show the current user in the JSON output
instead of both users?
That implies that you have some authentication system (if you don't, you can have a look at devise).
Instead of fetching all the movies, just get the movies of the current user.
#movies = current_user.movies
In order to make this work, you'll have to have a relationship between the User model and the Movie model, something like this:
# user.rb
has_many :user_movies
has_many :movies, through: user_movies
# user_movie.rb
belongs_to :movie
belongs_to :user
# movie.rb
has_many :user_movies
has_many :users, through: :user_movies
Also, it seems that you are building an API, I would advice to use something like jbuilder to build your json object, it will be cleaner and you will be able to display pretty much everything you want.
I have the following model in rails:
class Model < ActiveRecord::Base
# id — integer
# name — string
# model_id — integer
belongs_to :parent, class_name: 'Model', foreign_key: 'model_id'
has_many :children, class_name: 'Model', foreign_key: 'model_id'
end
I am using adjacency structure, which can have infinite depth. I am on a Postgres database using recursive selects.
What will be the most sane way to get a nested hash of objects? I tried to select instances of Model and sort them, yet could not bring this to any usable result.
Lets say I have four Model instances saved in my database: Model_1, Model_2, Model_3 and Model_4. Model_3 is a child of Model_2 and Model_4 is a child of Model_3.
Here is an output I am trying to achieve (a nested hash of Model instances):
{
#<Model_1...> => {},
#<Model_2...> => {
#<Model_3...> => {
#<Model_4...> => {}
}
}
}
Any ideas?
Update: Tree is already recovered — either as a CollectionProxy, Relation or any other array-ish data structure. I wan't to sort that tree into the hash of nested hashes.
I would name it parent_id field.
belongs_to :parent, class_name: "Model"
has_many :children, class_name: "Model", foreign_key: "parent_id"
When you have the hash, you would use sort or sort_by:
http://www.ruby-doc.org/core-2.1.0/Enumerable.html#method-i-sort_by
def sort(hash)
hash.sort { |m1, m2| m1.id <=> m2.id }
sort(hash.children)
end
First, define the ff method inside your Model class:
def to_hash
if children.empty?
{self => {}}
else
{self => children.inject({}) {|hash, model| hash.merge(model.to_hash) } }
end
end
Then do the ff to get the output you want:
top_level_models = #code that queries top-level models while eager-loading all nested children
hash_of_nested_models = top_level_models.inject({}) {|hash, ancestor| hash.merge(ancestor.to_hash) }
The hash argument that you pass to includes should cover the depth of your nesting. The argument passed to includes above will be the nesting for children with a depth of 3 descendants. For as long as you includes all the nested children in your where query, generating the hash will not do any more db queries.
Hope that helps!
Getting AR to do this without N+1 queries would be difficult. Will have to write something that works with the data in memory.
You would have to write a custom function which looks something like:
def to_hash
root_hash = {}
# Load all the model into memory and into a hash to allow faster access when we have the id
models = Hash[Model.all.collect {|m| [m.id, m]}]
# The resultant hash for each child
models_with_hash = Hash[map.values.collect {|m| [m.id, {}]} ]
# Stitch them together
models.each do |id, m|
if m.model_id.nil?
root_hash.merge! m => models_with_hash[m.id]
else
# should name model_id to parent_id
models_with_hash[m.model_id].merge! m => models_with_hash[m.id]
end
end
root_hash
end
I'm new to rails, but most of the documentation is towards user inputting something into the view and it eventually gets passed into the database.
Is there a rails way of storing below into a SQL database? Do I put it in the model or controller?
Is there a clean way to store this data, or do I have to explicitly store every attribute in this Hash individually?
I've already made the migrations manually that matches most if not all of the hashed data below, but is there a tool that can convert these hashes into a relational Data model?
.
{
"_id" : "36483f88e04d6dcb60684a33000791a6bc522a41",
"address_components" : [
{
"long_name" : "ON",
"short_name" : "ON",
"types" : [
"administrative_area_level_1",
"political"
]
},
{
"long_name" : "CA",
"short_name" : "CA",
"types" : [
"country",
"political"
]
},
{
"long_name" : "M5J 1L4",
"short_name" : "M5J 1L4",
"types" : [
"postal_code"
]
}
],
"formatted_address" : "ON, Canada",
"formatted_phone_number" : "(416) 362-5221",
"geometry" : {
"location" : {
"lat" : 43.640816,
"lng" : -79.381752
}
},
"icon" : "http://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id" : "36483f88e04d6dcb60684a33000791a6bc522a41",
"international_phone_number" : "+1 416-362-5221",
"name" : "Scandinavian Airlines",
"reference" : "CoQBcgAAAMobbidhAbzwIMkxq3GTHzzlEW4hAAwxg5EmGDP7ZOcJRwUK29poFMTDvED5KW9UEQrqtgTwESj_DuCAchy6Qe5pPZH9tB47MmmuQHvyHFlApunmU3MN05_KLekN5hEbrW7Gv2ys2oXmn7FpvD7-0N0QILlFXCiwL5UlYWo2sEg3EhBMBsrkHBu4WCFsMCHRqgadGhTM3BVWR15l9L87zL1uN1ssoW4WCw",
"types" : [
"restaurant",
"food",
"establishment"
],
"url" : "https://plus.google.com/100786723768255083253/about?hl=en-US",
"utc_offset" : -300,
"vicinity" : ""
}
This data structure can be stored by a matching hierarchy of models/associations.
There is a very clean way which is...
Use accepts_nested_attributes_for. This will work for your entire structure except for the 'types' arrays which contains simple lists of strings. However, you can use a workaround for this specific case.
The only thing that cannot be stored (easily) is the id. ActiveRecord won't permit you to set the id directly, as it is supposed to be an implementation detail of the backing database. In your case, you can simply borrow the _id field which seems to contain the same data and insert that into an alias of some sort.
Here is an example of the code you might use:
class Address < ActiveRecord::Base
has_many :address_components
has_many :address_types
has_one :geometry
attr_accessor :address_components_attributes, :geometry_attributes
accepts_nested_attributes_for :address_components, :geometry
def types=(types)
types.each do |t|
self.address_types << AddressType.build(name: t)
end
end
def _id=(orig_id)
self.original_id = orig_id
end
end
class AddressType < ActiveRecord::Base
belongs_to :address
end
class Geometry < ActiveRecord::Base
belongs_to :address
has_one :location
attr_accessor :location_attributes
accepts_nested_attributes_for :location
end
class Location < ActiveRecord::Base
belongs_to :geometry
end
class AddressComponent < ActiveRecord::Base
belongs_to :address
has_many :component_types
def types=(types)
types.each do |t|
self.component_types << ComponentType.build(name: t)
end
end
end
class ComponentType < ActiveRecord::Base
belongs_to :address_component
end
Now you can store the entire structure using:
Address.create(data_hash)
If you have setter methods on your model, they can handle the data and import from this hash as you would want.
For example, given the hash above, if you had a method:
def address_components=(ac)
# Handle address components
end
This will get called if when you do the following (assuming the name of your model is MyModel and the hash is stored in #hash).
MyModel.new(#hash)
All the keys will trigger setter methods of the structure 'key='. This is very powerful - if you have a very well structured model, but have an arbitrary hash, you can create methods that handle the keys in the hash. Based on these, you can build new objects, build associations and save it all at the same time.
Note - you may need to strip out some keys, or handle some keys that use reserved ruby terms in a custom way.