I am working on a backend of an application written in Sinatra.
It has a route "/notifications"
which renders all the notifications in JSON.
I want to change the json structure and wrote some custom serializer and it is failing now.
the error i get is
"{"message":"undefined method `read_attribute_for_serialization' for nil:NilClass"}"
I have a file called webservice/notification.rb
which selects a notification serializer.
the code is something like this
serializer = NotificationSerializer
json serialize(notifications, root: :notifications, each_serializer: serializer)
The NotificationSerializer is something like this.
class NotificationSerializer < Serializer
attributes :id, :tag, :event, :time, :read
has_one :reference, polymorphic: true, include: true
The reference here can be a lot of things.
the notification model defines reference as
def reference
company || contact || deal || invitation || meeting || todo || reference_email || reference_user ||
contact_import_job
end
now all of these models in reference have there Serializer implements in directory Serializer/*
I want to make custom Serializers for all of these which will render limited information.
how can I call my custom Serializer for things inside reference.
I wrote a custom serializer for notifications and called it like this inside my refernce function and it worked.
...|| UserNotificationSerializer.new(reference_user) || ...
but if i do the same for my other models i get the error given above.
what would be the correct way to call my custom serializers.
A good way to do it is to write an instance method on the model:
class Notification < ActiveRecord::Base
def public_attributes # or call it whatever
attributes_hash = attributes
# in activerecord, the attributes method turns a model instance into a hash
# do some modifications to the hash here
return attributes_hash
end
end
then say you're returning json in a controller:
get '/some_route' do
#notifications = Notification.all # or whatever
serialized_notifications = #notifications.map(&:public_attributes)
# For a single record, you could do #notification.public_attributes
json_data = serialized_notifications.to_json # a serialized array of hashes
content_type :json
return json_data
end
Related
I am using a model that consists of many fields. There is one field that is a property, and it returns an instance of a model. Something like the following:
class A(Model):
#property
def last_obj(self):
# Returns an object
The issue I'm having is that this property can return 2 different Model types. It can either return an object of type one, or an object of type two. This creates complications in the serializer. I have a serializer that consists of nested serializers. The two objects are similar enough that one serializer can be used over the other, but then the fields unique to them are not serialized.
class A_Serializer(Serializer):
class SerializerOne(CustomSerializer):
#Serializes certain fields in custom manner
class Meta:
model = models.one
exclude = ('id')
base_name = 'one'
class SerializerTwo(CustomSerializer):
#Serializes certain fields in custom manner
class Meta:
model = models.two
exclude = ('id')
base_name = 'two'
last_obj = SerializerOne() #This works, but not viable because of what I stated above
So my solution to be able to dynamically call the correct serializer, was to conditionally serialize the property within a serializer method field:
class A_Serializer(Serializer):
class SerializerOne(CustomSerializer):
#Serializes certain fields in custom manner
class Meta:
model = models.one
exclude = ('id')
base_name = 'one'
class SerializerTwo(CustomSerializer):
#Serializes certain fields in custom manner
class Meta:
model = models.two
exclude = ('id')
base_name = 'two'
def get_last_obj(self, instance):
if (isinstance(instance.last_obj, models.one)):
return self.SerializerOne(instance.last_obj).data
else:
return self.SerializerTwo(instance.last_obj).data
last_obj = SerializerMethodField() #Does not work
However, this solution creates the error "NoneType Object is not iterable" and it happens at
super(ReturnDict, self).__init__(*args, **kwargs) in rest_framework/utils/serializers_helpers.py in init which causes the error at return ReturnDict(ret, serializer=self) in rest_framework/serializers.py in data
I do not understand why calling a nested serializer like obj = Serializer() works, but calling the serializer explicitly like obj = Serializer(instance).data does not work in this situation. Can anyone figure out what I have been doing wrong? Thank you.
I have found out from here that when working with hyperlinked relations (which in my case was the CustomSerializer that SerializerOne and SerializerTwo were inheriting from), you must pass the request object through context. The reason why obj = Serializer() works, but obj = Serializer(instance).data does not work is that in the former, the request object is automatically added through context through DRF. While in the latter, it is being explicitly called so you must pass context with the request object manually. So for me to get it working, I did:
return self.SerializerOne(instance.last_obj, context={'request': self.context['request']}).data
inside the serializer method field.
I have this code that brings one vacancy from my model Vacancy and then render in json the attributes according to the serializer VacancyDetailSerializer:
Controller
vacancy = Vacancy.find(params[:id])
render json: vacancy, serializer: VacancyDetailSerializer,
include: [:restaurant]
The thing here is that in the include: [:restaurant] I want to specify a custom serializer the way I did with vacancy, because right now is taking the serializer of RestaurantSerializer, but I don't want to take that file, is there a way to do it with the include? Maybe is here in the controller, or maybe in the serializer?
If you have belongs_to :restaurant association in the VacancyDetailSerializer, then serializer for this association can be specified:
class VacancyDetailSerializer < ActiveModel::Serializer
belongs_to :restaurant, serializer: AnotherRestaurantSerializer
end
Or it can be overridden by providing a block:
class VacancyDetailSerializer < ActiveModel::Serializer
belongs_to :restaurant do
AnotherRestaurantSerializer.new(object.restaurant)
end
end
Or a custom association serializer lookup can be implemented.
I am trying to store my ruby object in couchdb with couchrest. I am extending my model from CouchRest::Model::Base But still i am unable to see the changes in db.
I've defined Server as CouchRest.new also mentioned in model to use_database 'players'
# Controller Method
get '/new/:name' do
DB = SERVER.database!('players')
#new_player = Player.new(params['name'])
#new_player.create
"success: #{#new_player.persisted?}" #shows true
# Model
class Player < CouchRest::Model::Base
use_database 'players'
property :name ,String
timestamps!
def initialize(arg)
#name=arg
end
end
How To Persist the object?
How can i retreive all persisted objects?
Is there any simple applications which i can refer to?
in rails 4, I can not figure out how to fetch model and associated models together.
For example, I have a User model which has_many Message
The following code work properly.
#user = User.find 29, include: :messages
#messages = #user.messages
However, when I try to return #user with #messages in json
render :json #user
The returned result does not contain #messages, only the #user data is returned.
One workaround I can do is to construct a hash with
{user: #user, messages: #messages}
But the problem is messages are not nested or associated to user.
Is there a activemodel/activerecord buildin method to render associated data in a easier way?
as always, the documentation on the rails side is pretty bad. what you need to do is call either call to_json with options or override the models to_json method: http://apidock.com/rails/ActiveRecord/Serialization/to_json
Some of attributes specified in ActiveModel are non db attributes which are just defined as getter setter. Problem is that these attributes values are not reflected on activeresource record on client side.
#server side code
class Item < ActiveRecord::Base
#not null name attribute defined on db
end
class SpecialItem < ActiveRecord::Base
#nullable item_name attribute defined on db
#association many to one for item defined here
#name accessor
def name
if !item_name.nil?
return item_name
else
return item.name
end
end
end
#client side code
class SpecialItem < ActiveResource::Base
schema do
attribute 'name', 'string'
end
end
I am getting nil value for attribute name for SepcialItem record on client. Basically i am trying to map accessor method name to name attribute on client side.
What is possible solution?
ActiveResource is a means of communicating with a RESTful service and requires the class variable site to be defined, i.e.
class SpecialItem < ActiveResource::Base
self.site = 'http://example.com/'
self.schema = { 'name' => :string}
end
This would utilize the default Rails collection and element conventions. So for a call to SpecialItem.find(1), ActiveResource would route to GET http://example.com/specialitems/1.json