user1639926
user1639926

Reputation: 862

Django rest framework updating time using EPOCH time

I need to take a form that has a field "starttime" that is in EPOCH and convert it to

  1. python datetime
  2. fieldname = "created"

when I have:

models.py

class Snippet(models.Model):
    created = models.DateTimeField(auto_now_add=True

    class Meta:
        ordering = ('created',)

serializers.py

import time
class SnippetSerializer(serializers.ModelSerializer):
    starttime  = serializers.SerializerMethodField('epoch')

    def epoch(self, obj):
        """ Return epoch time for a datetime object or ``None``"""
        try:
            return int(time.mktime(obj.created.timetuple()))
        except (AttributeError, TypeError):
            return None

    class Meta:
        model = Snippet
        fields = ('starttime')

If I:

"GET" /snippets/1/

{"id":1, 'starttime':13232111}

I want to be able to do:

"POST" /snippets/1/ {"id":1, 'starttime':1}

{"id":1, 'starttime':1}

right now, it just ignores the request. I am forced to use unix epoch times to conform to existing API's.

Upvotes: 6

Views: 5820

Answers (5)

iman
iman

Reputation: 323

Please see below code it will help you to solve your problem.

class UnixEpochDateField(serializers.DateField):
    def to_representation(self, value):
        return int(time.mktime(value.timetuple()))

Upvotes: 0

Diviei
Diviei

Reputation: 181

Adapting Kevin Stone code for Django Rest Framework 3:

class UnixEpochDateField(serializers.DateTimeField):
    def to_representation(self, value):
        """ Return epoch time for a datetime object or ``None``"""
        import time
        try:
            return int(time.mktime(value.timetuple()))
        except (AttributeError, TypeError):
            return None

    def to_internal_value(self, value):
        import datetime
        return datetime.datetime.fromtimestamp(int(value))

Upvotes: 18

kerryz
kerryz

Reputation: 381

Based on Kevin Stone's answer, I've created timezone aware serializer fields, including a UnixEpochDateField. The actual conversion methods are static because I've found them useful elsewhere in my code.

class UnixEpochDateTimeField(DateTimeField):
    def to_native(self, value):
        """
        to_native method is responsible for turning the 
        Python object into a simple, serializable value.
        Here: return epoch time for a datetime object or `None`
        """
        return self.datetime_to_epoch(value)

    def from_native(self, value):
            return self.epoch_to_datetime(value)

    @staticmethod
    def datetime_to_epoch(value):
        try:
            return int(calendar.timegm(value.utctimetuple()))
        except (AttributeError, TypeError):
            return None

    @staticmethod
    def epoch_to_datetime(value):
        try:
            return datetime.datetime.utcfromtimestamp(int(value)).replace(tzinfo=utc)
        except (ValueError, TypeError):
            raise ValidationError('%s is not a valid value' % value)


class UnixEpochDateField(DateField):
    def to_native(self, value):
        return self.date_to_epoch(value)

    def from_native(self, value):
            return self.epoch_to_date(value)

    @staticmethod
    def date_to_epoch(value):
        try:
            return int(calendar.timegm(value.timetuple()))
        except (AttributeError, TypeError):
            return None

    @staticmethod
    def epoch_to_date(value):
        try:
            return datetime.date.fromtimestamp(int(value))
        except (ValueError, TypeError):
            raise ValidationError('%s is not a valid value' % value)

Upvotes: 1

Kevin Stone
Kevin Stone

Reputation: 8981

You want to write your own serializer Field sub-class with overridden to_native() and from_native() for the actual conversion. Here's my attempt:

class UnixEpochDateField(serializers.DateTimeField):
    def to_native(self, value):
        """ Return epoch time for a datetime object or ``None``"""
        import time
        try:
            return int(time.mktime(value.timetuple()))
        except (AttributeError, TypeError):
            return None

    def from_native(self, value):
        import datetime
        return datetime.datetime.fromtimestamp(int(value))

And then use that field in your Serializer definition:

class SnippetSerializer(serializers.ModelSerializer):
    starttime  = UnixEpochDateField(source='created')

Upvotes: 20

mariodev
mariodev

Reputation: 15559

If I understand it correctly, you need to deserialize the starttime field and use it's value to update created field. If that's so, then you need to create your own serializer field and override field_from_native (it doesn't return anything by default, that's why it doesn't have any effect in your case):

class EpochSerializerField(SerializerMethodField):
    def field_from_native(self, data, files, field_name, into):
        starttime = data['starttime']
        # generate `created` value using `starttime`

        into['created'] = created
        super(EpochSerializerField, self).field_from_native(data, files, field_name, into)

so the idea is simple, just reverse the calculation to generate created value and use your new serializer field. You can also move the content from the epoch method into field_to_native method of the new serializer field.

Upvotes: 0

Related Questions