Django: call REST API through a view instead of Postman - django

I have a working python code on my desktop that prints and makes PDFs perfectly. All I want to do is use that code and use Django to allow users to enter a value.
My code uses docusign API to call data. I use postman which needs a key and other parameters to use the API. The value entered by my user will determine what data they get.
What I think I have to do is rewrite my code, put it somewhere, then turn it into a view. The view will be sent to template.
Edit -
My code:
# Get Envelope Data- use account ID from above
# Get Todays Date, Daily Setting
day = datetime.datetime.today().strftime('%Y-%m-%d')
url = "https://demo.docusign.net/restapi/v2/accounts/" + accountId + "/envelopes"
# if Envelope is completed
querystring = {"from_date": Date, "status": "completed"}
headers = {
'X-DocuSign-Authentication': "{\"Username\":\""+ email +"\",\"Password\":\""+Password+"\",\"IntegratorKey\": \""+IntegratorKey+"\"}",
'Content-Type': "application/json",
'Cache-Control': "no-cache",
'Postman-Token': "e53ceaba-512d-467b-9f95-1b89f6f65211"
}
response = requests.request("GET", url, headers=headers, params=querystring)
envelopes = response.text
Sorry, let me try again. I currently have a python3 program on my desktop. I run it with idle and everything is how I want it.
What I want to do with Django is use this code to print its outputs on a webpage and have the user download it’s additional csv file output. I have managed to make a Django localhost and I am stuck at that point. I do not know how to use my python3 code to run to webpage.
The code is made up of API calls, I use postman to help me with sending the right parameters. I will add a picture of code. All I want is for user to enter value such as accountID so that the API can complete the request and give them data for their own request.

I'll try to give you a overview of how this could work with Django.
You could have a form to obtain the users account_id.
class AccountForm(forms.Form):
account_id = forms.IntegerField()
You could display this form through a generic FormView (see also this):
class AccountView(views.FormView):
form_class = AccountForm
template_name = 'account.html'
def form_valid(self, form):
# here you make your request to the external API
account_id = form.cleaned_data['account_id']
url = "https://demo.docusign.net/restapi/v2/accounts/" + account_id + "/envelopes"
headers = ...
querystring = ...
resp = requests.request("GET", url, headers=headers, params=querystring)
ctx = {
'result': resp.text,
}
return render(self.request, 'result.html', ctx)
I don't show the template account.html here. You will have to figure that one out yourself; the links I provided should point you in the right direction.
Now, what remains to be determined is what exactly the method form_valid should return. The code I showed renders a template with the API call response in the context, so in your template result.html you could display the result data any way you like.
You mentioned downloading a CSV file as well. That could be a different view, probably triggered by a link or button in result.html.

Related

Can I return a different Django Response based on whether or not the view is invoked from swagger?

I am using Django-Rest-Framework for my API. And I am documenting that API w/ swagger using drf_yasg. One of my views returns a Django FileResponse.
When I access the view directly at "http://localhost:8000/api/my_documents/1" it successfully displays the (PDF) file. But when I access it via swagger it successfully returns a 200 but gives the following message:
Unrecognized response type; displaying content as text.
This is b/c of this issue in swagger itself. As suggested in that ticket, the problem goes away if I change the "Content-Disposition" response header from "inline" to "attachment". However, I don't want to always download the file.
My question is: Can I determine whether the request was made by swagger in the view and conditionally change the headers? Something like:
class MyDocumentView(GenericAPIVIew):
def get(self, request, pk):
my_document = MyDocument.objects.get(pk=pk)
response = FileResponse(my_document.file) # (file is a FileField)
# WHAT DO I PUT HERE ?!?
if request.is_from_swagger:
response.headers["Content-Disposition"] = response.headers["Content-Disposition"].replace("inline", "attachment")
return response
Thanks.
You can do this by checking the request.headers.get('referer'). There should be your swagger url in there.

Retrieving users password from Django and passing as variable to log in to Django REST

I have a Django powered web-app which also utilizes the Django REST framework. I want to read and write to my database using the API generated by Django REST.
I can do this successfully when I hardcode the password of the current user, but I can't do this when I attempt to pass in the retrieved password from Django, as Django does not store the password as plain text. (I know I can change this, but I am hoping there is a better way).
Essentially, my logic flow is this:
user logs in using Django login form
user would like to write new data to database, but will need API token
Obtain API token (already generated when they sign up)
use this token to authenticate and POST JSON data to REST framework
I can do all of these steps above when I hardcode the password as plain text in my script, but I would like to "retrieve" the password and pass it automatically if possible.
Code below works as described above:
views.py
class DefaultsListView(LoginRequiredMixin,ListView):
model = models.DefaultDMLSProcessParams
template_name = 'defaults_list.html'
login_url = 'login'
def get_queryset(self):
return models.DefaultDMLSProcessParams.objects.filter
(customerTag=self.request.user.customerTag)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
testing = super().get_context_data(**kwargs)
userName = str(self.request.user.username)
passWord = str(self.request.user.password)
url = 'http://127.0.0.1:8000/api-token-auth/'
context = {'tokenURL': url, 'user':userName, 'pass':passWord}
return context
template.html
<div class = "debugging">
<p id = "csrf">{% csrf_token %}</p>
<p id = "tokenURL">{{tokenURL}}</p>
<p id = "user">{{user}}</p>
<p id = "pass">{{pass}}</p>
</div>
script.js
var csrfToken = document.querySelector("#csrf input").value;
var user = document.getElementById("user").innerHTML;
var pass = document.getElementById("pass").innerHTML;
var xhr = new XMLHttpRequest();
var url = "http://127.0.0.1:8000/api-token-auth/";
xhr.open("POST", url,);
xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
xhr.setRequestHeader("Accept", "application/json");
xhr.setRequestHeader( "X-CSRFToken", csrfToken);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
var json = JSON.parse(xhr.responseText);
console.log(json.token);
}
};
var data = JSON.stringify({"username": user, "password": 'myhardcodedpass' });
xhr.send(data);
This script will log the API token of the current user to the console as expected. But instead of hardcoding the password, I would like to just pass it as a variable, but the password is a very long string as it is not stored as plain text (var pass). So trying to use pass is rejected, as it obviously isn't the correct password when passed as text.
Is there a way to decrypt this password, or translate it somehow? Or perhaps an easier way to retrieve the API token from Django REST?
First and foremost, you should definitely NOT store the password as plain text. That is never the solution in any case, and only will cause problems.
I have a few suggestions that you can potentially use, but the easiest would be to simply use a JSON Web Token which can easily be integrated with Django Rest Framework using the django-rest-framework-jwt library.
Your workflow would basically be as follows:
User logs in successfully, which returns a JWT to authenticate the user
The script would be included in the header of the request, and the middleware would be able to successfully identify and authenticate the user.
Hopefully this gives you an idea on how to move forward. Best of luck

How to save the latest url requests in django?

I'd like to add a 'Last seen' url list to a project, so that last 5 articles requested by users can be displayed in the list to all users.
I've read the middleware docs but could not figure out how to use it in my case.
What I need is a simple working example of a middleware that captures the requests so that they can be saved and reused.
Hmm, don't know if I would do it with middleware, or right a decorator. But as your question is about Middleware, here my example:
class ViewLoggerMiddleware(object):
def process_response(self, request, response):
# We only want to save successful responses
if response.status_code not in [200, 302]:
return response
ViewLogger.objects.create(user_id=request.user.id,
view_url=request.get_full_path(), timestamp=timezone.now())
Showing Top 5 would be something like;
ViewLogger.objects.filter(user_id=request.user.id).order_by("-timestamp")[:5]
Note: Code is not tested, I'm not sure if status_code is a real attribute of response. Also, you could change your list of valid status codes.

Use Django views for handling blobstore uploads

In stead of using the BlobstoreUploadHandler supplied in AppEngine, I'd prefer to use a Django view, so I can keep all the urls and view functions together. However, I can't find out how to get the blob-key of the uploaded file! (like get_uploads() does for the upload handler). I saw that the BlobstoreUploadHandler uses request.params, but I don't think that is available from Django's Request.
def upload_form(request):
upload_url = blobstore.create_upload_url(reverse(upload_blob))
output = '<html><body>'
output += '<form action="%s" method="POST" enctype="multipart/form-data">' % upload_url)
output += ('''Upload File: <input type="file" name="file"><br> <input type="submit"
name="submit" value="Submit"> </form></body></html>''')
def upload_blob(request):
print request
# How to get the 'blob-key' from request?!
When I examine the request object, all I get is
<WSGIRequest
GET:<QueryDict: {}>,
POST:<QueryDict: {u'submit': [u'Submit']}>
# And COOKIES, META, etcetera
EDIT: Request.FILES
I discovered that some info can be extracted using Request.FILES, which gives:
<MultiValueDict: {u'file': [<InMemoryUploadedFile: my_file (message/external-body)>]}>
However, I assume that the blobstore still handles the file content (is that why it says "content_type=message/external-body"?), so I still need the key somehow. Calling read() gives:
Content-Type: application/octet-stream
MIME-Version: 1.0
Content-Length: 17
Content-MD5: ZmQ3OTJhNjMzNGE0OTAzNGU4NjE5MDNmMGEwNjliMGE=
content-type: application/octet-stream
content-disposition: form-data; name="file"; filename="a1_blob"
X-AppEngine-Upload-Creation: 2012-02-12 22:11:49.643751
So it looks like AppEngine actually replaced the file content by this descriptor, but still, where does AppEngine put the key?
I'm starting to suspect that the blob-key is just lost when not using the webapp framework, since the UploadedFile object has no key() method.
It took me a long time to find, but the content_type: message/external-body requires extra parameters, to find the actual file, in AppEngine's case, this is the blob-key. However, Django doesn't support these extra content_type parameters, so they are indeed lost in the process. There seems to be a patch, but I don't think it's in the AppEngine Django version yet.
https://code.djangoproject.com/ticket/13721
I had the same problem yesterday. Thanks to your post I realizad that the problem was django and his class views. I finally use a code that I have since 2011 and it still works. It does not use BlobstoreUploadHandler, but it gets the blob_infos from the request after automatically upload it to blobstore.
You can use that function in the next way from your callback django function or class (I finally did not try it in a class view from Django but I think it will work. Currently I'm using it in a function view from Django with its request):
media_blobs = get_uploads(request, populate_post=True)
The function is the next:
import cgi
from google.appengine.ext import blobstore
def get_uploads(request, field_name=None, populate_post=False):
"""Get uploads sent to this handler.
Args:
field_name: Only select uploads that were sent as a specific field.
populate_post: Add the non blob fields to request.POST
Returns:
A list of BlobInfo records corresponding to each upload.
Empty list if there are no blob-info records for field_name.
"""
if hasattr(request,'__uploads') == False:
request.META['wsgi.input'].seek(0)
ja = request.META['wsgi.input']
fields = cgi.FieldStorage(request.META['wsgi.input'], environ=request.META)
request.__uploads = {}
if populate_post:
request.POST = {}
for key in fields.keys():
field = fields[key]
if isinstance(field, cgi.FieldStorage) and 'blob-key' in field.type_options:
request.__uploads.setdefault(key, []).append(blobstore.parse_blob_info(field))
elif populate_post:
if isinstance(field, list):
request.POST[key] = [val.value for val in field]
else:
request.POST[key] = field.value
if field_name:
try:
return list(request.__uploads[field_name])
except KeyError:
return []
else:
results = []
for uploads in request.__uploads.itervalues():
results += uploads
return results
The last function is not mine. I do not remember where I got it three or four years ago. But I think it will help someone.
UPDATE:
You also can use a view handler of webapp.WSGIApplication and at the same time use django. This way will allow you to use BlobstoreUploadHandler and BlobstoreDownloadHandler (for video stream as example). You only need to add the view class in main.py and create its handler:
class ServeVideoHandler(blobstore_handlers.BlobstoreDownloadHandler):
def get(self, resource):
...
downloader_handler = webapp.WSGIApplication([('/pathA/pathB/([A-Za-z0-9\-\=_]+)', ServeVideoHandler),], debug=True)
And in app.yaml add the handler before the script main.application that contains your django app.
- url: /pathA/pathB/(.+)
script: main.downloader_handler
The key info isn't directly in the file, it's in file.blobstore_info.key()
post your form containing your image to a url created using blobstore.create_upload_url():
from google.appengine.ext import blobstore
upload_url = blobstore.create_upload_url('/add_image/')
the created url will save the image in the blobstore and redirect the request (with modified file object) to /add_image/
define a url pattern and view for /add_image/ and handle the image:
def add_action_image(request):
image = request.data.get('image')
image_key = image.blobstore_info.key()
... addl' logic to save a record with the image_key...
As you noted, BlobstoreUploadHandler is open source, so you can see the logic they use to parse the key out of the request params. Note that request.params just includes variables from both the query string and the request body (for POST requests). So you might want to start with your djnago request's REQUEST object.

How to receive POST data in django

I've been trying to integrate a payment gateway into my site in django.
I'm having trouble getting the response data from the payment gateway.
The payment gateway has sample docs for php which looks like this :
$ErrorTx = isset($_POST['Error']) ? $_POST['Error'] : ''; //Error Number
$ErrorResult = isset($_POST['ErrorText']) ? $_POST['ErrorText'] : ''; //Error message
$payID = isset($_POST['paymentid']) ? $_POST['paymentid'] : ''; //Payment Id
In the view for the url that the payment gateway is redirecting to after entering card details etc, I'm checking if it's a GET if request.method == "GET" and then passing the request to a function. When I debug the request, I can see an empty query dict. and if I try something like res = request.GET['paymentid'] I get an error that says there's no key called paymentid.
Am I missing something obvious? I'm still pretty new to django, so I'm sure I'm doing something wrong.
res = request.GET['paymentid'] will raise a KeyError if paymentid is not in the GET data.
Your sample php code checks to see if paymentid is in the POST data, and sets $payID to '' otherwise:
$payID = isset($_POST['paymentid']) ? $_POST['paymentid'] : ''
The equivalent in python is to use the get() method with a default argument:
payment_id = request.POST.get('payment_id', '')
while debugging, this is what I see in the response.GET: <QueryDict: {}>, request.POST: <QueryDict: {}>
It looks as if the problem is not accessing the POST data, but that there is no POST data. How are you are debugging? Are you using your browser, or is it the payment gateway accessing your page? It would be helpful if you shared your view.
Once you are managing to submit some post data to your page, it shouldn't be too tricky to convert the sample php to python.
You should have access to the POST dictionary on the request object.
for class based views, try this:
class YourApiView(generics.ListAPIView):
"""
API endpoint
"""
def post(self, request, *args, **kwargs):
print("request data")
print(request.data)