Django rest framework updating time using EPOCH ti

2019-04-09 02:26发布

问题:

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.

回答1:

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')


回答2:

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))


回答3:

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.



回答4:

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)


回答5:

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()))