How to upload an image using a post api in django - django

I am working on a project using django 3.1 for backend and vue 3 for frontend.
I am pretty new to Django so I am still learning the ropes and I do not know if what I am trying is totally wrong.
I created a model that holds a user email and and an image field as follows:
class UsedBike(models.Model):
sellerEmail = models.CharField(max_length=255)
image = models.ImageField(upload_to='uploads/', blank=True, null=True)
class Meta:
ordering = ('sellerEmail', )
def __str__(self):
return self.sellerEmail
def get_image(self):
if self.image:
return 'http://127.0.0.1:8000' + self.image.url
return ''
I created a serializer for my model as follows:
class UsedBikeSerializer(serializers.ModelSerializer):
class Meta:
model = UsedBike
fields = (
"id",
"sellerEmail",
"get_image",
)
and in the views file, I created a function for saving the data in the database:
#api_view(['POST'])
def sellBike(request):
serializer = UsedBikeSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
I also registered the url in the urls file.
As for vue part, I used axios to send my post request as follows:
submitData() {
const formData = {
sellerEmail: this.sellerEmail,
image: this.productImage
}
axios
.post("/api/v1/sell-bike/", formData)
.then(response => {
console.log(response)
}).catch(error => {
console.log(error)
})
},
where in the template I am getting the inputs like this:
<input type="text" v-model="sellerEmail">
<input type="file" accept="image/png, image/jpeg" id="imageInput" v-on:change="onFileChange()">
<button #click="submitData">Upload</button>
and the method onFileChange is:
onFileChange(e) {
let imageInput = document.getElementById("imageInput")
this.productImage = imageInput.files[0]
},
When I send the request, I get "POST /api/v1/sell-bike/ HTTP/1.1" 200 55 in the django terminal. However, If I print request.data I get the following:
{'sellerEmail': 'test#email.com', 'image': {}}
As you can see, image is empty and when I checked the database, the email part is filled correctly but image is empty.
How can I solve this problem?
Any help is appreciated.

IN your axios call, you need to include the below header as well:
submitData() {
const formData = new FormData();
formData.append('sellerEmail', this.sellerEmail);
formData.append('image', this.productImage);
const headers = {headers: { 'Content-Type': 'multipart/form-data' }}
axios
.post("/api/v1/sell-bike/", formData, headers)
.then(response => {
console.log(response)
}).catch(error => {
console.log(error)
})
}
Also, to save image to database, you also need to include the "image" in fields inside UsedBikeSerializer

Related

How to upload an image to django from an html input file

I am developing a web service on django with frontend on react. I ran into a problem that I can't upload an image to django. Below is my component code where I'm trying to download it:
export function ProviderRegistration(){
const[logoField, setLogoField] = useState()
const token = useSelector((state) => state.user.token)
const [cookie, setCookie] = useCookies(['auth'])
const confirm = () => {
axios.post(`/providers/?username=${cookie.auth.login}`,
{
photo : logoField
},
{ "headers" : { "Authorization" : "token " + token }})
.then(res => console.log(res))
.catch(err => console.log(err))
}
return(
<div className="registration-provider-container">
<div className="registration-title">Provider registration</div>
<input type="file" className="registration-logo-add" onChange={(e) => setLogoField(e.target.value)}/>
<button className="registration-confirm" onClick={() => confirm()}>Confirm</button>
</div>
)}
And the endpoint processing this request
class Providers(viewsets.ModelViewSet):
filterset_class = ProvidersFilter
queryset = Provider.objects.all()
permission_classes = [IsAuthenticatedOrReadOnly, IsProviderPermission, IsOneToOneProviderPermission]
def get_serializer_class(self):
if self.action == 'list':
return GetProviderSerializer
else:
return PutProviderSerializer
def create(self, request):
username = request.GET.get('username', '')
user = User.objects.get(username=username).pk
request.data.update({'user' : user})
print(request.data)
return super().create(request)
When I try to upload an image, django returns the following error:
"The submitted data was not a file. Check the encoding type on the form."
And I haven't found a way to correctly upload an image to django using Ajax.
I also output what my browser sends to the server:
{photo : 'C:\\fakepath\\magnit.jpg'}

Pass List of UUID to django endpoint url as param

I have this code
#VIEWS
def report_pdf(request, queryset):
if request.method == "GET":
trans = Transaction.objects.filter(id__in=queryset)
return something
#URLS
path("pdf/<uuid:queryset>", views.report_pdf, name="get_pdf")
#FRONT END
const handlePDFDownload = (ids) => {
const body = ids
axios.get(`/calc/pdf/`,body , {
responseType: 'blob',
}).then(res => {
fileDownload(res.data, 'filename.zip');
console.log(res, 'downloading');
}).catch(err => {
console.log(err);
})
}
Now from my frontend react iam sending a get request to this endpoint with a list of UUID values.
I cant find a way to access that list in my view coming from frontend.
Would appreciate any suggestions!
I would suggest you pass the uuids as query params to the url as
../pdf?queryset=<uuids here>
then get the list in your view as
queryset = request.GET.get('queryset')
and then use queryset further in view.
You are sending list of uuid's from frontend but in your urls expects one uuid so that you need to change it like this:
urls.py:
path("pdf/", views.report_pdf, name="get_pdf")
views.py:
def report_pdf(request):
if request.method == "GET":
uuid_list = request.GET.getlist("queryset[]")
trans = Transaction.objects.filter(id__in=uuid_list)
return something

django upload a file from other python script

I want to save file from the client to the django project server's database from a script. I've tried to do this using a model and a view in the django project, and post request in the other python script, but it keeps return 403 error and not save the file and the data to the database.
models.py:
class ScreenRecord(models.Model):
record = models.FileField(default='output.avi', upload_to='records')
writeTime = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
views.py:
def getscreenrecords(request):
user = User.objects.filter(username=request.POST.get('user')).first()
k = ScreenRecord(record=request.FILES.get('record'), user=user)
k.save()
return HttpResponse('success ' + request.GET.__getitem__('user'))
url.py:
from . import views
urlpatterns = [
path('screenrecords/', views.getscreenrecords, name='getscreenrecords'),
]
python script to send the file:
url = 'http://127.0.0.1:8000/send/screenrecords/'
files = {'record': open('output.avi','rb')}
values = {'user': 'newUser'}
r = requests.post(url, files=files, data=values)
print(r)
what's wrong in my code or is there a way to do this better?
Django needs a CSRF token in POST requests by default.
Check this for more info on how to use it on your requests.
You need to pass csrf_token along with the data passed in your js, if you are doing it within the Django project, here is a sample code to do it.
<script>
var token = '{{csrf_token}}';
$("#id_username").change(function () {
console.log($(this).val());
var form = $(this).closest("form");
$.ajax({
headers: { "X-CSRFToken": token },
url: form.attr("data-validate-username-url"),
data: form.serialize(),
dataType: 'json',
success: function (data) {
if (data.is_taken) {
alert(data.error_message);
}
}
});
});
</script>

How to upload File from Angular 7 to Django - get error 403 (Forbidden)

I tried now in so many different ways, I can not get a file uploaded with Angular 7 to the Django Backend - shouldn't be so difficult?!
My .html:
<div class="form-group">
<label for="file">Choose File</label>
<input type="file"
id="file"
(change)="handleFileInput($event.target.files)">
</div>
uploader.component.ts
fileToUpload: File = null;
handleFileInput(files: FileList) {
this.fileToUpload = files.item(0);
this.uploadFileToActivity();
}
uploadFileToActivity() {
this.uploaderService.post(this.fileToUpload).subscribe(data => {
// do something, if upload success
}, error => {
console.log(error);
});
}
my uploader.service.ts that also shows the upload progress
public post( fileToUpload: File): Observable<number>{
const url = '/api/upload/';
var subject = new Subject<number>()
const req = new HttpRequest('POST', url, fileToUpload, {
reportProgress: true,
});
this.httpClient.request(req).subscribe(event => {
if (event.type === HttpEventType.UploadProgress) {
const percentDone = Math.round(100 * event.loaded / event.total);
subject.next(percentDone);
} else if (event instanceof HttpResponse) {
subject.complete();
}
});
return subject.asObservable();
}
And in the Django backend:
views.py:
def post(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
#handle_uploaded_file(request.FILES['file'])
return HttpResponseRedirect('/success/url/')
else:
form = UploadFileForm()
return render(request, 'index.html', {'form': form})
forms.py:
class UploadFileForm(forms.Form):
title = forms.CharField(max_length=50)
file = forms.FileField()
and urls.py:
urlpatterns = [
path(r'api/upload/', views.post, name='post'),
]
When I ran this I get
zone.js:2969 POST http://127.0.0.1:8000/api/upload/ 403 (Forbidden)
Do I need to include an authorization token? If yes: how?
Thanks a lot!
EDIT: After the useful input from Martin Urbanec I inspected the file upload request in the Browser. Here the result:
Someone any idea what I need to change in my code above to make this work?
Content-Type header must be multipart/form-data to transfer any files. I recommend you to check if this header is sent to your Django backend.

ionic 2 upload image to django rest

I am trying to upload an image from Ionic 2 app to Django-powered website through Django Rest API.
The API is working and tested through Postman but I always get HTTP 400 BAD Request error in Ionic.
Here is my code in Ionic:
openCamera(){
var options = {
sourceType: Camera.PictureSourceType.CAMERA,
destinationType: Camera.DestinationType.DATA_URL
};
Camera.getPicture(options).then((imageData) => {
this.imageName = imageData;
this.imageURL = 'data:image/jpeg;base64,' + imageData;
}, (err) => {
this.showAlert(err);
});
}
Upload file (I am serving my Django project on my local PC with IP address 192.168.22.4):
transferData(auth){
let headers = new Headers();
headers.append('Authorization', auth);
let formData = new FormData();
formData.append('image', this.imageURL, this.imageName);
this.http.post("http://192.168.22.4/api-imageUpload", formData, {headers: headers}).subscribe(res => {
let status = res['status'];
if(status == 200){
this.showAlert( "The image was successfully uploaded!");
}else{
this.showAlert("upload error");
}
}, (err) => {
var message = "Error in uploading file " + err
this.showAlert(message);
});
}
On Django, here is my serializer:
class ImageDetailsSerializer(serializers.ModelSerializer):
image = serializers.ImageField(max_length=None, use_url=True)
class Meta:
model = ImageDetails
fields= ('image','status','category', 'user') ####status, category has default value
and views.py:
class ImageDetailsViewSet(generics.ListCreateAPIView):
queryset = ImageDetails.objects.all()
serializer_class = ImageDetailsSerializer
I am not sure if my code in uploading file is correct. I am trying to pass the data through Form data since the form works well in my API. Is this method correct? Are there any other methods to get this work?
Note: I have tried to use Transfer Cordova plugin but it is not working.
I finally solved the problem. The HTTP 400 indicates that there is a syntax error somewhere in the code and that is the encoding used in the uploaded photo. Mobile data uses base64 encoding. When sending requests, the file will then be converted to a Unicode string.
On the other hand, Django-Rest uses normal encoding for images, thus by default, it cannot support base64 image. But luckily, this plugin is already available at GitHub.
You just need to install the plugin and import it on your serializers.py:
from drf_extra_fields.fields import Base64ImageField
class ImageDetailsSerializer(serializers.ModelSerializer):
image = Base64ImageField()
class Meta:
model = ImageDetails
fields= ('image','status','category', 'user')
On Ionic side, you have to submit the actual image not the imageURL. In my case I just have to tweak my code to:
transferData(auth){
let headers = new Headers();
headers.append('Authorization', auth);
let formData = new FormData();
formData.append('category', 1);
formData.append('status', 'Y')
formData.append('image', this.imageName);
this.http.post("http://192.168.22.4/api-imageUpload", formData, {headers: headers}).subscribe(res => {
let status = res['status'];
if(status == 201){
var message = "The image was successfully uploaded!";
this.showAlert(message);
}else{
var message = "upload error";
this.showAlert(message);
}
}, (err) => {
var message = "Error in uploading file " + err;
this.showAlert(message);
});
In order to inspect what's ongoing with the request:
from rest_framework.exceptions import ValidationError
class ImageDetailsViewSet(generics.ListCreateAPIView):
queryset = ImageDetails.objects.all()
serializer_class = ImageDetailsSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if not serializer.is_valid():
print(serializer.errors) # or better use logging if it's configured
raise ValidationError(serialize.errors)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
Even without Base64 it is possible using ionic native components for file-transfer and image-picker see here: https://gist.github.com/AndreasDickow/9d5fcd2c608b4726d16dda37cc880a7b