Django Forms to an API payload? - django

I am using a Django form to generate my HTML form.
I wanted to use the data from my form as an API payload.
if request.method == 'POST':
form = GetKeyForm(request.POST)
if form.is_valid():
appkey = form.cleaned_data['appkey']
secretkey = form.cleaned_data['secretkey']
shop_token = form.cleaned_data['shop_token']
shop_domain = form.cleaned_data['shop_domain']
payload = {
"account_platform": {
"shop_token": {shop_token},
"shop_secret": "",
"shop_domain": {shop_domain},
},
"utoken": {utoken}
}
headers = {
"Accept": "application/json",
"Content-Type": "application/json"
}
This is the error that I am getting:
Object of type set is not JSON serializable

Related

Delete all documents in a MongoDB collection via Django backend and Angular frontend

I have managed to write code to add a customer to my MongoDB collection from my Angular service method to my Django http function, as follows:
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Accept': 'application/json'
}),
withCredentials: false
}
#Injectable()
export class MongoService {
myApiBaseUrl = "http://localhost:8000/mydjangobaselink/";
constructor(private httpClient: HttpClient) { }
addCustomer(customerFormInfo: Customer): Observable<Customer> {
return this.httpClient.post<Customer>(`${this.myApiBaseUrl}`, JSON.stringify(customerData), httpOptions);
}
deleteCustomer(): Observable<Customer> {
return this.httpClient.delete<Customer>(`${this.myApiBaseUrl}`);
}
}
#csrf_exempt
#api_view(['GET', 'POST', 'DELETE'])
def handle_customer(request):
if request.method == 'POST':
try:
customer_data = JSONParser().parse(request)
customer_serializer = CustomerModelSerializer(data=customer_data)
if customer_serializer.is_valid():
customer_serializer.save()
# Write customer data to MongoDB.
collection_name.insert_one(customer_serializer.data)
response = {
'message': "Successfully uploaded a customer with id = %d" % customer_serializer.data.get('id'),
'customers': [customer_serializer.data],
'error': ""
}
return JsonResponse(response, status=status.HTTP_201_CREATED)
else:
error = {
'message': "Can not upload successfully!",
'customers': "[]",
'error': customer_serializer.errors
}
return JsonResponse(error, status=status.HTTP_400_BAD_REQUEST)
except:
exceptionError = {
'message': "Can not upload successfully!",
'customers': "[]",
'error': "Having an exception!"
}
return JsonResponse(exceptionError, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
elif request.method == 'DELETE':
try:
CustomerModel.objects.all().delete()
# Delete customer data from MongoDB.
collection_name.deleteMany({})
return HttpResponse(status=status.HTTP_204_NO_CONTENT)
except:
exceptionError = {
'message': "Can not delete successfully!",
'customers': "[]",
'error': "Having an exception!"
}
return JsonResponse(exceptionError, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
The POST method works fine and I can see the added document in my MongoDB Compass, but when I try to delete, I get:
DELETE http://localhost:8000/mydjangobaselink/ 500 (Internal Server Error)
All the posts and articles I have seen address communication issues in the browser, local host, etc... but given that my posting method works fine, I don't think that is my issue. Also, in Postman, I get Can not delete successfully!
Can anyone see what might be wrong that I cannot delete from database?
Try with collection_name.delete_many({})
https://pymongo.readthedocs.io/en/stable/api/pymongo/collection.html#pymongo.collection.Collection.delete_many
Edit: as pointed by #NKSM, there is the documentation copied:
delete_many(filter, collation=None, hint=None, session=None)
Delete one or more documents matching the filter:
>>> db.test.count_documents({'x': 1})
3
>>> result = db.test.delete_many({'x': 1})
>>> result.deleted_count
3
>>> db.test.count_documents({'x': 1})
0

Django ajax return a html page

I am trying to decrypt encrypted text and return the plain text through ajax call, instead of getting the message response I get an HTML page back as a response. I have tried returning the response as JSON but still getting the same HTML response.
function loadMessage() {
fetch("{% url 'chat:history' chatgroup.id %}")
.then( response => response.json() )
.then( data => {
for (let msg of data) {
var message=msg.message;
$.ajax({
type: 'GET',
url: '',
data: { message: message},
success: function(response){
broadcastMessage(response.message, msg.username, msg.date_created)
}
})
}
})
}
views.py
def get(request):
message = request.GET.get('message')
key = b'\xa8|Bc\xf8\xba\xac\xca\xdc/5U0\xe3\xd6f'
cipher = AES.new(key, AES.MODE_CTR)
nounce = b64encode(cipher.nonce).decode('utf-8')
if request.is_ajax():
nounce_ = self.nounce
msg_ = self.message
key = self.key
nounce = b64decode(nounce_)
ct = b64decode(msg_)
cipher = AES.new(key, AES.MODE_CTR, nounce=nounce)
msg_ = cipher.decrypt(ct)
mwssage = msg_.decode()
return JsonResponse({'message': message})
return render(request, 'chat/room.html')
The path to the decryption view was empty, after adding a path in the url it worked.

Getting 403 Forbidden when trying to upload file to AWS S3 with presigned post using Boto3 (Django + Javascript)

I've tried researching other threads here on SO and other forums, but still can't overcome this issue. I'm generating a presigned post to S3 and trying to upload a file to it using these headers, but getting a 403: Forbidden.
Permissions
The IAM user loaded in with Boto3 has permissions to list, read and write to S3.
CORS
CORS from all origins and all headers are allowed
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"GET",
"HEAD",
"POST",
"PUT"
],
"AllowedOrigins": [
"*"
],
"ExposeHeaders": []
}
]
The code
The code is based on Python in Django as well as Javascript. This is the logic:
First the file is retrieved from the HTML, and used to call a function for retrieving the signed URL.
(function () {
document.getElementById("file-input").onchange = function () {
let files = document.getElementById("file-input").files;
let file = files[0];
Object.defineProperty(file, "name", {
writeable: true,
value: `${uuidv4()}.pdf`
})
if (!file) {
return alert("No file selected");
}
getSignedRequest(file);
}
})();
Then a GET request is sent to retrieve the signed URL, using a Django view (described in the next section after this one)
function getSignedRequest(file) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/sign_s3?file_name=" + file.name + "&file_type=" + file.type)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
let response = JSON.parse(xhr.responseText)
uploadFile(file, response.data, response.url)
}
else {
alert("Could not get signed URL")
}
}
};
xhr.send()
}
The Django view generating the signed URL
def Sign_s3(request):
S3_BUCKET = os.environ.get("BUCKET_NAME")
if (request.method == "GET"):
file_name = request.GET.get('file_name')
file_type = request.GET.get('file_type')
s3 = boto3.client('s3', config = boto3.session.Config(signature_version = 's3v4'))
presigned_post = s3.generate_presigned_post(
Bucket = S3_BUCKET,
Key = file_name,
Fields = {"acl": "public-read", "Content-Type": file_type},
Conditions = [
{"acl": "public-read"},
{"Content-Type": file_type}
],
ExpiresIn = 3600
)
return JsonResponse({
"data": presigned_post,
"url": "https://%s.s3.amazonaws.com/%s" % (S3_BUCKET, file_name)
})
Finally the file should be uploaded to the bucket (this is where I'm getting the 403 error)
function uploadFile(file, s3Data, url) {
let xhr = new XMLHttpRequest();
xhr.open("POST", s3Data.url)
let postData = new FormData()
for (key in s3Data.fields) {
postData.append(key, s3Data.fields[key])
}
postData.append("file", file)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200 || xhr.status === 204) {
document.getElementById("cv-url").value = url
}
else {
alert("Could not upload file")
}
}
};
xhr.send(postData)
}
The network request
This is how the network request looks in the browser
#jellycsc helped me. I had to open up the BlockPublicAcl option for the bucket for it to work.
The URL that you should be using in the upload is supposed to be the one that the presigned response has. Don't just upload whatever url you want.
Update your response to be:
return JsonResponse({
"data": presigned_post,
"url": presigned_post.url
})
Specifically the url you are using looks like:
https://BUCKTET_NAME.s3.amazonaws.com/KEY_PATH
When it should look like:
https://s3.REGION.amazonaws.com/BUCKET_NAME
However looking at your code this is what it should be doing, but your screen shot from inspector says otherwise. Why does the url in the network request NOT match the url that was returned by the create_presigned_post request?

Unable to get request.FILES django

I am trying to upload a text file to my django backend, but my request.FILES is always empty.
I am using axios to send the file and have followed the django requirement to have 'multipart/form-data' as content type of the request.
What am I missing?
On my app.js I send a post request via:
new Vue({
el: '#app',
data: {
reqtype: '',
uploadedFile: '',
},
methods: {
onSubmit(event) {
this.submitLoading = true;
if (! this.validateForm(this)) {
event.preventDefault();
this.submitLoading = false;
return;
}
var formData = new FormData();
formData.append("reqtype", this.reqtype)
formData.append('fileToUpload', this.uploadedFile)
axios.post('/sreqtool/tc/', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
},
onFileChange(e) {
var files = e.target.files || e.dataTransfer.files;
if (!files.length)
return;
var reader = new FileReader();
var vm = this;
reader.onload = (e) => {
vm.uploadedFile = e.target.result;
};
reader.readAsDataURL(files[0]);
}
},
}
On the network request payload:
------WebKitFormBoundarymAnl54hGVTifZzwM Content-Disposition: form-data; name="reqtype"
filebased
------WebKitFormBoundarymAnl54hGVTifZzwM Content-Disposition: form-data; name="fileToUpload"
data:text/plain;base64,OTA1NTIzMzg2NQ0KOTE3NTAwMTU0Mg0KOTc3NDczNjcyNg0KMTIzNTQ2ODQ1Ng==
------WebKitFormBoundarymAnl54hGVTifZzwM--
In my views.py I have:
#csrf_exempt
def index(request):
if request.method == 'POST':
DLOG.info(request.POST)
DLOG.info(request.FILES)
form = ExtractForm(request.POST, request.FILES)
if form.is_valid():
res = QueryManager.processRequest(request.user, form.cleaned_data)
DLOG is my logger and the output of the dlog is:
[2017-12-18 16:51:06,510] INFO views index: <QueryDict: {u'fileToUpload': [u'data:text/plain;base64,OTA1NTIzMzg2NQ0KOTE3NTAwMTU0Mg0KOTc3NDczNjcyNg0KMT
IzNTQ2ODQ1Ng=='], u'reqtype': [u'filebased']}>
[2017-12-18 16:51:06,512] INFO views index: <MultiValueDict: {}>
it says your image encoded to base64
{u'fileToUpload': [u'data:text/plain;base64,OTA1NTIzMzg2NQ0KOTE3NTAwMT...
I am able to read the file content now.
I used the link from Farrukh's comment a
stackoverflow answer
Code is updated to:
#csrf_exempt
def index(request):
if request.method == 'POST':
form = ExtractForm(request.POST, request.FILES)
if form.is_valid():
res = QueryManager.processRequest(request.user, form.cleaned_data)
format, imgstr = data.split(';base64,')
ext = format.split('/')[-1]
data = ContentFile(base64.b64decode(imgstr), name='temp.' + ext)
filetext = data.read()
filetext contains the string I need from the file.

View didn't return a response

I am working on a python django web app in which I want to implement internationalization and auto translate the whole app into french or chinese.
I took reference from this site https://www.metod.io/en/blog/2015/05/05/django-i18n-part-1/
But whenever I try to run the app it shows this error:
500: ValueError at /en/get_dashboard_data/ The view
dashboard.views.getDashboardData didn't return an HttpResponse object.
It returned None instead.
And url get_dashboard_data is fetching data through ajax.
url(r'^get_dashboard_data/$', views.getDashboardData, name='getDashboardData'),
view
#login_required(login_url='/')
def getDashboardData(request):
dbname = request.user.username
if request.method == 'POST' and request.is_ajax():
if request.POST.get('action') == 'sale_chart_data':
data = DashboardData(dbname).getSaleChartData()
channel_list = data[0]
data_list = data[1]
print 123, data_list, channel_list
return HttpResponse(json.dumps({'channel_list':channel_list, 'data_list':data_list}), content_type='application/json')
if request.POST.get('action') == 'get_sale_numbers':
sale_data = DashboardData(dbname).getSaleNumbers()
return HttpResponse(json.dumps({'sale_number_data':sale_data}), content_type='application/json')
if request.POST.get('action') == 'get_inventory_numbers':
inventory_data = DashboardData(dbname).getInventoryData()
return HttpResponse(json.dumps({'inventory_data':inventory_data}), content_type='application/json')
if request.POST.get('action') == 'get_order_numbers':
order_data = DashboardData(dbname).getOrderData()
return HttpResponse(json.dumps({'order_data':order_data}), content_type='application/json')
if request.POST.get('action') == 'get_hourly_data':
order_data = DashboardData(dbname).getHourlyData()
sale_data = order_data[1]
count_data = order_data[0]
return HttpResponse(json.dumps({'sale_data':sale_data, 'count_data':count_data}), content_type='application/json')
if request.POST.get('action') == 'top_performers':
data = DashboardData(dbname).getTopPerformers()
inventory_count_dict = data[0]
current_month_dict = data[1]
last_month_dict = data[2]
current_quarter_dict = data[3]
current_year_dict = data[4]
channel_list = data[5]
return HttpResponse(json.dumps({'inventory_count_dict':inventory_count_dict,'current_month_dict':current_month_dict,'last_month_dict':last_month_dict,'current_quarter_dict':current_quarter_dict,'current_year_dict':current_year_dict,'channel_list':channel_list}), content_type='application/json')
if request.POST.get('action') == 'top_products':
product_data = DashboardData(dbname).getTopProducts()
return HttpResponse(json.dumps({'product_data':product_data}), content_type='application/json')
javascript
function getSaleChart(){
$.ajax({
url : "/get_dashboard_data/",
type : "POST",
data : {action:'sale_chart_data'},
success : function(response) {
channel_list = response.channel_list;
data_list = response.data_list;
c3.generate({
bindto: '#sale-chart-30-days',
data:{
x: 'dates',
xFormat: '%b %d',
columns: data_list,
colors:{
Flipkart: '#1AB394',
Paytm: '#BABABA'
},
type: 'bar',
groups: [ channel_list ]
},
axis: {
x: {
type: 'timeseries'
}
}
});
},
error : function(xhr,errmsg,err) {
toastr["error"]("Something Broke.", "Oops !!!.");
console.log(xhr.status + ": " + xhr.responseText);
}
});
}
This is why you should really practice more defensive programming. Though you insist that the request method is POST and it is ajax and the action is sale_chart_data one of the three isn't what you expect it to be.
Your function really should be like follows. It's plain old good practice.
def getDashboardData(request):
dbname = request.user.username
if request.method == 'POST' and request.is_ajax():
action = request.POST.get('action')
if action == 'sale_chart_data':
data = DashboardData(dbname).getSaleChartData()
....
...
# other if conditions here
else :
return HttpResponse(json.dumps({'message':'Unknown action {0}'.format(action)}), content_type='application/json')
else :
return HttpResponse(json.dumps({'message':'Only ajax post supported'}), content_type='application/json')
And then you ought to set break points and evaluate the request to figure out what exactly is happening in this particular request.
My guess would be that your JavaScript does indeed make a POST request to /get_dashboard_data/ but it receives a redirect response (HTTP 301 or 302) to /en/get_dashboard_data/ due to some kind of i18n middleware.
The browser follows the redirect, but the new request to /en/get_dashboard_data/ is a GET request.
Edit:
When following a redirect, the browser will always perform the second request as GET, there is no way to prevent that. You have several options to solve this:
make the initial request to the right application. This means you have to pass your i18n URL into your JavaScript instead of hardcoding it. You can add something like this to your template:
<script>var dashBoardDataUrl = "{% url "name-of-dashboard-url" %}"</script>
as your "actions" just get code, you could just accept a GET request and read the action from query paramters
Split that view into several smaller views that accept GET request so you have something that resembles a REST API.