class Event(models.Model):
start_date = models.DateTimeField()
end_date = models.DateTimeField()
class EventSerializer(serializers.ModelSerializer):
bar = serializers.SerializerMethodField()
class Meta:
model = Event
fields = ('id', 'start_date', 'end_date')
It works all fine and dandy:
GET .../api/v1/Event/
{
"count":23,
"next":null,
"previous":null,
"results":[{
"databaseId":101488,
"start_date":"2013-11-01T09:46:25",
"end_date":"2013-11-02T09:46:25"
},...
]}
Now when I create a new event:
POST /api/v1/Event/
{
"start_date":"2013-11-03T09:46:25",
"end_date":"2013-11-04T09:46:25"
}
In the JSON response I get:
{
"databaseId":101489,
"start_date":"2013-11-03T09:46:25.250000",
"end_date":"2013-11-04T09:46:25.750000"
}
So I get back a more precise format. I'd like to get back exactly the same format, so the client developer won't have to write different parser codes.
I'm using Python 2.7, DRF 3.1.3, Django 1.4.21 (I know it's old but it's a large codebase, one day we'll migrate).
So far I couldn't figure out what causes this, but explicitly enforcing format string helps:
start_date=serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S')
Related
I have a django-rest-framework model viewset (for tests) that is using a serialize like this:
class ProcessSerializer(serializers.Serializer):
class Meta:
model = Process.draft_model
fields = ['id', 'name']
class TestSerializer(serializers.ModelSerializer):
process = ProcessSerializer()
class Meta:
model = ConfigurationTest
fields = [
'id',
'name',
'process',
]
This works great when retrieving tests, but doesn't work for creation / updates, where I would ideally like to just provide the ID with a request like this:
{
process: 1
name: 'A new test'
}
When sending that request to the server I get an error like Invalid data. Expected a dictionary, but got int
What I tried:
Have process and process_id as included fields and just sending process_id in a POST request. In that case I get an error like process_id cannot be null, which is plain confusing.
Use a different serializer for the create action, that uses process as a plain PrimaryKeyRelatedField without a nested serializer. This works nicely for getting the request, but also obviously means the server reponse to that POST request doesn't include the nicely nested serializer.
Models for reference
class ConfigurationTest(...):
name = CharField(max_length=120)
process = ForeignKey(Process)
class Process(...):
name = CharField(max_length=120)
I would give a serializer like this. One serializer field for read_only where it uses ProcessSerializer and process_id for write_only as integer.
class TestSerializer(serializers.ModelSerializer):
process = ProcessSerializer(read_only=True)
process_id = IntegerField(write_only=True)
class Meta:
model = ConfigurationTest
fields = [
'id',
'name',
'process',
'process_id',
]
And POST this:
{
process_id: 1
name: 'A new test'
}
I am not 100% sure you don't need to override create/update but this should work fine.
N.B.:
I see that you tried something with similar logic. Was it the same code though ?
I'm using serializers of django-rest-framework (DRK)
I'm fetch data from an external API, and I want to convert this datas into an internal model (here Period)
The thing is that the field's format of the external api are like this :
{"DateFrom": "2020-02-10T00:00:00"}
I want to rename into "date_from" field.
Here what I tried :
Serializer :
class PeriodSerializer(serializers.ModelSerializer):
date_from = serializers.DateTimeField(write_only=True, source='DateFrom')
class Meta:
model = Period
fields = ('date_from',)
Notice that I tried with "write_only=True, source='DateFrom'"
And then in my code :
json = {"DateFrom": "2020-02-10T00:00:00"}
serializer = PeriodSerializer(data=json)
serializer.is_valid() # This is returning False
print(serializer.errors)
And then the output is :
{'date_from': [ErrorDetail(string='This field is required.', code='required')]}
How handle that ? (in the best way (good practice))
I think you have something backwards here. Given your model and what you expose in your API, you would want to do:
class PeriodSerializer(serializers.ModelSerializer):
DateFrom = serializers.DateTimeField(write_only=True, source='date_from')
source specifies the data source on the model, while the name of the serializer field is what the field will be named when serialized.
i'm trying to convert a string with this form "2019-07-17T16:00:50.282203+01:30" to timezone format so i can update models.DateTimeField field.
models.py:
from django.utils import timezone
class task(models.Model):
title = models.CharField(max_length=100)
create_date = models.DateTimeField(default=timezone.now)
.
.
.
request will be like this:
{
"title": "editeddd task",
"create_date" : "2019-07-17T16:00:50.282203+01:30"
}
being stuck in converting UTC part.
view.py:
.
.
.
create_date = datetime.strptime(self.request.data['expiration_date'], '%Y-%m-%dT%H:%M:%S.%f%z')
.
.
the error is for %z part. it can converting "2019-07-17T16:00:50.282203+0130" but not "2019-07-17T16:00:50.282203+01:30" (notice to : in UTC part)
As you are referencing request.data, it looks as though you are using Django REST Framework. In that case the most straightforward way is to use DRF's serializers to parse / validate the incoming data.
from rest_framework import serializers
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = ('title', 'create_date')
If you just want to retrieve the validated data you can do:
>>> serializer = TaskSerializer(data=request.data)
>>> serializer.is_valid(raise_exception=True)
>>> serializer.validated_data
OrderedDict([('title', 'editeddd task'), ('create_date', datetime.datetime(2019, 7, 17, 14, 30, 50, 282203, tzinfo=<UTC>))])
Or if you want to save a new task:
task = serializer.save()
It's important to validate all incoming data and validating each field manually by reaching into request.POST or request.data tends to be error prone. DRF's serializers (or Django's forms) have a lot of pre-built logic and make the task significantly easier and safer.
Being strict while sending and tolerant while receiving, you should do a little string mangling on that create_date string before passing it on to strptime.
My suggestions is to work with a regular expression, or to have look at rreplace - How to replace the last occurrence of an expression in a string? to get rid of that last :.
Hope this helps!
I am trying to understand why this is happening. I have a Django DateTime field and Django Rest Framework serializer that uses the field.
I am trying to compare the dates for both of them and get the following results from JSON endpoint and model result:
DRF: 2018-12-21T19:17:59.353368Z
Model field: 2018-12-21T19:17:59.353368+00:00
Is there a way to make them similar? So, either to make both of them be "Z" or "+00:00."
It's because django rest framework uses it's own datetime formating. To change that, in your settings.py file, there should exist a dict variable called REST_FRAMEWORK (if not create it) and add this:
REST_FRAMEWORK = {
...
'DATETIME_FORMAT': "%Y-%m-%d - %H:%M:%S",
...
}
Also check USE_TZ variable state too in your settings.py
I came here from google looking for a quick fix to get this (e.g. copy and paste). So, borrowing from #RezaTorkamanAhmadi's answer, for anyone looking to get a DRF serializer DateTimeField to have the format 2018-12-21T19:17:59.353368+00:00 (the same format as the default models.DateTimeField so that your serialized values match your model values -- the OP's question and mine too) you're looking for either:
# settings.py
REST_FRAMEWORK = {
...
'DATETIME_FORMAT': "%Y-%m-%d %H:%M:%S.%f%z",
...
}
or if you just want it for a specific DateTimeField serializer field you're looking for
from rest_framework import serializers
class MySerializer(serializers.Serializer):
some_date = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S.%f%z")
Sources:
https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior
https://www.django-rest-framework.org/api-guide/fields/#datetimefield
https://stackoverflow.com/a/53893377/10541855 (Reza Torkaman
Ahmadi's answer)
Apart from previous answer, also you can change DateTime format in your serializer.
from rest_framework import serializers
class YourSerializer(serializers.ModelSerializer):
your_datetime_field = serializers.DateTimeField(format="%Y-%m-%dT%H:%M:%S")
class Meta:
model = YourModel
fields = '__all__'
I'm currently using Django RestFramework to create APIs that use both GET and POST to retrieve/insert data within my Django application. I currently have a database model called TransactionDateTime that has a field called start_dt (see below), which takes DateTimeField. The challenge is that I'm passing a string in my json POST data structure (see below) and I need to override the create method in order to loop through the JSON structure to convert the string to the appropriate datetime structure. I know the logic to be used to successfully convert string to datetime, because I was able to perform it in the Django shell (see below), but I don't know how to override the create method and write the appropriate code within the create method to make this happen. Please assist. Below is a copy of my view that is successfully returning JSON data structure via GET, with a BIG question within the create method
TransactionDateTime model from models.py
class TransactionDateTime(models.Model):
room = models.ForeignKey(Room, on_delete = models.CASCADE)
start_dt = models.DateTimeField('start_dateTime')
end_dt = models.DateTimeField('end_dateTime', blank=True, null=True)
def __str__(self):
return str(self.start_dt)
Data Structure to be used on POST
[
{
"start_dt": "2015-01-28 03:00:00"
},
{
"start_dt": "2015-01-28 05:30:00"
}
]
Logic to convert string to datetime
from datetime import datetime
my_date = datetime.strptime('2015-01-28 05:30:00', '%Y-%m-%d %H:%M:%S')
Django Mixin and View
class DateTimeMixin(object):
serializer_class = SimpleSerializer4
permission_classes = (permissions.IsAuthenticated,)
class DateTimeViewSet(DateTimeMixin, generics.BulkModelViewSet):
def get_queryset(self):
num = self.kwargs['dt_rm']
num2 = self.kwargs['id']
r1 = Room.objects.get(id = num)
s1 = Schedule.objects.get(pk=num2)
u= self.request.user.pk
usr = User.objects.get(pk=u)
if(s1.user.username == usr.username):
queryset = r1.transactiondatetime_set.all()
return queryset
else: raise Http404("User does not exist")
def get_serializer_context(self):
num = self.kwargs['id']
s1 = Schedule.objects.get(pk=num)
var = s1.user.username
context = super(DateTimeViewSet, self).get_serializer_context()
return {'request' : var}
def created(self, request, *args, **kwargs):
???
I think the proper way to go about it is not to write your own custom create method, but rather to teach your serializer how to accept the date format you use, e.g.
class SimpleSerializer4(something_here):
...
start_dt = serializers.DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=['%Y-%m-%d %H:%M:%S'])
...
Then later all you have to do is to add CreateModelMixin to you ViewSet and it should work, e.g.
from rest_framework.mixins import CreateModelMixin
...
class DateTimeViewSet(DateTimeMixin, CreateModelMixin, generics.BulkModelViewSet):
...
Docs on DateTimeField here
Docs on extending view sets here
And I found it often very helpful to look how things are done in the rest_framework itself, so here is the link to the source on GitHub