Angular 5 & Django REST - Issue uploading files - django

I developed an Angular application where the user can handle brands.
When creating/updating a brand, the user can also upload a logo. All data are sent to the DB via a REST API built using the Django REST Framework.
Using the Django REST Framework API website I'm able to upload files, but using Angular when I send data thu the API I get an error.
I also tried to encode the File object to base64 using FileReader, but I get the same error from Django.
Can you help me understanding the issue?
Models:
export class Brand {
id: number;
name: string;
description: string;
is_active: boolean = true;
is_customer_brand: boolean = false;
logo_img: Image;
}
export class Image {
id: number;
img: string; // URL path to the image (full size)
img_md: string; // medium size
img_sm: string; // small
img_xs: string; // extra-small/thumbnail
}
Service:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Headers, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { Brand } from './brand';
const endpoint = 'http://127.0.0.1:8000/api/brands/'
#Injectable()
export class BrandService {
private brands: Array<Brand>;
constructor(private http: Http) { }
list(): Observable<Array<Brand>> {
return this.http.get(endpoint)
.map(response => {
this.brands = response.json() as Brand[];
return response.json();
})
.catch(this.handleError);
}
create(brand: Brand): Observable<Brand> {
console.log(brand);
return this.http.post(endpoint+'create/', brand)
.map(response => response.json())
.catch(this.handleError);
}
get(id): Observable<Brand> {
return this.http.get(endpoint+id)
.map(response => response.json())
.catch(this.handleError);
}
private handleError(error:any, caught:any): any {
console.log(error, caught);
}
}
Error from the browser console:
"{"logo_img":{"img":["The submitted data was not a file. Check the
encoding type on the form."]}}"
Django Serializer:
class BrandSerializer(ModelSerializer):
is_active = BooleanField(required=False)
logo_img = ImageSerializer(required=False, allow_null=True)
class Meta:
model = Brand
fields = [
'id',
'name',
'description',
'is_active',
'is_customer_brand',
'logo_img',
]
def update(self, instance, validated_data):
image = validated_data.get('logo_img',None)
old_image = None
if image:
image = image.get('img',None)
brand_str = validated_data['name'].lower().replace(' ','-')
ext = validated_data['logo_img']['img'].name.split('.')[-1].lower()
filename = '{0}.{1}'.format(brand_str,ext)
user = None
request = self.context.get('request')
if request and hasattr(request, 'user'):
user = request.user
image_serializer_class = create_image_serializer(path='logos', filename=filename, created_by=user, img_config = {'max_w':3000.0,'max_h':3000.0,'max_file_size':1.5,'to_jpeg':False})
image_serializer = image_serializer_class(data=validated_data['logo_img'])
image_serializer.is_valid()
validated_data['logo_img'] = image_serializer.save()
old_image = instance.logo_img
super(BrandSerializer, self).update(instance,validated_data)
if old_image: # Removing old logo
old_image.img.delete()
old_image.img_md.delete()
old_image.img_sm.delete()
old_image.img_xs.delete()
old_image.delete()
return instance
def create(self, validated_data):
image = validated_data.get('logo_img',None)
print(image)
if image:
print(image)
image = image.get('img',None)
print(image)
brand_str = validated_data['name'].lower().replace(' ','-')
ext = validated_data['logo_img']['img'].name.split('.')[-1].lower()
filename = '{0}.{1}'.format(brand_str,ext)
user = None
request = self.context.get('request')
if request and hasattr(request, 'user'):
user = request.user
image_serializer_class = create_image_serializer(path='logos', filename=filename, created_by=user, img_config = {'max_w':3000.0,'max_h':3000.0,'max_file_size':1.5,'to_jpeg':False})
image_serializer = image_serializer_class(data=validated_data['logo_img'])
image_serializer.is_valid()
validated_data['logo_img'] = image_serializer.save()
return super(BrandSerializer, self).create(validated_data)

When posting a new brand to the server with files, I have three main choices:
Base64 encode the file, at the expense of increasing the data size by around 33%.
Send the file first in a multipart/form-data POST, and return an ID to the client. The client then sends the metadata with the ID, and the server re-associates the file and the metadata.
Send the metadata first, and return an ID to the client. The client then sends the file with the ID, and the server re-associates the file and the metadata.
The Base64 encoding will involve unacceptable payload.
So I choose to use multipart/form-data.
Here's how I implemented it in Angular's service:
create(brand: Brand): Observable<Brand> {
let headers = new Headers();
let formData = new FormData(); // Note: FormData values can only be string or File/Blob objects
Object.entries(brand).forEach(([key, value]) => {
if (key === 'logo_img') {
formData.append('logo_img_file', value.img);
} else {
formData.append(key, value);
});
return this.http.post(endpoint+'create/', formData)
.map(response => response.json())
.catch(this.handleError);
}
IMPORTANT NOTE: Since there's no way to have nested fields using FormData, I cannot append formData.append('logo_img', {'img' : FILE_OBJ }). I had change the API in order to receive the file in one field called logo_img_file.
Hope that my issue helped someone.

Related

Passing a pdf from Django backend to Angular frontend

I haven't been able to make any of the solutions to similar problems work for my case.
I would like to load a pdf from filesystem with django and return it via an API call to Angular so that it can be displayed. My Django code is pretty much:
class LoadPdfViewSet(views.APIView):
def get(self, request):
# some code here here
response = FileResponse(open(path_to_pdf, 'rb').read())
response.headers = {
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment;filename="report.pdf"',
}
response.as_attachment = True
return response
while on the Angular side I have a service that does this:
export class LoadPdfService {
constructor(
private http: HttpClient
) {}
getPdf(): Observable<Blob> {
const params = new HttpParams({
fromObject: {
responsetype: 'arraybuffer'
// other stuff here
}
})
return self.http.get<Blob>(loadpdf_api_url, {params}).pipe(catchError(self.myErrorHandler))
}
}
and a component that tries to open the pdf like this:
export class MyComponent {
constructor(
public loadPdfService: LoadPdfService
) {}
download_pdf() {
let call = self.loadPdfService.getPdf();
call.subscribe( (response:Blob) => {
if (window.navigator && window.navigator.msSaveOrOpenBlob) { // for IE
window.navigator.msSaveOrOpenBlob(blob, "report.pdf");
} else {
let pdfUrl = URL.createObjectURL(blob)
window.open(pdfUrl, '_blank')
URL.revokeObjectURL(pdfUrl);
}
}
}
}
but nothing happens. I have also tried using different responses and passthrough renderers on the django side, and Observable<Response> and .then() callbacks like
response.arrayBuffer().then(buffer => new Blob([buffer], {type: 'application/pdf'}))
on the Angular side. Sometimes I have managed to get the new window/tab to open but no pdf could be displayed.
I finally figured it out. In the python part, the read() can be removed with no problem. The issue was with the service response type and mapping of the response:
getPdf(): Observable<Blob> {
const options = {
params: new HttpParams({
fromObject: {
// my own parameters here
}
}),
responseType: 'blob' as 'json'
};
return this.http.get(this.url, options).pipe(
map(response => response as Blob),
catchError(this.myErrorHandler))
}

Why is flask jsonify returning unidentified?

I am using fetch on the frontend to send data to my flask backend in order to make a movie seat booking. The whole process works fine until the client awaits the response, which is "undefined" . So , basically the database saves the data , the only problem is the response which is sent to the client. I used jsonify which usually works fine. Can anybody tell me what I am missing? Thanks in advance.
Here is the JS code :
function sendReservationToServer() {
const selectedSeats = sessionStorage.getItem('selectedSeats')
const reservation = { userId, selectedSeats, showTimeId, movieHallId }
fetch('/bookSeats', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(reservation)
}).then(response => {
response.json()
}).then(data => {
theatreHall.innerHTML = `${data} <br> <a href='/home'>Back to main menu</a>`
console.log(`${data}`)
}).catch(err => infoMsg.textContent = err)
sessionStorage.clear()
}
And this is the flask controller which handles the request:
#app.route("/bookSeats", methods=["POST"])
def book_seats():
selected_seats = request.json
user_id = selected_seats.get('userId')
seats = json.loads(selected_seats.get('selectedSeats'))
movie_hall_id = selected_seats.get('movieHallId')
seat_ids = []
showtime_id = selected_seats.get('showTimeId')
for seat in seats:
seat_ids.append(db.session.query(Seat).filter(
Seat.seat_number == seat).filter(Seat.movie_hall_id == movie_hall_id).all()[0].stid)
for seat in seat_ids:
reserved_seat = ReservedSeat(
seat_id=seat, show_time=showtime_id, user=user_id)
db.session.add(reserved_seat)
db.session.commit()
reservation = Reservation(
user=user_id, show_time=showtime_id, number_of_tickets=len(seat_ids))
db.session.add(reservation)
db.session.commit()
message = f'{seats} booked successfully'
return jsonify(message)
data is undefined because the first then does not return anything. Either make it return response.json() or move everything in the second then to the first and replace data with response.json().

How to pass django serializers Validation Error of a field to React as json fromat

In my rest framework user registration form I successfully created a custom registration form and added some validators. It shows corresponding validation errors in rest framework.but when linked this to react through redux it gets the error but not showing corresponding validation errors like " A user is already registered with this e-mail address. ".
The rest showsit like this.
JSON format that gets at react is this
error: {
message: 'Request failed with status code 400',
name: 'Error',
fileName: 'http://localhost:3000/static/js/1.chunk.js',
lineNumber: 871,
columnNumber: 15,
stack: 'createError#http://localhost:3000/static/js/1.chunk.js:871:15\nsettle#http://localhost:3000/static/js/1.chunk.js:1092:12\nhandleLoad#http://localhost:3000/static/js/1.chunk.js:346:13\nEventHandlerNonNull*dispatchXhrRequest#http://localhost:3000/static/js/1.chunk.js:322:5\nxhrAdapter#http://localhost:3000/static/js/1.chunk.js:301:10\ndispatchRequest#http://localhost:3000/static/js/1.chunk.js:924:10\npromise callback*request#http://localhost:3000/static/js/1.chunk.js:712:23\nforEachMethodWithData/Axios.prototype[method]#http://localhost:3000/static/js/1.chunk.js:736:17\nwrap#http://localhost:3000/static/js/1.chunk.js:1252:15\nsignUp/<#http://localhost:3000/static/js/main.chunk.js:3406:50\ncreateThunkMiddleware/</</<#http://localhost:3000/static/js/1.chunk.js:40096:18\ndispatch#http://localhost:3000/register:1:28546\nonAuth#http://localhost:3000/static/js/main.chunk.js:3305:110\nRegMain/this.handlesubmit#http://localhost:3000/static/js/main.chunk.js:2684:62\ncallCallback#http://localhost:3000/static/js/1.chunk.js:8030:18\ninvokeGuardedCallbackDev#http://localhost:3000/static/js/1.chunk.js:8079:20\ninvokeGuardedCallback#http://localhost:3000/static/js/1.chunk.js:8132:35\ninvokeGuardedCallbackAndCatchFirstError#http://localhost:3000/static/js/1.chunk.js:8147:29\nexecuteDispatch#http://localhost:3000/static/js/1.chunk.js:8232:46\nexecuteDispatchesInOrder#http://localhost:3000/static/js/1.chunk.js:8257:24\nexecuteDispatchesAndRelease#http://localhost:3000/static/js/1.chunk.js:11141:33\nexecuteDispatchesAndReleaseTopLevel#http://localhost:3000/static/js/1.chunk.js:11150:14\nforEachAccumulated#http://localhost:3000/static/js/1.chunk.js:11122:12\nrunEventsInBatch#http://localhost:3000/static/js/1.chunk.js:11167:25\nrunExtractedPluginEventsInBatch#http://localhost:3000/static/js/1.chunk.js:11377:23\nhandleTopLevel#http://localhost:3000/static/js/1.chunk.js:11421:40\nbatchedEventUpdates$1#http://localhost:3000/static/js/1.chunk.js:29567:16\nbatchedEventUpdates#http://localhost:3000/static/js/1.chunk.js:8639:16\ndispatchEventForLegacyPluginEventSystem#http://localhost:3000/static/js/1.chunk.js:11431:28\nattemptToDispatchEvent#http://localhost:3000/static/js/1.chunk.js:12151:48\ndispatchEvent#http://localhost:3000/static/js/1.chunk.js:12072:45\nunstable_runWithPriority#http://localhost:3000/static/js/1.chunk.js:41893:16\nrunWithPriority$1#http://localhost:3000/static/js/1.chunk.js:18917:14\ndiscreteUpdates$1#http://localhost:3000/static/js/1.chunk.js:29584:16\ndiscreteUpdates#http://localhost:3000/static/js/1.chunk.js:8652:16\ndispatchDiscreteEvent#http://localhost:3000/static/js/1.chunk.js:12051:22\nEventListener.handleEvent*addEventBubbleListener#http://localhost:3000/static/js/1.chunk.js:11905:15\ntrapEventForPluginEventSystem#http://localhost:3000/static/js/1.chunk.js:12045:31\ntrapBubbledEvent#http://localhost:3000/static/js/1.chunk.js:12015:36\nsetInitialProperties#http://localhost:3000/static/js/1.chunk.js:13826:27\nfinalizeInitialChildren#http://localhost:3000/static/js/1.chunk.js:15351:27\ncompleteWork#http://localhost:3000/static/js/1.chunk.js:26705:44\ncompleteUnitOfWork#http://localhost:3000/static/js/1.chunk.js:29895:20\nperformUnitOfWork#http://localhost:3000/static/js/1.chunk.js:29868:16\nworkLoopSync#http://localhost:3000/static/js/1.chunk.js:29833:26\nperformSyncWorkOnRoot#http://localhost:3000/static/js/1.chunk.js:29451:13\nscheduleUpdateOnFiber#http://localhost:3000/static/js/1.chunk.js:28883:32\nupdateContainer#http://localhost:3000/static/js/1.chunk.js:32032:19\nlegacyRenderSubtreeIntoContainer/<#http://localhost:3000/static/js/1.chunk.js:32415:26\nunbatchedUpdates#http://localhost:3000/static/js/1.chunk.js:29601:16\nlegacyRenderSubtreeIntoContainer#http://localhost:3000/static/js/1.chunk.js:32414:25\nrender#http://localhost:3000/static/js/1.chunk.js:32497:14\n./src/index.js#http://localhost:3000/static/js/main.chunk.js:4166:50\n__webpack_require__#http://localhost:3000/static/js/bundle.js:785:30\nfn#http://localhost:3000/static/js/bundle.js:151:20\n1#http://localhost:3000/static/js/main.chunk.js:4181:18\n__webpack_require__#http://localhost:3000/static/js/bundle.js:785:30\ncheckDeferredModules#http://localhost:3000/static/js/bundle.js:46:23\nwebpackJsonpCallback#http://localhost:3000/static/js/bundle.js:33:19\n#http://localhost:3000/static/js/main.chunk.js:1:71\n',
config: {
url: 'http://127.0.0.1:8000/rest-auth/register/',
method: 'post',
data: '{"fname":"Abhiram","lname":"PS","username":"rooteeee","phone":"8589967868","email":"abhirampjayan#outlook.com","cemail":"abhirampjayan#outlook.com","password1":"abhi1998","password2":"abhi1998","gender":"M","acctype":"Student"}',
headers: {
Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/json;charset=utf-8'
},
transformRequest: [
null
],
transformResponse: [
null
],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1
}
}
source code of serializer.py is given bellow
from rest_framework import serializers
from accounts.models import Account
from allauth.account import app_settings as allauth_settings
from allauth.utils import email_address_exists
from allauth.account.adapter import get_adapter
from allauth.account.utils import setup_user_email
from rest_framework.validators import UniqueValidator
from rest_framework.response import Response
from rest_framework import status
class CustomUserDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ('pk','email','fname','lname','username','phone','gender','acctype')
#read_only_fields = ('email','acctype','fname','lname')
class CustomRegisterSerializer(serializers.Serializer):
fname = serializers.CharField(required=True, write_only=True)
lname = serializers.CharField(required=True, write_only=True)
username = serializers.CharField(min_length=allauth_settings.USERNAME_MIN_LENGTH,required=allauth_settings.USERNAME_REQUIRED,validators=[UniqueValidator(queryset=Account.objects.all(),message='A user is already registered with this Username.')])
phone=serializers.CharField(required=True, write_only=True,min_length=10,max_length=10)
email = serializers.EmailField(required=allauth_settings.EMAIL_REQUIRED)
cemail = serializers.EmailField(required=allauth_settings.EMAIL_REQUIRED)
password1 = serializers.CharField(write_only=True, required=True)
password2 = serializers.CharField(write_only=True, required=True)
acctype=serializers.ChoiceField(['Student','Staff'])
gender=serializers.ChoiceField([('M','M'),('F','F')])
def validate_email(self, email):
email = get_adapter().clean_email(email)
if allauth_settings.UNIQUE_EMAIL:
if email and email_address_exists(email):
raise serializers.ValidationError(("A user is already registered with this e-mail address."))
return email
def validate_password1(self, password):
return get_adapter().clean_password(password)
def validate(self, data):
if data['password1'] != data['password2']:
raise serializers.ValidationError(("The password fields didn't match."))
if data['email'] !=data['cemail']:
raise serializers.ValidationError(("The email fields didn't match."))
return data
def get_cleaned_data(self):
return {
'fname': self.validated_data.get('fname', ''),
'lname': self.validated_data.get('lname', ''),
'username': self.validated_data.get('username', ''),
'acctype': self.validated_data.get('acctype', ''),
'gender': self.validated_data.get('gender', ''),
'phone': self.validated_data.get('phone', ''),
'password1': self.validated_data.get('password1', ''),
'email': self.validated_data.get('email', ''),
}
def save(self, request):
adapter = get_adapter()
user = adapter.new_user(request)
self.cleaned_data = self.get_cleaned_data()
adapter.save_user(request, user, self)
setup_user_email(request, user, [])
user.fname=self.cleaned_data.get('fname')
user.lname=self.cleaned_data.get('lname')
user.phone=self.cleaned_data.get('phone')
user.acctype=self.cleaned_data.get('acctype')
user.gender=self.cleaned_data.get('gender')
user.save()
return user
redux action creators file(auth.js) is given below
import * as actionTypes from './actionTypes'
import axios from 'axios'
export const authStart=()=>{
return{
type:actionTypes.AUTH_START
}
}
export const authSuccess=(token)=>{
return{
type:actionTypes.AUTH_SUCCESS,
token:token
}
}
export const authFail=(error)=>{
return{
type:actionTypes.AUTH_FAIL,
error:error
}
}
export const checkAuthTimeout = expirationTime =>{
return disaptch=>{
setTimeout(()=>{
disaptch(logout())
},expirationTime * 1000)
}
}
export const autLlogout=()=>{
return{
type:actionTypes.AUTH_LOGOUT
}
}
export const authLogin=(username,password)=>{
return disaptch=>{
disaptch(authStart())
axios.post('http://127.0.0.1:8000/rest-auth/login/',{
username:username,
password:password
}).then(res=>{
const token=res.data.key
const expirationDate = new Date(new Date().getTime() + 3600 * 1000)
localStorage.setItem('token',token)
localStorage.setItem('expirationDate',expirationDate)
disaptch(authSuccess(token))
disaptch(checkAuthTimeout(3600))
}).catch(err=>{
disaptch (authFail(err))
})
}
}
export const signUp=(fname,lname,username,phone,email,cemail,password1,password2,gender,acctype)=>{
return disaptch=>{
disaptch(authStart())
axios.post('http://127.0.0.1:8000/rest-auth/register/',{
fname:fname,
lname:lname,
username:username,
phone:phone,
email:email,
cemail:cemail,
password1:password1,
password2:password2,
gender:gender,
acctype:acctype,
}).then(res=>{
const token=res.data.key
const expirationDate = new Date(new Date().getTime() + 3600 * 1000)
localStorage.setItem('token',token)
localStorage.setItem('expirationDate',expirationDate)
disaptch(authSuccess(token))
disaptch(checkAuthTimeout(3600))
}).catch(err=>{
disaptch (authFail(err))
})
}
}
export const logout=()=>{
localStorage.removeItem('uesr')
localStorage.removeItem('expirationDate')
return {
type:actionTypes.AUTH_LOGOUT
}
}
export const authCheckState=()=>{
return disaptch=>{
const token=localStorage.getItem('token')
if(token===undefined){
disaptch(logout())
}else{
const expirationDate=new Date(localStorage.getItem('expirationDate'))
if(expirationDate<=new Date()){
disaptch(logout())
}else{
disaptch(authSuccess(token))
disaptch(checkAuthTimeout((expirationDate.getTime()-new Date().getTime())/1000))
}
}
}
}
On first glance, this issue looks to be in react layer:
Axios will treat the 400 status code response as an error, and rather than resolve the promise and determine response data, will throw the error you are seeing above.
One way to handle this:
axios.create({
// ...vars
responseType: "json",
validateStatus: status => {
// handling our own errors less than 500 status
return status < 500;
},
headers: headers
});
when creating the initial axios client. This will cause axios to not throw an error.
Then, within your promise .then(res=>{...}) you will need to check the response status code for 400, and process errors accordingly.
Code:
export const authLogin=(username,password)=>{
return dispatch=>{
dispatch(authStart())
axios.post('http://127.0.0.1:8000/rest-auth/login/',{
username:username,
password:password
}, {
validateStatus: status => {return status < 500}
}).then(res=>{
if (res.status === 400) {
// Will have validation errors on response body
dispatch(authFail(res.data))
} else {
const token=res.data.key
const expirationDate = new Date(new Date().getTime() + 3600 * 1000)
localStorage.setItem('token',token)
localStorage.setItem('expirationDate',expirationDate)
dispatch(authSuccess(token))
dispatch(checkAuthTimeout(3600))
}
}).catch(err=>{
dispatch (authFail(err))
})
}
}

Django2: Submit and store blobs as image files

I have made a few Django projects after having read the tutorial but I am by no means an expert in Django.
I am trying to take a screenshot of the current page and store it (if one does not exist).
To achieve this, we require a few things:
function to get screen shot of current page
function to async post this image to a view which should store it
view that stores the posted image
However, the screen shot function results in a Blob and I am having trouble getting a Django view to properly handle this.
A demo project is available here: https://gitlab.com/SumNeuron/so_save_blob
Function for screenshot
const screenshot = (function() {
function urlsToAbsolute(nodeList) {
if (!nodeList.length) {
return [];
}
var attrName = 'href';
if (nodeList[0].__proto__ === HTMLImageElement.prototype
|| nodeList[0].__proto__ === HTMLScriptElement.prototype) {
attrName = 'src';
}
nodeList = [].map.call(nodeList, function (el, i) {
var attr = el.getAttribute(attrName);
if (!attr) {
return;
}
var absURL = /^(https?|data):/i.test(attr);
if (absURL) {
return el;
} else {
return el;
}
});
return nodeList;
}
function addOnPageLoad_() {
window.addEventListener('DOMContentLoaded', function (e) {
var scrollX = document.documentElement.dataset.scrollX || 0;
var scrollY = document.documentElement.dataset.scrollY || 0;
window.scrollTo(scrollX, scrollY);
});
}
function capturePage(){
urlsToAbsolute(document.images);
urlsToAbsolute(document.querySelectorAll("link[rel='stylesheet']"));
var screenshot = document.documentElement.cloneNode(true);
var b = document.createElement('base');
b.href = document.location.protocol + '//' + location.host;
var head = screenshot.querySelector('head');
head.insertBefore(b, head.firstChild);
screenshot.style.pointerEvents = 'none';
screenshot.style.overflow = 'hidden';
screenshot.style.webkitUserSelect = 'none';
screenshot.style.mozUserSelect = 'none';
screenshot.style.msUserSelect = 'none';
screenshot.style.oUserSelect = 'none';
screenshot.style.userSelect = 'none';
screenshot.dataset.scrollX = window.scrollX;
screenshot.dataset.scrollY = window.scrollY;
var script = document.createElement('script');
script.textContent = '(' + addOnPageLoad_.toString() + ')();';
screenshot.querySelector('body').appendChild(script);
var blob = new Blob([screenshot.outerHTML], {
type: 'text/html'
});
return blob;
}
return capturePage
})()
Function to async post Blob
function setupAjaxWithCSRFToken() {
// using jQuery
var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
// set csrf header
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
}
function asyncSubmitBlob( url, blob ) {
var fd = new FormData();
fd.append('image', blob);
$.ajax({
url: url,
type: "POST",
data: fd,
contentType: false,
processData: false,
success: function(response){ console.log(response) },
error: function(data){ console.log(data) }
})
}
So to submit a screenshot of the current page:
setupAjaxWithCSRFToken()
const page = window.location.pathname;
const blob_url = "{% url 'my-app:post_blob' 'REPLACE' %}".replace(/REPLACE/,page == '/' ? '' : page)
asyncSubmitBlob( blob_url, screenshot() )
View to store the posted blob image
urls.py
...
from django.urls import include, path
...
app_name='my-app'
url_patterns=[
...
path('post_blob/', views.post_blob, {'page':'/'},name='post_blob'),
path('post_blob/<page>', views.post_blob,name='post_blob'),
...
]
views.py
from .models import PageBlob
...
def post_blob(request, page):
if request.FILES: # save screenshot of specified page
try:
pb = PageBlob.objects.all().filter(page=page))
if not pb.count():
pb = PageBlob()
pb.page = page
pb.blob = request.FILES['image']
pb.save()
return HttpResponse('Blob Submitted')
except:
return HttpResponse('[App::my-app]\tError when requesting page_image({page})'.format(page=page))
else: # return screenshot of requested page
try:
# get objects storing screenshot for requested page
pb = PageBlob.objects.all().filter(page=page)
# if one exists
if pb.count():
pb = pb[0]
## this just returns the string literal "blob"
return HttpResponse(str(pb.blob))
return HttpResponse('[App::my-app]\tNo blob for {page}'.format(page=page))
except:
return HttpResponse('[App::my-app]\tError when trying to retrieve blob for {page}'.format(page=page))
return HttpResponse('Another response')
models.py
class PageBlob(models.Model):
page = models.CharField(max_length=500)
blob = models.TextField(db_column='data', blank=True)
But I can not seem to faithfully capture and retrieve the blob.
Many S.O. questions of storing blobs use the model approach with import base64 to encode and decode the blob. One even recommends using the BinaryField. However, Django's documentation states firmly that BinaryField is not a replacement for the handling of static files.
So how could I achieve this?
S.O. posts I have found helpful to get this far
Upload an image blob from Ajax to Django
How to screenshot website in JavaScript client-side / how Google did it? (no need to access HDD)
How to download data in a blob field from database in django?
passing blob parameter to django
Returning binary data with django HttpResponse
https://djangosnippets.org/snippets/1597/
Django Binary or BLOB model field
Django CSRF check failing with an Ajax POST request
How can javascript upload a blob?

Saving user object to post Graphql/Apollo Vue

I am trying to save a user id to a new biz. I keep getting a 400 error and can not figure out why. I am using django for the backend with graphql and apollo client for the front with vue js. I am able to get the owner id but not able to save it for some reason.
Create Biz Mutation Apollo
export const CREATE_BIZ_MUTATION = gql`
mutation CreateBizMutation($name: String!, $owner: ID!) {
createBiz(name: $name, ownerId: $owner) {
name
}
}`
Create Biz mutation Django
class CreateBiz(graphene.Mutation):
id = graphene.Int()
name = graphene.String()
code = graphene.String()
owner = graphene.Field(UserType)
class Arguments:
name = graphene.String()
def mutate(self, info, name):
user = get_user(info) or None
code = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits + string.ascii_lowercase) for _ in range(6))
biz = Biz(
code = code,
name = name,
owner = user
)
biz.save()
return CreateBiz(
id= biz.id,
name = biz.name,
code = biz.code,
owner = biz.owner
)
Create Biz Component
createBiz () {
const owner = localStorage.getItem(DJANGO_USER_ID)
if (!owner) {
console.error('No user logged in')
return
}
const { name } = this.$data
this.$apollo.mutate({
mutation: CREATE_BIZ_MUTATION,
variables: {
name,
owner
}
}).catch((error) => {
console.log(error)
})
}
}