How to handle a file form request and send by REST API - django

I'm trying to upload files, documents and images (once) using a Django form. The idea is to send it over an internal REST API that processes the file, stores it, and does other things.
The API is working but I can't successfully send the file over the API.
This is my code.
Here I process the form
def postNewFile(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = PostNewFile(request.POST, request.FILES)
# check whether it's valid:
if form.is_valid():
file_uploaded = request.FILES.get('file_uploaded')
file_name = file_uploaded.name
content_type = file_uploaded.content_type
formatType = form.cleaned_data['type']
category = form.cleaned_data['type']
description = form.cleaned_data['type']
response = postFile(file_uploaded, file_name, formatType, category, description, content_type)
if response == 0:
return HttpResponse('<H1>Enviado exitosamente</H1>')
else:
return HttpResponse('<H1>Error interno</H1>')
else:
print('No es valido')
return HttpResponse('<H1>Error en el formulario</H1>')
else:
return HttpResponse('<H1>Error en el formulario</H1>')
Here I make de API request
def postFile(doc, name, docType, category, description, content_type):
try:
url = URL_API+"file/"
payload={}
docs=[
('document',(name+'.'+docType, open(doc,'rb'),content_type))
]
headers = {
'Authorization': 'Bearer as',
'name': name,
'type': docType,
'category': category,
'description': description,
'content-type': content_type
}
response = requests.request("POST", url, headers=headers, data=payload, files=docs)
print(response.text)
return 0
except Exception as e:
print(e)
return -1

Related

How to convert json to form in Django?

I'm developing a Django project, and the team want to seperate the front-end and the back-ed, so I'm using Django to develop api. The format of the data transmitting is json. However, I want to use the defalt user package (django.contrib.auth), and I have to use Form. How could I convert the json received from the frontend to the form that I'm going to use in backend? thanks!
I have tried the silly code as below and it does not work.
#require_http_methods(["POST"])
def register(request):
form = CustomUserCreationForm(data=request.POST)
response = {"status": "success"}
if form.is_valid():
new_user = form.save()
print("valid")
return JsonResponse(response)
This is how your view must look if you want to register new user with custom form :
def register(request):
if request.method == 'POST':
form = CustomUserCreationForm(request.POST)
response = {}
if form.is_valid():
# The form is valid, now we can save the user in the db
new_user = form.save()
response = {"status": "success", "message": "User created"}
return JsonResponse(response)
else:
# Invalid form
response = {"status": "error", "message": "Form is invalid"}
else:
# It's not a post request ( GET or others)
# Instantiate an empty form
form = CustomUserCreationForm()
context = {
'form': form,
}
return render(request, 'register_template.html', context=context)

Send file to a different server

I have an upload url in my backend and i want to upload a file in another server.
My API view:
class AssessmentFileUpload(APIView):
parser_classes = (MultiPartParser, )
def post(self, request, format=None):
tenant = request.user.tenant.id
response = AssessmentFileUploadHelper(tenant).upload_file(request.FILES)
response_text = json.loads(response.text)
print(response_text)
if response.status_code == status.HTTP_201_CREATED:
return Response({"message": "success", "id": response_text.get('id')}, status=status.HTTP_201_CREATED)
return Response({"message": "failed"}, status=status.HTTP_400_BAD_REQUEST)
My class which sends request data to the other serve's url:
class AssessmentFileUploadHelper:
def __init__(self, tenant_id):
self.tenant_id = tenant_id
def upload_file(self, file):
print("FILE IS", file)
url = settings.ASSESSMENT_CONNECTION_SETTINGS["api_endpoint"] + "tenant/" + \
str(self.tenant_id) + "/fileupload/"
return RequestSender().send_request(url,None, file)
class RequestSender:
def __init__(self):
super().__init__()
def __get_authorized_header(self):
usernamepassword = settings.ASSESSMENT_CONNECTION_SETTINGS["userid"] + ":" + settings.ASSESSMENT_CONNECTION_SETTINGS["password"]
userAndPass = b64encode(usernamepassword.encode("utf-8")).decode("ascii")
authorization = "Basic " + userAndPass
headers = {'Authorization': authorization, "Content-Type": "application/json"}
return headers
def send_request(self, url, data, files=None):
json_data = json.dumps(data)
response = requests.post(url,
data=json_data,
headers=self.__get_authorized_header(),
files=files
)
return response
Now, the errors im getting is InMemoryUploadedFile is not json serilizaable . How to send request.FILES to that server ?
You neet to convert 'InMemoryUploadedFile' type to string:
str = request.FILES['file'].read().decode()

TypeError: 'NoneType' object is not subscriptable in Django unittests for message

Am trying to do a unit test for a view that returns a message when some input is duplicated in the database.it gives me the error TypeError: 'NoneType' object is not subscriptable
Here is the view
#login_required
def worker_create(request):
worker_create_form = WorkerCreateForm(request.POST)
if request.method == 'POST':
if worker_create_form.is_valid():
form = worker_create_form.save(commit=False)
phone = form.phone
check_phone = Worker.objects.filter(phone=phone)
if check_phone.count() != 0:
messages.error(request, 'رقم الهاتف مستخدم من قبل')
else:
form.save()
return redirect('worker_list')
else:
worker_create_form = WorkerCreateForm(request.POST)
context = {
'worker_create_form': worker_create_form,
}
return render(request, 'erp_system/worker/create.html', context)
and here are the tests I've created for it
class WorkerCreateTest(TestCase):
def setUp(self):
User.objects.create_user(username='test_user', email='test#gmail.com', password='test_password')
branch = Branch.objects.create(name='test branch')
Worker.objects.create(name="ay btngan ", phone='01207199086', branch=branch)
def test_get_request_unauthenticated(self):
response = self.client.get(reverse('worker_create'))
url = reverse('worker_create')
self.assertRedirects(response, '/login/?next=' + url)
def test_get_request_authenticated(self):
self.client.login(username='test_user', password='test_password')
response = self.client.get(reverse('worker_create'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed('erp_system/worker/create.html')
def test_post_request_empty_data(self):
self.client.login(username='test_user', password='test_password')
response = self.client.post(reverse('worker_create'), {})
self.assertFormError(response, 'worker_create_form', 'name', 'This field is required.')
self.assertFormError(response, 'worker_create_form', 'phone', 'This field is required.')
self.assertFormError(response, 'worker_create_form', 'branch', 'This field is required.')
def test_post_request_invalid_data(self):
self.client.login(username='test_user', password='test_password')
branch = Branch.objects.create(name='test again ')
name = 'just a name'
for i in range(300):
name += 'h'
response = self.client.post(reverse('worker_create'),
{'name': name, 'phone': '01207199086', 'branch': branch.id})
self.assertEqual(response.status_code, 200)
def test_post_request_duplicated_phone(self):
self.client.login(username='test_user', password='test_password')
branch = Branch.objects.create(name='test again ')
response = self.client.post(reverse('worker_create'),
{'name': 'test worker', 'phone': '01207199086', 'branch': branch.id})
print(response)
messages = list(response.context['messages'])
self.assertEqual(len(messages), 1)
self.assertEqual(str(messages[0]), 'رقم الهاتف مستخدم من قبل')
def test_post_request_valid_data(self):
self.client.login(username='test_user', password='test_password')
branch = Branch.objects.create(name='test branch1234')
name = 'new valid name'
response = self.client.post(reverse('worker_create'),
{'name': name, 'branch': branch.id, 'phone': '0151951115'})
self.assertEqual(response.status_code, 302)
Important
I noticed when I added print(response) that it gives me HttpResponseRedirect not just HttpResponse what means there is no context given. Why it using redirect in here!?
In your test test_post_request_duplicated_phone, you make a POST request to the worker_create, and you expect to retrieve an error, because there exists already a record for the given phone.
The documentation on tests [Django-doc] however mentions that:
A TestCase, on the other hand, does not truncate tables after a test. Instead, it encloses the test code in a database transaction that is rolled back at the end of the test. This guarantees that the rollback at the end of the test restores the database to its initial state.
So that means that, unless you implement some "tricks" to prevent this, the side-effects that one test has (on the database) will be gone when you enter a second test. This makes sense, since reordering the tests should not result in a different outcome.
You can however create such Worker object in your test in advance, and thus make sure that the test will indeed error:
def test_post_request_duplicated_phone(self):
self.client.login(username='test_user', password='test_password')
branch = Branch.objects.create(name='test again ')
Worker.objects.create(name=" just a name ", phone='01207199086', branch=branch)
response = self.client.post(reverse('worker_create'),
{'name': 'test worker', 'phone': '01207199086', 'branch': branch.id})
print(response)
messages = list(response.context['messages'])
self.assertEqual(len(messages), 1)
self.assertEqual(str(messages[0]), 'رقم الهاتف مستخدم من قبل')

django test client.put doesn't have data

I am trying to test django view with REST framework.
It starts like this.
class MoveViewSet(viewsets.ModelViewSet):
serializer_class = FamilySerializer
queryset = Family.objects.all()
http_method_names = ['put']
def update(self, request, pk=None):
user = request.mobile_user
...
family_id = request.POST.get('family_id', None)
...
in test.py I make a request like this.
data = dict(
join_type='join',
family_id=target_family.id,
)
# also tried data = { ... }
header = {
'Authorization': user.api_key,
...
}
client = Client()
response = client.put(url, data=data, content_type='x-www-form-urlencoded', **header)
### What I've tried ###
# response = client.put(url, data=data, **header)
# response = self.client.put(url, data=data, **header)
# response = client.put(url, data=data, content_type='application/json', **header)
but in view, request.POST.get('paramname', 'default') makes error. request.POST has empty parameter set and of course, request.PUT is None.
I checked middleware but neither could I find params there.
Also I've tried this in middleware's process_request.
def process_request(self, request):
if request.method == "PUT" and request.content_type != "application/json":
if hasattr(request, '_post'):
del request._post
del request._files
try:
request.method = "POST"
request._load_post_and_files()
request.method = "PUT"
except AttributeError as e:
request.META['REQUEST_METHOD'] = 'POST'
request._load_post_and_files()
request.META['REQUEST_METHOD'] = 'PUT'
request.PUT = request.POST
It gives me this error.
AttributeError: 'WSGIRequest' object has no attribute 'content_type'
If I send client.post() with data, request.POST has data, but put doesn't.
How could I test client.put() with parameters?
request's content_type attribute has been added in Django 1.10. Since you are using Django 1.9 you cannot use this attribute. You can chec request.META.get('HTTP_ACCEPT') solution as mentioned here or update Django.

Vimeo 'Replace' API Endpoint Not Changing Thumbnail

I am using Vimeo's API for the users of my app to upload videos, or replace their existing video with a new one. I am using a Vimeo Client to help me make the calls in my Django Application. Uploading works without any issues, but when I try to replace an existing video with a new one, the thumbnail stays as the old video. If you play the video, it will play the new one, but the thumbnail never changes.
Model Method that Uploads/Replaces
def vimeo_upload(self):
media_file = self.video_file
if media_file and os.path.exists(media_file.path):
v = vimeo.VimeoClient(token=settings.VIMEO_ACCESS_TOKEN, key=settings.VIMEO_API_KEY,
secret=settings.VIMEO_API_SECRET)
if self.video_url is None:
try:
video_uri = v.upload(media_file.path)
except AssertionError as exc:
logging.error('Vimeo Error: %s Video: %s' % (exc, media_file.path))
else:
self.video_url = video_uri
else:
try:
v.replace(video_uri=self.video_url, filename=media_file.path)
except Exception as exc:
self.video_url = None
logging.error('Vimeo Replace Error: %s Video: %s' % (exc, media_file.path))
# set the video title, description, etc.
if self.video_url:
try:
# convert locale from en-us form to en
v.patch(self.video_url, data={'description': self.customer.full_name, })
except Exception as exc:
logging.error('Vimeo Patch Error: %s Video: %s' % (exc, media_file.path))
Vimeo Client Model, and UploadVideoMixin
class UploadVideoMixin(object):
"""Handle uploading a new video to the Vimeo API."""
UPLOAD_ENDPOINT = '/me/videos'
REPLACE_ENDPOINT = '{video_uri}/files'
def upload(self, filename, upgrade_to_1080=False):
"""Upload the named file to Vimeo."""
ticket = self.post(
self.UPLOAD_ENDPOINT,
data={'type': 'streaming',
'upgrade_to_1080': 'true' if upgrade_to_1080 else 'false'},
params={'fields': 'upload_link,complete_uri'})
return self._perform_upload(filename, ticket)
def replace(self, video_uri, filename, upgrade_to_1080=False):
"""Replace the video at the given uri with the named source file."""
uri = self.REPLACE_ENDPOINT.format(video_uri=video_uri)
ticket = self.put(
uri,
data={'type': 'streaming',
'upgrade_to_1080': 'true' if upgrade_to_1080 else 'false'},
params={'fields': 'upload_link,complete_uri'})
return self._perform_upload(filename, ticket)
def _perform_upload(self, filename, ticket):
"""Take an upload ticket and perform the actual upload."""
if ticket.status_code != 201:
raise UploadTicketCreationFailure(ticket, "Failed to create an upload ticket")
ticket = ticket.json()
# Perform the actual upload.
target = ticket['upload_link']
last_byte = 0
# Try to get size of obj by path. If provided obj is not a file path
# find the size of file-like object.
try:
size = os.path.getsize(filename)
with io.open(filename, 'rb') as f:
while last_byte < size:
try:
self._make_pass(target, f, size, last_byte)
except requests.exceptions.Timeout:
# If there is a timeout here, we are okay with it, since
# we'll check and resume.
pass
last_byte = self._get_progress(target, size)
except TypeError:
size = len(filename.read())
f = filename
while last_byte < size:
try:
self._make_pass(target, f, size, last_byte)
except requests.exceptions.Timeout:
# If there is a timeout here, we are okay with it, since
# we'll check and resume.
pass
last_byte = self._get_progress(target, size)
# Perform the finalization and get the location.
finalized_resp = self.delete(ticket['complete_uri'])
if finalized_resp.status_code != 201:
raise VideoCreationFailure(finalized_resp, "Failed to create the video")
return finalized_resp.headers.get('Location', None)
def _get_progress(self, upload_target, filesize):
"""Test the completeness of the upload."""
progress_response = self.put(
upload_target,
headers={'Content-Range': 'bytes */*'})
range_recv = progress_response.headers.get('Range', None)
_, last_byte = range_recv.split('-')
return int(last_byte)
def _make_pass(self, upload_target, f, size, last_byte):
"""Make a pass at uploading.
This particular function may do many things. If this is a large upload
it may terminate without having completed the upload. This can also
occur if there are network issues or any other interruptions. These
can be recovered from by checking with the server to see how much it
has and resuming the connection.
"""
response = self.put(
upload_target,
timeout=None,
headers={
'Content-Length': str(size),
'Content-Range': 'bytes: %d-%d/%d' % (last_byte, size, size)
}, data=f)
if response.status_code != 200:
raise VideoUploadFailure(response, "Unexpected status code on upload")
class VimeoClient(ClientCredentialsMixin, AuthorizationCodeMixin, UploadMixin):
"""Client handle for the Vimeo API."""
API_ROOT = "https://api.vimeo.com"
HTTP_METHODS = set(('head', 'get', 'post', 'put', 'patch', 'options', 'delete'))
ACCEPT_HEADER = "application/vnd.vimeo.*;version=3.2"
USER_AGENT = "pyvimeo 0.3.10; (http://developer.vimeo.com/api/docs)"
def __init__(self, token=None, key=None, secret=None, *args, **kwargs):
"""Prep the handle with the authentication information."""
self.token = token
self.app_info = (key, secret)
self._requests_methods = dict()
# Make sure we have enough info to be useful.
assert token is not None or (key is not None and secret is not None)
# Internally we back this with an auth mechanism for Requests.
#property
def token(self):
return self._token.token
#token.setter
def token(self, value):
self._token = _BearerToken(value) if value else None
def __getattr__(self, name):
"""This is where we get the function for the verb that was just
requested.
From here we can apply the authentication information we have.
"""
if name not in self.HTTP_METHODS:
raise AttributeError("%r is not an HTTP method" % name)
# Get the Requests based function to use to preserve their defaults.
request_func = getattr(requests, name, None)
if request_func is None:
raise AttributeError(
"%r could not be found in the backing lib" % name
)
#wraps(request_func)
def caller(url, jsonify=True, **kwargs):
"""Hand off the call to Requests."""
headers = kwargs.get('headers', dict())
headers['Accept'] = self.ACCEPT_HEADER
headers['User-Agent'] = self.USER_AGENT
if jsonify \
and 'data' in kwargs \
and isinstance(kwargs['data'], (dict, list)):
kwargs['data'] = json.dumps(kwargs['data'])
headers['Content-Type'] = 'application/json'
kwargs['timeout'] = kwargs.get('timeout', (1, 30))
kwargs['auth'] = kwargs.get('auth', self._token)
kwargs['headers'] = headers
if not url[:4] == "http":
url = self.API_ROOT + url
response = request_func(url, **kwargs)
if response.status_code == 429:
raise APIRateLimitExceededFailure(
response, 'Too many API requests'
)
return response
return caller
class _BearerToken(requests.auth.AuthBase):
"""Model the bearer token and apply it to the request."""
def __init__(self, token):
self.token = token
def __call__(self, request):
request.headers['Authorization'] = 'Bearer ' + self.token
return request