How to pass data saved from a POST method to the GET method using REST API Django (without a model)? - django

I have created an API that allows me to upload an image using the POST method in POSTMAN. After submission, I want to display that image name after making a GET request. I am not using any model and I don't intend to grab the image from the directory it is stored in; since I will be uploading images in a server later.
I have looked at multiple sources. A few examples are this, and this.
This is my current code so far but not successful:
views.py:
class API(APIView):
parser_classes = (MultiPartParser,)
def get(self, request, *args, **kwargs):
name = self.request.GET.get('image')
if name:
return Response({"img_name": name}, status=200)
return Response({"img_name" : None}, status = 400)
def post(self, request):
file = self.request.data
img_file = file['image'] #store the image data in this variable
if img_file:
uploaded_file = img_file
img = [{"image_name": uploaded_file}]
serializer = ImgSerializer(img, many = True).data
return Response(serializer, status = 200)
else:
return Response("Please upload", status = 400)
serializers.py:
from rest_framework import serializers
class ImgSerializer(serializers.Serializer):
image_name = serializers.CharField()
My expected result within GET request should be like this:
{'image_name' : 'image_name_from_POST_Request'}
But I am getting this result instead:
None
How can I pass data from the POST request to the GET request using Django's rest framework? Is there an efficient way to deploy this requirement without using a model?

I figured it out. I just created a JSON file in the POST method and stored the necessary data in it. Finally, in order to view the data within the GET method, I opened the file and returned it as a Response.
views.py:
class API(APIView):
parser_classes = (MultiPartParser,)
def get(self, request):
with open('data.txt') as json_file:
data = json.load(json_file)
if data:
return Response(data, status=200)
return Response({"name" : None}, status = 400)
def post(self, request):
posted_file = self.request.data
img_file = posted_file['image']
if img_file:
uploaded_file = img_file
data = [{"image_name": uploaded_file}]
json_data = {"image_name": uploaded_file}
data = {}
data['key'] = []
data['key'].append(json_data)
with open('data.txt', 'w') as outfile:
json.dump(image, outfile)
serializer = ImgSerializer(image, many = True).data
return Response(serializer, status = 200)
else:
return Response(serializer.errors, status = 400)

Related

how i can test views py django

i just learning how to pytest my code in django, and i need your help. So, i have tested my forms.py already,and now i want to test my views.py. I know that i need to test is it post on page,like by response on by ORM, but i cant understand how to do that, probably with my factories or no?
This is my views.py
class AddPost(CreateView):
model = Posts
form_class = PostsForm
template_name = 'posts/addpost.html'
success_url = '/'
def form_valid(self, form):
instance = form.save(commit=False)
if self.request.user.is_authenticated:
instance.owner = self.request.user
instance.save()
return HttpResponseRedirect(self.get_success_url())
class ShowPost(ListView):
model = Posts
template_name = 'posts/allposts.html'
paginate_by = 2
this is test_forms
#pytest.mark.django_db(True)
class TestPostCreationForm:
def test_form(self):
proto_post = PostsFactory.build()
form_payload = {
'phone_number': proto_post.phone_number,
'title': proto_post.title,
'type': proto_post.type,
'text': proto_post.text,
'price': proto_post.price,
'status': proto_post.status,
'image': proto_post.image,
}
form = PostsForm(form_payload)
assert form.is_valid()
instance = form.save()
assert instance.phone_number == proto_post.phone_number
assert instance.title == proto_post.title
assert instance.price == proto_post.price
and factories
from users.tests.factories import UserFactory
def get_mock_img(name='test.png', ext='png', size=(50, 50), color=(256, 0, 0)):
file_obj = BytesIO()
image = Image.new("RGB", size=size, color=color)
image.save(file_obj, ext)
file_obj.seek(0)
return File(file_obj, name=name)
class PostsFactory(factory.DjangoModelFactory):
owner = SubFactory(UserFactory)
phone_number = factory.Faker("phone_number", locale='uk_UA')
title = factory.fuzzy.FuzzyText(length=50)
text = factory.fuzzy.FuzzyText(length=250)
price = factory.fuzzy.FuzzyDecimal(10.5, 50.5)
status = factory.fuzzy.FuzzyChoice(choices=['active', 'deactivated'])
type = factory.fuzzy.FuzzyChoice(choices=['private', 'business'])
image = get_mock_img()
class Meta:
model = 'posts.Posts'
Use the Django test client. Although I don't use pytest, so I can't be certain that it's completely separable from the Django test runner which I use. I'd be surprised if that was not the case
Typical usage (POST to a simple form, which is expected to succeed and redirect)
data = {
"number_per_gridbox": "10",
"submit": "Submit" }
url_fill_gridbox = reverse( "jobs:qa_break_into_gridboxes",
kwargs={'jobline': self.jobline.pk ,'stockline': self.stockline.pk})
response = self.client.post( url_gridbox, data)
self.assertRedirects( response, url_fill_gridbox)
where you are testing error responses or GET, you can obtain the rendered HTML with
body = response.content.decode()
and you can look at entities in the context data that was used for rendering:
form_errors = response.context['form'].errors
As for setting up some objects for the view to process, you can either just create and save them with code (often in the setUp method), or you can use faker and factory-boy

"The submitted data was not a file. Check the encoding type on the form." validation error, despite uploading correct file (django rest framework)

I'm trying to create endpoint for uploading images, in my api, which i'm building with django rest framework.
When I try to test the endpoint with postman, i'm getting response
"image": [
"The submitted data was not a file. Check the encoding type on the form."
]
with status code 400.
When I try to print variable with image to console I get
[<TemporaryUploadedFile: test.jpg (image/jpeg)>]
I've checked out some tutorials and I think i'm sending the file correctly.
That is my post man configuration
that's the view
class ImageView(APIView):
parser_classes = (MultiPartParser, )
permission_classes = (IsAuthenticated, IsRestaurant)
def post(self, request, *args, **kwargs):
data = {
'image': request.data.pop('image'),
'info': request.user.info.pk
}
file_serializer = RestaurantImageSerializer(data=data)
if file_serializer.is_valid():
file_serializer.save()
return Response(status=status.HTTP_201_CREATED)
else:
return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
the serializer
class RestaurantImageSerializer(serializers.ModelSerializer):
class Meta:
model = RestaurantImage
fields = '__all__'
and model
class RestaurantImage(models.Model):
info = models.ForeignKey(RestaurantInfo, related_name='images', on_delete=models.CASCADE)
image = models.ImageField()
def __str__(self):
return self.image.name
Your Postman configuration could be an issue, try removing all the wrong or unnecessary headers. I think you need only Authorization in your case. This also could help you out: https://stackoverflow.com/a/41435972/4907382

How to POST image to django rest API

I am not too practiced in handling image files, and I am very confused about posting and handling an image using my django REST API. I am using a python script as client just for testing (will be ios app eventually).
Anyone who can help me receive an image file, and save it locally on the server would be greatly appreciated.
Client:
i = Image.open('image.jpeg')
upload = {
'id': 3,
'picture': i
}
r = requests.post(ip, data=upload, stream=True)
print(r.text)
Server:
class Picture_Upload(APIView):
def post(self, request):
f = request.data['picture']
return Response(f.content)
You might want to use MultiPartParser to parse the form data so that you can read it on your APIView, and surely that we have to send data as multipart/form-data. Like so:
upload = {
'picture': open('image.jpeg', 'rb')
}
data = {
'id': 3
}
resp = requests.post('/upload_url/', files=upload, data=data)
and your view:
class Picture_Upload(APIView):
parser_classes = (MultiPartParser, FormParser, )
def post(self, request):
picture = request.data['picture']
id = request.data['id']
# Use your model to handle the uploaded file
return Response(status=200)

Django ModelViewSet PATCH request return model fields updated

class MerchantStampCardViewSet(viewsets.ModelViewSet):
'''
A view set for listing/retrieving/updating/deleting stamp cards for the current
merchant
'''
permission_classes = (IsMerchantAndAuthenticated, )
def get_queryset(self):
if len(MerchantProfile.objects.filter(user=self.request.user)) > 0:
merchant_profile = MerchantProfile.objects.get(user=self.request.user)
if merchant_profile.merchant:
return StampCard.objects.filter(merchant=merchant_profile.merchant)
return None
def get_serializer_class(self):
if self.request.method == 'GET':
return StampCardSerializerWithRewards
else:
return StampCardSerializer
I'm trying to make this code return the fields changed in the response body. The model class has a couple fields like name, city, province, zip code and address and through the front-end the user can only change one at a time, but I want the body of the 200 response to contain the field name changed and the new value just to confirm that a change was successful and nothing went wrong.
So for example if the user changes the name to Billy. The response should be 200 and the body should say {name : 'Billy'}
How do I do this?
You can try like this:
class YourViewSet(...):
def update(self, request, *args, **kwargs):
instance = self.get_object()
current_data = self.get_serializer(instance).data # collect current data
# next few lines of the code is from default implementation
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
instance._prefetched_objects_cache = {}
updated_data = serializer.data # now we get the updated data
response_dict = dict()
for key, value in current_data:
# find the differences
if updated_data.get(key) != value:
response_dict[key] = updated_data.get(key)
return Response(response_dict) # send the difference through response
Here I have put a override on update method. Then I have collected the dictionary data from current object and updated object. Then compared them and sent differences in a dictionary as response. FYI its an untested code.

Retrieving images from GridFS using django-tastypie-mongoengine

I have a project in Django, and I'm using mongoengine to save images into a Mongo database using GridFSStorage.
All ok so far, but the problem is... when trying to retrieve the images via http request, with a REST API made with django-tastypie-mongoengine, I get back a json object like this:
{"file": "<GridFSProxy: 516ed7cf56ba7d01eb09f522>", "id": "516ed7cf56ba7d01eb09f524", "resource_uri": "/api/v1/pic/516ed7cf56ba7d01eb09f524/"}
Does anybody know how could I get the file from GridFS via http request?
Many thanks!
You'll need to write your own view, but you can make it seem like it's part of the API. First, the view:
def api_image(pk):
obj = get_object_or_404(Model, pk=pk)
image_file = obj.file
return Response(image_file.read(),
mime_type='image/png') # or whatever the MIME type is
Then, you can map it in your urls.py:
url('^/api/v1/pic/(?P<pk>\w+)/file/$', api_image)
And to make sure tastypie shows what you want in the output:
def dehydrate_file(self, bundle):
return '/api/v1/pic/%s/file/' % (bundle.obj.id)
Just make sure the fake API view appears ahead of your actual API definitions, and you should be all set!
Paul's hint was very useful. Here i have implemented this completely in tastypie manner for uploading and downloading images.
Here you go..
1. Overriding deseriazer to support 'multipart'.
class MultipartResource(object):
def deserialize(self, request, data, format=None):
if not format:
format = request.META.get('CONTENT_TYPE', 'application/json')
if format == 'application/x-www-form-urlencoded':
return request.POST
if format.startswith('multipart'):
data = request.POST.copy()
data.update(request.FILES)
return data
return super(MultipartResource, self).deserialize(request, data, format)
2. Model class
class Research(Document):
user = ReferenceField(User)
academic_year = StringField(max_length=20)
subject = StringField(max_length=150)
topic = StringField(max_length=50)
pub_date = DateTimeField()
authored = StringField(max_length=20)
research_level = StringField(max_length=20)
paper_presented = BooleanField()
thesis_written = BooleanField()
proof_image = ImageField()
3. Resource class
class ResearchResource(MultipartResource, MongoEngineResource):
class Meta:
queryset = Research.objects.all()
list_allowed_methods = ['get','post']
resource_name = 'research'
authentication = SessionAuthentication()
authorization = Authorization()
def prepend_urls(self):
return [
url(r"^(?P<resource_name>%s)/$" % self._meta.resource_name,
self.wrap_view('dispatch_list'), name="api_dispatch_list"),
#url to download image file.
url(r"^(?P<resource_name>%s)/(?P<pk>\w+)/file/$"% self._meta.resource_name,
self.wrap_view('get_image'), name="api_get_image"),
]
#Preparing image url dynamically
def dehydrate_proof_image(self, bundle):
return '/api/v1/%s/%s/file/' % (self._meta.resource_name,bundle.obj.id)
#view will call based on image url to download image.
def get_image(self, request, **kwargs):
obj = Research.objects.get(id=kwargs['pk'])
image_file = obj.proof_image
return HttpResponse(image_file.read(), content_type="image/jpeg"))
Hope this will be very useful for everyone in future. :)