I'm making a Django application, where I am sending requests through rest API. I am using POST and GET and all works pretty well, but when I am trying to use PATCH (as I must firstly upload field "start_time", and then add field "time"), I'm getting following error:
match = time_re.match(value)
TypeError: expected string or buffer
Surely it is about views.py, but I cannot find clear recipe how to do that, thus I don't know where I am wrong.
Thank you.
Views.py
...
elif request.method = 'PATCH':
serializer = TABLESerializer(data=request.data, partial=True)
if serializer.is_valid():
obj = TABLE.objects.get(start_time=request.data['start_time'])
obj.time = serializer['time']
obj.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Serializers.py
class TABLESerilizer(serializers.serializer):
start_time = serializers.DateTimeField(format = None, allow_null=True)
time = serializers.TimeField(format=None, required=False)
models.py
class TABLE(models.Model):
start_time=models.DateTimeField(primary_key=True)
time = models.TimeField(null= True, blank= True, default = '00:00:00')
format - A string representing the output format. If not specified,
this defaults to the same value as the TIME_FORMAT settings key, which
will be 'iso-8601' unless set.
But in your case you set it to None. we get error when python try to parse the data into time object with format None format must me like HH:MM:SS, etc.
update your serializer like below
from rest_framework.settings import api_settings
class TABLESerilizer(serializers.serializer):
start_time = serializers.DateTimeField(format=api_settings.TIME_FORMAT)
time = serializers.TimeField(format=api_settings.TIME_FORMAT)
References: http://www.django-rest-framework.org/api-guide/fields/#timefield
https://github.com/django/django/blob/master/django/utils/dateparse.py#L80
Related
I am deserializing JSON data from file to a model object. I'm using a Django Rest Framework serializer.
The JSON is quite large and I expected some fields that needed tweaking. However, the error messages are not helpful, contrary to those in the docs.
The serializer:
class ChannelSerializer(serializers.ModelSerializer):
class Meta:
model = Channel
fields = '__all__'
The test:
class UnitTests(unittest.TestCase):
def testDeserializeChannel(self):
json = Path(Dirs.CHANNELS_METADATA_TEST, 'A SIMULATED REALITY/youtube/A SIMULATED REALITY.json')
with open(json, 'rb') as file:
stream = io.BytesIO(file.read())
parsed_channel = JSONParser().parse(stream)
serializer = ChannelSerializer(data=parsed_channel, many=True)
if not serializer.is_valid(): # required for serializer.save() to work
print(serializer.error_messages, "\n")
print(serializer.errors)
channel = serializer.validated_data.save()
self.assertEqual(channel[0].title, "A SIMULATED REALITY")
self.assertEqual(channel[0].original_url, "https://www.youtube.com/channel/UCBT3VPJlhJHeIvmDadeZB5w")
The error messages:
{'required': 'This field is required.', 'null': 'This field may not be null.', 'not_a_list': 'Expected a list of items but got type "{input_type}".', 'empty': 'This list may not be empty.', 'max_length': 'Ensure this field has no more than {max_length} elements.', 'min_length': 'Ensure this field has at least {min_length} elements.'}
{'non_field_errors': [ErrorDetail(string='Expected a list of items but got type "dict".', code='not_a_list')]}
We can see required and null errors but without mentioning of the fields.
I suspect the 'all' bit in the serializer is the cause, contrary to explicitly defined field. Can somebody comfirm? And more importantly, can it be fixed?
Kr,
You can use DRF's APIException to create your custom exceptions:
from rest_framework.exceptions import APIException
class MyCustomException(APIException):
status_code = 400
default_detail = 'This serializer is not valid my friend...'
default_code = 'bad_request'
Usage:
. . .
if not serializer.is_valid():
raise MyCustomException()
. . .
Outcome if serializer is not valid:
"detail": {"This serializer is not valid my friend..."}
Like this you can use custom exceptions all around your code. :)
I am using Celery - Redis - Django rest framework together.
The error happens when I try to pass the serializer to the delay of celery within the Django rest framework.
Here is the viewset
class TestSet(viewsets.ModelViewSet):
queryset = Test.objects.all()
serializer_class = ImageSerializer
def create(self, request, *args, **kwargs):
serializer = TestSerializer(data=request.data)
if serializer.is_valid():
image_uploaded= "static_path"
json_data = base64.b64encode(np.array(image_uploaded)).decode('ascii')
result = test_call.delay({'image': json_data})
result = test_call.delay(serializer)
data = {"task": result.task_id}
return Response(data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
#shared_task(name="values")
def test_call(decoded_image):
return decoded_image
The error I get is
EncodeError(TypeError('Object of type Response is not JSON
serializable'))
Update:
Even when I do this, I still get an error
result = test_call.delay({"task": 1})
#shared_task(name="values")
def test_call(decoded_image):
return {"task": 2}
This isn't going to answer your question, but I can't leave a comment (low reputation).
It seems that you are trying to JSON Serialize something that obviously isn't JSON serializable. Based on the name, it is some kind of image data. You could try a different workflow that should be JSON Serializable, example:
One Example:
first save the image somewhere that is accessible later and add the location in the serializer (S3 bucket and then a link to the image)
In your celery task, fetch the image data based on that location
Second Example:
convert the image data into something JSON serializable like a Base64 image string
I'm trying to write a test in which an object is updated using patch.
class Search(models.Model):
id_search = models.AutoField(primary_key=True)
id_user = models.IntegerField(null=False)
.
.
archive = models.BooleanField(default=False)
def test_archive_search(self):
user = User(id_user=75720912,
login='Client:75720912',
)
user.save()
search = Search(
id_user=75720912,
.
.
archive=False
)
search.save()
url = reverse('search-update', kwargs={'id_search':1})
data = {'archive': True}
response = self.client.patch(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
url(r'^search-update/(?P<id_search>\d+)$', SearchUpdateView.as_view(), name='search-update')
class SearchUpdateView(generics.UpdateAPIView):
serializer_class = SearchSerializer
def get_object(self,id_search):
return Search.objects.get(id_search=id_search)
def patch(self, request):
id_search = self.request.query_params.get('id_search', None)
search_object = self.get_object(id_search=id_search)
serializer = SearchSerializer(search_object, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.data, status=status.HTTP_400_BAD_REQUEST)
and get this error:
TypeError: patch() got an unexpected keyword argument 'id_search'
Interesting thing is that when the url was:
url(r'^search-update/$', SearchUpdateView.as_view(), name='search-update')
SearchUpdateView worked properly with given query params.
EDIT
I discovered that passing id_search to patch in view solves this problem when it comes to test, but it spoils working view.
class SearchUpdateView(generics.UpdateAPIView):
serializer_class = SearchSerializer
def get_object(self, id_search):
return Search.objects.get(id_search=id_search)
def patch(self, request, id_search):
#id_search = self.request.query_params.get('id_search', None)
search_object = self.get_object(id_search=id_search)
serializer = SearchSerializer(search_object, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.data, status=status.HTTP_400_BAD_REQUEST)
Still I've got no idea how to bring it together.
if you define the url that way, the patch method will get the id_search param as a keyword argument, as the error says.
Instead, you are retrieving it as if it came as a query param, i.e. not part of the url path but as search-update?id_search=.
Given you are passing None as a default when getting it, it works when you omit it.
So choose which way you want to go.
In case the url definition is correct, then add the id_search argument to the signature of the patch method
and remove the code that retrieves it manually.
Or do both, as suggested in the comments above, by assigning a default value of None to the argument and retrieving it from the request if it is not part of the path
I create realtime chat application using websocket frontend(angular) backend(Django).. I want to store messages in to db(mySql).. when I trying to store message from angular to django.. it give me error:
non_field_errors:
["Expected a list of items but got type "dict"."]
so what is wrong?
model.py
class msg(models.Model):
name = models.ForeignKey(User, on_delete=models.CASCADE)
receiver = models.CharField(max_length=20)
text = models.CharField(max_length=1200)
myDate = models.DateTimeField()
serializer.py
class MesSerializer(serializers.ModelSerializer):
name = serializers.SlugRelatedField(many=False, slug_field='name', queryset=User.objects.all())
class Meta:
model = msg
fields = '__all__'
view.py
class msg_list(APIView):
def get(self, request, format=None):
mes = msg.objects.all()
serializer = MesSerializer(mes, many=True) # convert into JSON
return Response(serializer.data)
def post(self, request, formate = None):
serializer = MesSerializer(data=request.data, many = True) #type list
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)
The trouble is not with the slug. It is that you have used many = True in the view when you pass the data to the serializer, but you are in fact only sending a single message - which is why it is a dict and not a list. Remove that parameter.
def post(self, request, formate = None):
serializer = MesSerializer(data=request.data)
I recently came across a problem which is similar to the OP. Hence would like to share my experience and solution.
I have a dictionary of items, each of them unique. I wanted to use PUT to update an existing item. So I used objects.filter to fetch the object based on the name passed via JSON Request(I know I should have used pk, but I didn't because the items are unique and will remain so). Then I created a Django REST Serializer class object to save the updated object but I failed. This is because I wasn't using many = True. But when I did use it, I faced another error:
"non_field_errors": [
"Expected a list of items but got type \"dict\"."
]
So I finally removed both many=True as well objects.filter.
Instead I used objects.get. This solved the problem because objects.get returns the required object which I want to update whereas objects.filter returns a queryset object and not the actual object that I want to update. Of course objects.get will fail if I have multiple results, in which case I need to ensure there is a pk. Then again in my case objects.get will never return more than one object.
Hope this post helps someone and saves a lot of their time. :-)
In my tests, I send mock data of models that I've passed through the serializer. The serializer.data looks something like this
{
"field": None
}
However, the data that my API receives is formatted like
{
"field": "None"
}
which is a problem because I'm trying to specify a foreign key that is allowed to be null. Shouldn't the APIClient convert None into null instead of unicode?
Is there any way to fix this or get around it?
Here's my serializer
class MyModelSerializer(serializers.ModelSerializer):
field = serializers.PrimaryKeyRelatedField(
queryset=OtherModel.objects.all(), required=False, allow_null=True)
And my create method in a viewset
def create(self, request):
model = MyModel()
serializer = MyModelSerializer(model, data=request.data)
if serializer.is_valid():
serializer.save(owner=request.user)
return Response(serializer.data, status=201)
return Response(serializer.errors, status=406)
Also my model class
class MyModel(models.Model):
field= models.OneToOneField(
OtherModel, blank=True, null=True)
In addition to what Kevin already said, you can force the APIClient to send JSON using the parameter format='json'.
See the documentation.
The problem here is that the APIClient is sending data to the view as form-data by default, which doesn't have a concept of None or null, so it is converted to the unicode string None.
The good news is that Django REST framework will coerce a blank string to None for relational fields for this very reason. Alternatively, you can use JSON and actually send None or null, which should work without issues.
In addition to existing answers,
if you are expecting a null, this probably means you expect your api to receive json.
If that's the case, you may want to configure the test request default format to json instead of form-data:
In your setting.py:
REST_FRAMEWORK = {
...
'TEST_REQUEST_DEFAULT_FORMAT': 'json'
}
This way, no need to add format='json' to each request