I have an active record class with an embedded sample:
class LabResults < ActiveRecord::Base
serialize :sample
end
class Sample
attr_accessor :values # GSL::Vector of responses
def to_yaml
YAML.quick_emit( self, opts ) { |out|
out.map( "!testfile,2012-02-27" ) { |map|
#values.map{|v| v.to_a }
}
}
end
def analyze; end; # do stuff with values
end
I want to serialize and store sample in the database, but GSL::Vector (from gsl gem), does not have a to_yaml method. Defining to_yaml and YAML.quick_emit for Sample is apparently deprecated when using Rails 3.2's default YAML engine Psych.
Any ideas how to serialize and de-serialize this object?
You can write a custom (de)serializer for the column, and pass it as the second argument to "serialize", e.g.:
serialize :sample, SampleSerializer.new
Where SampleSerializer is a class that defines "load" and "dump" methods.
More detail in this answer: ActiveRecord serialize using JSON instead of YAML
Related
I'm using Django rest framework 3.7.7
I want to serialize an object instance and then deserialize it (without saving to DB again).
So I created a serializer that inherits from serializers.ModelSerializer.
To this model instance I sometimes add a field within the request that is not part of the model, using property setter. I did add it to the serializer as well, but when serializing - I don't see that field in the serialized data.
This is the model:
class MyModel(models.Model):
manager = models.CharField(max_length=200)
reviewer = models.CharField(max_length=200)
#property
def note(self):
return getattr(self, '_note', None)
#note.setter
def note(self, value):
self._note = value
This is the model serializer:
class MyModelSerializer(serializers.ModelSerializer):
note = serializers.CharField(write_only=True, required=False, max_length=1000, allow_null=True)
class Meta:
model = MyModel
fields = [
'manager',
'reviewer',
'note'
]
This is how I use the serializer:
ser_instance = MyModelSerializer(self.instance)
ser_data = ser_instance.data
seems like the ser_instance.data contains only the model fields data (without the 'note')
From the write_only--[DRF-Doc], it states that
Set this to True to ensure that the field may be used when updating or creating an instance, but is not included when serializing the representation.
Defaults to False
So, if you need to serialize the note field, remove the write_only=true from serializer.
#serializers.py
class MyModelSerializer(serializers.ModelSerializer):
note = serializers.CharField(required=False, max_length=1000, allow_null=True) # removed "write_only=True"
...
I would like to have a nested object inside a serializer instead of just the foreignkey (or url).
As this documentation says, I just had to specify the serializer class of the nested object in the parent serializer:
# Models
class NestedSample(models.Model):
something = models.CharField(max_length=255)
class Sample(models.Model):
thing = models.BooleanField()
nested = models.ForeignKey(NestedSample)
# Serializers
class NestedSampleSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = api_models.NestedSample
class SampleSerializer(serializers.HyperlinkedModelSerializer):
nested = NestedSampleSerializer() # HERE!
class Meta:
model = api_models.Sample
# Views
class NestedSampleViewSet(viewsets.ModelViewSet):
queryset = api_models.NestedSample.objects.all()
serializer_class = api_serializers.NestedSampleSerializer
class SampleViewSet(viewsets.ModelViewSet):
queryset = api_models.Sample.objects.all()
serializer_class = api_serializers.SampleSerializer
This works very well when I get the objects, but it is not possible to create (=POST) Sample objects anymore, I get the error:
{u'non_field_errors': [u'Invalid data']}
I tried to overwrite the create method in the viewset to get the object using the pk:
class SampleViewSet(viewsets.ModelViewSet):
queryset = api_models.Sample.objects.all()
serializer_class = api_serializers.SampleSerializer
def create(self, request):
request.DATA['nested'] = get_object_or_404(api_models.NestedSample, pk=request.DATA['nested'])
return super(SampleViewSet, self).create(request)
But it doesn't work as well.
Any idea?
I also found this question I can relate with which of course solves the problem but do not let me expose the full nested object, so back to the beginning.
Thanks,
I can think of two solutions to this problem. I prefer the first one.
First solution:
Use a django model form to create objects. Override the create and update methods. A sample create method:
def create(self, request):
form = SampleForm(data=request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
return Response(dict(id=instance.pk), status=status.HTTP_201_CREATED)
return Response(form.errors, status=status.HTTP_400_BAD_REQUEST)
this way you can create Sample objects with any kind of validation you like.
Second solution:
Override get_serializer_class method and return serializer class based on request method. Define two serializers one for post and put and one for list and retrieve.
Can you confirm that you're sending a JSON encoded request - i.e. the request has the content type set to JSON ?
If not, the post is most probably send using form format which doesn't support nested.
I'm using acts-as-taggable-on 2.0.6 for tagging in a Rails 3.0.9 app. I've successfully added db-backed attributes to the Tag model, for example each tag has an RSS feed so I can call #post.tags.first.feed_url to grab the feedburner URL from my database.
But while I can add attributes to the Tag model, it seems I can't add instance methods. I created the instance method:
class Tag < ActiveRecord::Base
...
def subscribable?
!feed_url.blank?
end
...
end
But when I call #post.tags.first.subscribable? I get the error:
NoMethodError: undefined method `subscribable?' for #<ActsAsTaggableOn::Tag:0x00000100d32290>
Is there a way to tell ActsAsTaggableOn::Tag objects to inherit model methods from the Tag model?
You need to add this /config/initializers/tag_monkey_patch.rb
class ActsAsTaggableOn::Tag
def subscribable?
!feed_url.blank?
end
end
I'm wondering the exact same thing. As far as i've tried, it isn't working for me..
class Tag < ActsAsTaggableOn::Tag
def total_count
...
end
end
When I call it in a dirty chained form example, like so:
my_model.my_instance.tag_counts_on(:tags).first.total_count
I get a
undefined method `total_count' for #<ActsAsTaggableOn::Tag id: 1, name: "first">
From what I understand, you shouldn't be declaring a new "Tag" model like this, but should should be overriding the class like this:
class ActsAsTaggableOn::Tag
def total_count
...
end
end
I have the following (simplified model) and wish to access the 'spent' value in the to_json method of the object, ideally as an attribute of the object.
class Task < ActiveRecord::Base
has_many :hours
def spent
self.hours.sum(:spent)
end
end
Is there a way to do this without defining a method and hacking the to_json method? I've been hunting for a way to use scope or by hacking the after_initialize method, but none of these methods provide a 'spent' value when using inspect or to_json on the model.
I need to solve this on models higher up the tree that use a has_many, through relationship too.
You can use the :methods parameter to to_json call.
object.to_json(:methods => :spent)
http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html
The best solution I could find to this was overriding the 'as_json' method to include the attributes that I needed, or to perform the logic required, (in this case the sum of the has_many relationship).
class Task < ActiveRecord::Base
...
def as_json(options = { })
options = {} if options.nil?
self[:job_id] = self.phase.job_id
self[:spent] = self.hours.sum(:spent)
super(options)
end
end
I don't know even if its possible? I need to use instance method with/within scope. Something like this:
scope :public, lambda{ where ({:public => true}) }
and call instance method(complete?) on each record to see if it is completed. public scope here should return all records that are public and are completed and completion of a record is determined by a instance method 'complete?'
Any possibility?
Thanks
Scopes are about generating query logic using ARel. If you can't represent the logic of the complete? method in SQL then you're kind of stuck
Scopes - in rails 3 at least - are meant for chaining together query logic without returning a result set. If you need a result set to do your testing for complete you'll need to do something like
class MyModel < ActiveRecord::Base
scope :public, lambda{ where ({:public => true}) }
def self.completed_public_records
MyModel.public.all.select { |r| r.completed? }
end
end
# elsewhere
MyModel.completed_public_records
Or if you need more flexibility
class MyModel < ActiveRecord::Base
scope :public, lambda{ where ({:public => true}) }
# some other scopes etc
def self.completed_filter(finder_obj)
unless finder_obj.is_a?(ActiveRecord::Relation)
raise ArgumentError, "An ActiveRecord::Relation object is required"
end
finder_obj.all.select { |r| r.completed? }
end
end
# elsewhere
MyModel.completed_filter(MyModel.public.another_scope.some_other_scope)
I created a rubygem for this exact problem a few months back when I had the same problem.
It allows you to add methods that work on the result set of the query, but abstracts the methods into another class so its not muddled in with your model.
Check it out: https://github.com/coryodaniel/collectively