Django ajax image upload and cropped image saving - python

I want to upload image with ajax, crop the uploaded image and save the image.
I uses pillow 5.1.0., django 2.0.5.
models.py:
class TestPhoto(models.Model):
file = models.ImageField()
forms.py:
class TestPhotoFrom(forms.ModelForm):
# this is not necessary part, you can ignore it.
x = forms.FloatField(widget=forms.HiddenInput())
y = forms.FloatField(widget=forms.HiddenInput())
class Meta:
model = TestPhoto
fields = ('file', 'x', 'y',)
template.html:
<form method="post" enctype="multipart/form-data" id="formUpload">
{% csrf_token %}
{{ form }}
</form>
<button class="js-crop-and-upload">button</button>
<script>
$(function () {
$(".js-crop-and-upload").click(function () {
//Get file from form.
var form_upload = $("#formUpload")[0];
var form_data = new FormData(form_upload);
//Send file with ajax.
$.ajax({
url:'/crop/',
type:'post',
dataType:'json',
cache:false,
processData: false,
contentType: false,
data:{
'file': form_data,
'value': 'test_value',
},
success:function (data) {
console.log(data)
}
});
});
});
</script>
views.py:
def crop(request):
if request.method == "GET":
form = TestPhotoFrom()
return render(request, 'authapp/crop.html', {'form': form})
else:
if request.is_ajax():
# get file from request.
file = request.FILES
image = Image.open(file)
# cropping image
cropped_image = image.crop((0, 0, 200, 200))
resized_image = cropped_image.resize((200, 200), Image.ANTIALIAS)
# save cropped image
resized_image.save()
return JsonResponse({'success': 'file_uploaded'})
I read this: https://simpleisbetterthancomplex.com/tutorial/2017/03/02/how-to-crop-images-in-a-django-application.html
And now I need to do that with jquery-ajax.
But when I click button to do ajax request, server console is printing this error:
'MultiValueDict' object has no attribute 'read'
How can I do that?
Sorry, I don't know also whether these part on views.py is correct or not:
image = Image.open(file)
cropped_image = image.crop((0, 0, 200, 200))
resized_image = cropped_image.resize((200, 200), Image.ANTIALIAS)
resized_image.save()
But before checking it, I cannot solve this: 'MultiValueDict' object has no attribute 'read'
Question.1:
How can I solve 'MultiValueDict' object has no attribute 'read'?
Question.2:
Is this part correct? Or will it be working well?
image = Image.open(file)
cropped_image = image.crop((0, 0, 200, 200))
resized_image = cropped_image.resize((200, 200), Image.ANTIALIAS)
resized_image.save()
Because I'm very newbie in uploading image with ajax on django very little, I want your explanation or modification.
Thanks for reading poor question.

I can only give you an answer for the ajax side
When using FormData, you have to pass it as the data parameter.
If you want to add other fields you will use append()
#formUpload would be the form with the file input used to select the image.
$(".js-crop-and-upload").click(function () {
//Get file from form.
var form_upload = $("#formUpload")[0];
var form_data = new FormData(form_upload);
form_data.append('value', 'test_value');
//Send file with ajax.
$.ajax({
url:'/crop/',
type:'post',
dataType:'json',
cache:false,
processData: false,
contentType: false,
data:form_data,
success:function (data) {
console.log(data)
}
});
});

Related

Saving image captured from webcam

I'm building a Django project in which I need to take a picture from webcam, and then store it in my database and as a file. I'm saving the source in the database, but I'm having some trouble saving it as a file.
Here is my code:
html:
<form method="POST" action="{% url 'takePhoto' %}" enctype="multipart/form-data">
{% csrf_token %}
<video id="video" autoplay ></video>
<canvas id="canvas"></canvas>
<input type="hidden" name="photo" id="photo" value=""/>
<button id="startbutton1" class="btn btn-outline-secondary btn-sm">Take Photo</button>
<button id="submit" type="submit">submit</button>
<script src="{% static 'scripts/script.js' %}"></script>
javascript:
(function() {
var width = 320;
var height = 0;
var streaming = false;
var video = null;
var canvas = null;
var photo = null;
var startbutton1 = null;
function startup() {
video = document.getElementById('video');
canvas = document.getElementById('canvas');
photo = document.getElementById('photo');
startbutton1 = document.getElementById('startbutton1');
navigator.mediaDevices.getUserMedia({video: true, audio: false})
.then(function(stream) {
video.srcObject = stream;
video.play();
})
.catch(function(err) {
console.log("An error occurred: " + err);
});
video.addEventListener('canplay', function(ev){
if (!streaming) {
height = video.videoHeight / (video.videoWidth/width);
if (isNaN(height)) {
height = width / (4/3);
}
video.setAttribute('width', width);
video.setAttribute('height', height);
canvas.setAttribute('width', width);
canvas.setAttribute('height', height);
streaming = true;
}
}, false);
startbutton1.addEventListener('click', function(ev){
takepicture();
ev.preventDefault();
}, false);
clearphoto();
}
function clearphoto() {
var context = canvas.getContext('2d');
context.fillStyle = "#AAA";
context.fillRect(0, 0, canvas.width, canvas.height);
var data = canvas.toDataURL('image/png');
photo.setAttribute('src', data);
}
function takepicture() {
var context = canvas.getContext('2d');
if (width && height) {
canvas.width = width;
canvas.height = height;
context.drawImage(video, 0, 0, width, height);
var data = canvas.toDataURL('image/png');
photo.value=data;
} else {
clearphoto();
}
}
window.addEventListener('load', startup, false);
})();
views:
def takePhoto(request):
print("works")
if not request.session.get('logged_in'):
return redirect('/appChallenge/login')
if request.method== 'POST':
user = User.objects.get(username=request.session["username"])
img = request.POST.get("photo")
image = img
imagePath="/media"
a=str(img)
image = image.save(f"{imagePath}/{a}.png")
imgInfo= Image(user=user, img=img)
imgInfo.save()
print("works")
return render(request, 'page.html')
When I click submit, it says "'str' object has no attribute 'save'"
Please help. Thanks.
If your js/html code works, you will receive a raw image data encoded to base64 in your view 'takePhoto'. Try
print(img) to see something like "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAADwCAYAA...."
Maybe you will see just "iVBORw0KGgoAAAANSUhEUgAAAUAAAADwCAYAA...." without meta data.
So your 'img' is just a string with base64 encoded value, that hasn't method 'save'.
First, you need to get rid of "data:image/png;base64," in your raw image data, if your image string contains it. It is meta data and base64 could not decode it. You can do it at front end side:
var data = canvas.toDataURL('image/png').replace(/^data:image\/png;base64,/, "")
Or back end:
img = request.POST.get("photo").replace('data:image/png;base64,', '')
Next you need to use django ContentFile to create a file-like object. You need to simulate file with its method .read(). Also you need to decode base64 encoded image data:
import base64
from django.core.files.base import ContentFile
def takePhoto(request):
print("works")
if not request.session.get('logged_in'):
return redirect('/appChallenge/login')
if request.method== 'POST':
user = request.user
# remove meta data from base64 encoded data, you can also
# use 'split(',')[1]' to remove all before ','
img = request.POST.get("photo").replace('data:image/png;base64,', '')
# create a file-like object with your image data
image_file_like = ContentFile(base64.b64decode(img))
# first you need to create object
image = Image(user=user)
# and then save image into your model object
# note: only if your 'img' field is 'ImageField' or similar
image.img.save("filename.png", image_file_like)
image.save()
print("works")
return render(request, 'page.html')
P.S:
You don't need to add 'media' prefix in your image file name while saving it.
You should add MEDIA_ROOT variable in your settings.py like this:
BASE_DIR = Path(__file__).resolve().parent.parent # or manually set the path of your project
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
And all files automatically will be saved in your project media directory.

Django - How to get checked objects in template checkboxes?

Im new in Django,
I'm using xhtml2pdf for rendering data objects from template to PDF file , and i want to allow users to render only checked objects by checkboxes into pdf, is there anyway to filter that in Django ?
Thanks
views.py :
def render_to_pdf(template_src, context_dict={}):
template = get_template(template_src)
html = template.render(context_dict)
result = BytesIO()
pdf = pisa.pisaDocument(BytesIO(html.encode('ISO-8859-1')), result, link_callback=link_callback)
if pdf.err:
return HttpResponse('Error')
return HttpResponse(result.getvalue(), content_type='application/pdf')
class ViewPDF(View):
#method_decorator(login_required(login_url='livraison:login_page'))
def get(self, request, *args, **kwargs):
checked_objects = [i dont know how to get them]
queryset = Livraison.objects.filter(id__in=[checked_objects]) # i want something does that
data = {
'livraisons' : queryset,
'now' : f'Livraisons-{date.today()}'
}
pdf = render_to_pdf('pdf_template.html', data)
return HttpResponse(pdf, content_type='application/pdf')
use forms to get user data.
class LivraisonSelectForm(forms.Form):
livraison = forms.ModelChoiceField(label='select', queryset=Livraison.objects.all(), widget=forms.CheckboxInput())
then use it in your view:
class ViewPDF(FormView):
form_class = LivraisonSelectForm
template_name = 'path_to_template_with_html_to_filter_data'
#method_decorator(login_required(login_url='livraison:login_page'))
def form_valid(self, form):
checked_objects = form.cleaned_data.get('livraison', [])
queryset = Livraison.objects.filter(id__in=checked_objects) # i want something does that
data = {
'livraisons' : queryset,
'now' : f'Livraisons-{date.today()}'
}
pdf = render_to_pdf('pdf_template.html', data)
return HttpResponse(pdf, content_type='application/pdf')
and don't forget to show this form in your html {{ form }}
**
UPDATE
**
I Found a solution , i used Ajax code to collect every selected object
// check selected objects (#pdf is the id of the button which gonna generate the pdf file)
$('#pdf').click(function () {
var id = [];
$(':checkbox:checked').each(function (i) {
id[i] = $(this).val()
})
if (id.length === 0) {
alert('Please Select something..');
} else {
$.ajax({
url: '.',
method: 'GET',
data: {
id,
},
success: function (response) {
console.log('PDF is ready')
}
})
}
});
then i ad the variable into the view function to collect the selected objects and filter the queryset
myFunction.selected_ones = request.GET.getlist('id[]')
then filter queryset in the generated_pdf_func
queryset = MODEL.objects.filter(id__in=[x for x in myFunction.selected_ones if x != '0'])
NOTE : <if x != '0'> is important in case you want to select all because it returns 0 and you don't have any id = 0 in the DB

'Method not allowed' while making webpage in flask

I'm writing flask api using keras. However I get a lot of errors. One of them is Error 405 - method not allowed.
POST http://0.0.0.0:5000/static/predict 405 (METHOD NOT ALLOWED) jquery-3.3.1.min.js
I'm trying to get predictions written on the page, but they didn't show even before that error 405.
I don't know which place can lead to that error.
Here is code:
predict.html
<body>
<input id="image-selector" type="file">
<button id="predict-button"> Predict</button>
<p style="font-weight:bold">Predictions</p>
<p> Jablko <span id="apple-prediction"></span></p>
<p> Banan <span id="banana-prediction"></span></p>
<img id="selected-image" src=""/>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
let base64Image;
$("#image-selector").change(function(){
let reader = new FileReader();
reader.onload = function(e){
let dataURL = reader.result;
$('#selected-image').attr("src", dataURL);
base64Image = dataURL.replace("data:image/jpg;base64,", "");
//console.log(base64Image);
}
reader.readAsDataURL($("#image-selector")[0].files[0]);
$("#apple-prediction").text("");
$("#banana-prediction").text("");
});
$("#predict-button").click(function(event){
let message = {
image:base64Image
}
//console.log(message);
$.post("http://0.0.0.0:5000/static/predict", function(response){
$("#apple-prediction").text(response.prediction.apple.toFixed(6));
$("#banana-prediction").text(response.prediction.banana.toFixed(6));
console.log(response);
});
});
</script>
</body>
predict.py
app = Flask(__name__)
def get_model():
global model
model=load_model('fc_model1.h5')
#model.load_weights('model_grocery.h5')
#graph = tf.get_default_graph
print("** Model loaded!")
def preprocess_image(image, target_size):
image = image.resize(target_size)
image = image.img_to_array(image)
image = np.expand_dims(image, axis=0)
return image
print("**Loading model**")
get_model()
#app.route("/predict", methods=["POST"])
def predict():
message = request.get_json(force=True)
encoded = message['image']
decoded = base64.b64decode(encoded)
image = Image.open(io.BytesIO(decoded))
processed_image = preprocess_image(image, target_size=(224, 224))
#bt_prediction = vgg16.predict(processed_image)
prediction = model.predict(processed_image).tolist()
response = {
'prediction': {
'apple': prediction[0][0],
'banana': prediction[0][1]
}
}
return jsonify(response)
The error shows in google-chrome.
Your JS code has
$.post("http://0.0.0.0:5000/static/predict")
but your Flask route is
#app.route("/predict", methods=["POST"])
def predict():
Because the snippet you posted doesn't show that you're prepending /static/ to all routes, it looks like that's a mistake.
You specified methods=['POST'] correctly, so visiting 127.0.0.1:5000/predict should yield the expected result.
If you wanted to check 0.0.0.0:5000/predict, you need to add app.run(host='0.0.0.0')(See: Configure Flask dev server to be visible across the network).

Django - Filtering objects using ajax post call data returns nothing

I have the following Ajax POST call:
$.ajax({
type: "POST",
url: "{% url 'meds:prescription' %}",
data: {selected:'selected' , csrfmiddlewaretoken: "{{ csrf_token }}"},
success: function(result) {
window.location = "{% url 'meds:prescription' %}";
}
});
Where selected is an array of ids for example [5, 9, 17]
And the following view:
class PrescriptionView(generic.ListView):
template_name = 'meds/prescription.html'
context_object_name = 'meds'
model = Medicament
def post(self, request, **kwargs):
selected_ids = self.request.POST.getlist('selected[]')
meds = self.get_queryset().filter(id__in=selected_ids)
return render(request, self.template_name, {'meds': meds})
def get_queryset(self):
ids = self.request.POST.getlist('selected[]')
return Medicament.objects.filter(id__in=ids)
def get_context_data(self, **kwargs):
ids = self.request.POST.getlist('selected[]')
context = super(PrescriptionView, self).get_context_data(**kwargs)
context.update({
'meds': Medicament.objects.filter(id__in=ids),
'date': datetime.now()
})
return context
What I'm trying to do is just to be redirected to the prescription template with the objects filtered using the data from the post call, but instead my template is just empty, I'm not sure what I'm doing wrong..
You are sending a string selected:'selected' not an array. Remove single quotes around selected in your ajax handler:
data: {selected:'selected' , csrfmiddlewaretoken: "{{ csrf_token }}"},
should be:
data: {selected: selected, csrfmiddlewaretoken: "{{ csrf_token }}"},

Likes feature in Django

I'm trying to implement a Like feature for my web app. Here is the code:
Model:
class Like(models.Model):
user = models.ManyToManyField(User, related_name='likes')
doctor = models.ForeignKey(Doctor)
date = models.DateTimeField(auto_now_add=True)
total_likes = models.IntegerField(default=0)
View:
def like(request):
vars = {}
if request.method == 'POST':
user = request.user
slug = request.POST.get('slug', None)
doctor = get_object_or_404(Doctor, slug=slug)
liked, created = Like.objects.create(Doctor=doctor)
try:
user_liked = Like.objects.get(Doctor=doctor, user=user)
except:
user_liked = None
if user_liked:
user_liked.total_likes -= 1
liked.user.remove(request.user)
user_liked.save()
else:
liked.user.add(request.user)
liked.total_likes += 1
liked.save()
return HttpResponse(simplejson.dumps(vars),
mimetype='application/javascript')
URL:
url(r'^like/(?P<id>\d+)/$', views.like, name='like'),
Template:
<input type="button" id="like" name="{{doctor_slug}}" value="Like" />
<script>
$('#like').click(function(){
$.ajax({
type: "POST",
url: "{% url like %}",
data: {'slug': $(this).attr('name'), 'csrfmiddlewaretoken': '{{csrf_token}}'},
dataType: "text",
success: function(response) {
alert('You liked this')
},
error: function(rs, e) {
alert(rs.responseText);
}
});
})
</script>
When I open my template page, I get the error NoReverseMatch at /docprofile/1/
'url' requires a non-empty first argument. The syntax changed in Django 1.5, see the docs. I looked at the docs but couldn't find anything that I could use here. The problem seems to be in the template at "url: "{% url like %}",". This is inside Ajax.
As the error says, you should see the docs. You need to pass the path to the view function or the name of the url to the url template tag as a string.
<script>
$('#like').click(function(){
$.ajax({
type: "POST",
url: "{% url 'like' %}",
data: {'slug': $(this).attr('name'), 'csrfmiddlewaretoken': '{{csrf_token}}'},
dataType: "text",
success: function(response) {
alert('You liked this')
},
error: function(rs, e) {
alert(rs.responseText);
}
});
})
</script>

Categories

Resources