Uploading Canvas (web camera snap) to Django Server - javascript

I need to take a snap on the client's side with JS [done in 1.] and send it to my server [trial in 2.].
By now, I can send images to the server (media/images/), using the auto-generated button from Django through forms/models and views, which allows a user to choose a pic from his own computer.
The thing here is that I don't want to rely on this process - that forces the user to select an image from his computer. Instead, I want that when the user takes a snap, the program sends it to the server automatically, and the server stores it in a folder.
I am new to Django, so I would really appreciate it if you could provide some code in your answers.
player1.html
<video id="video"></video>
<canvas id="canvas"></canvas><br>
<button onclick="snap();">Snap</button>
JS
const video = document.getElementById('video');
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
navigator.mediaDevices.getUserMedia({video: true}) // request cam
.then(stream => {
video.srcObject = stream; // don't use createObjectURL(MediaStream)
return video.play(); // returns a Promise
})
function snap () {
canvas.width = video.clientWidth;
canvas.height = video.clientHeight;
context.drawImage(video, 0, 0); // This draws the captured image on the canvas
}
player1.html
<div>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.media }}
{{ form.as_p }}
<button id ='gg'>Upload</button>
</form>
</div>
JS
function snap () {
canvas.width = video.clientWidth;
canvas.height = video.clientHeight;
context.drawImage(video, 0, 0); // This draws the captured image on the canvas
console.log('canvas: ', canvas.toDataURL("image/jpeg"))
var formData = new FormData();
formData.append('csrfmiddlewaretoken', '{{ csrf_token }}');
formData.append("header_image", canvas.toDataURL("image/jpeg"));
var xhr = new XMLHttpRequest();
xhr.open( "POST", "http://127.0.0.1:8000/player1");
xhr.send(formData);
}
views.py
...
from encodings.base64_codec import base64_decode
class AddPostView(CreateView):
model = Post
form_class = PostForm
template_name = 'player1.html'
# fields = '__all__'
#def convert(self): I think I need something like this, but well written, to decode the image in here.. how should I write it, tho?
# print('here')
# base64_decode(self.object)
# print('hmm:', base64_decode(self.object))
urls.py
urlpatterns = [
path('', views.index, name='index'),
path('player1', AddPostView.as_view(), name='player1'),
]
models.py
class Post(models.Model):
header_image = models.ImageField(null=True, blank=True, upload_to="images/")
def get_absolute_url(self):
return reverse('player1')
forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('header_image',) # these fields came from models.py
[Solution? B: LAST HOPE] - I also read from people with the same problem that they added the image on the database, using PHP, etc. So if I do that, how can I then grab it to manipulate it on views...?
If someone knows going on that way, all good by me!

Related

How to manage JSON data in javascript pass form django model?

I need to dynamically display my audio on my website. The audio is a file path stored in the Django model.
Djanog Model:
class audio(models.Model):
instrumentId = models.OneToOneField(instrument, on_delete=models.CASCADE, primary_key=True)
location = models.FileField()
I pass the data through view.py into json data so my script can read the data.
view.py, PostJsonListView fetch data form the model and generate json data, testView connect to the html displaying audios
class PostJsonListView(View):
def get(self, *args, **kwargs):
print(kwargs)
#upper is to get the number from the js file to set a upper boundry
upper = kwargs.get('num_aduios') #intial state be 3
lower = upper - 3
#pass in the audio in to the list [lower:upper] use to set boundry
audios = list(audio.objects.values()[lower:upper])
#comfirmation of no more audi to load
audio_size = len(audio.objects.values())
size = True if upper >= audio_size else False
return JsonResponse({'data':audios, 'max': size}, safe=False)
class testView(TemplateView):
template_name = 'frontend/testView.html'
JSON data in browser
Javascript that hand the data, after a button click, it would display three more data
console.log('HELLO')
const aduiosBox = document.getElementById('audio-box') //get the div audio-box
const nextBtn = document.getElementById('next-btn')
let visible = 3
const handleGetData = () =>{
$.ajax({
type: 'GET',
url: `/json/${visible}/`,
success: function(response)
{
max_size = response.max
//getting all the data and display in the console
const data = response.data
data.map(post=>{
console.log(post)
aduiosBox.innerHTML +=
//how to get the location right?
`
<div class="card p-3 mt-3 mb-3">
<audio controls>
<source src= ${post.location} type="audio/wav">
</audio>
</div>
`
})
//check if the display size reach maxium
if(max_size){
console.log('done')
}
},
error:function(error){
console.log(error)
}
})
}
handleGetData()
//event listenrs
nextBtn.addEventListener('click', ()=>{
visible += 3
handleGetData()
})
Console on the browser
Here, the audio path was from home/sound1.wav. From my another exmaple, where I also path audio object, has the path of /sounds/sounds1.wav
view.py for the working example
def survey(request):
if request.method == 'POST': #if something is post to the server, reference to HTML form method == post
form = rateForm(request.POST)
if form.is_valid():
form.save()
form = rateForm()
wavFile = audio.objects.all()
return render(request, "frontend/audio.html",{
'wavFile': wavFile,
'form': form,
})
Working example
File path of my Django app
In conclusion, I belive the website cannot get to the file path correctly. home/sound1.wav VS sounds/sound1.wav. How can I manage it so the Javascript code can get to the correct path to get the audio to display in the website?
I solved it by correcting the directory of my audio file. For anyone that is wondering the something, you should look at what have done to your setting.py for the directory of storing data you wanted to access

Django 3. Having trouble passing dropdown menu selection from .html to forms

I'm still new to Django (2 weeks or so). I've been struggling the past few days with passing a string from an html file to forms. My project lets the user choose a state from a dropdown menu (Michigan and Ohio for now, I'll add the rest later). When the state is selected, it will take that string and pull a list of counties of that state from a spreadsheet. This is where the problem lies. I've searched far and wide and I just can't seem to find a solution. The major holdback to many of these solutions is I don't want to "submit" with a button. I want the user to "select" a state, then select a county without a page refresh. I've also included a screenshot of the webpage. So far the dependent dropdowns work perfectly thanks to a youtube tutorial. The "submit" button in the picture is cosmetic for now.
Thanks in advance for helping out. Let me know if you have any questions regarding models or anything else regarding the code.
views.py
def StateForm_Page(request):
context = {}
stateChoice = 'Michigan' //hardcode a state so if the post fails, the function can still find an excel sheet
if request.method == 'POST':
State_Form = StateForm(request.POST)
stateChoice = State_Form.cleaned_data['stateChoice'] //I think this is where my code is failing
else:
State_Form = StateForm()
context['State_Form'] = State_Form
dataArray = pd.read_excel(r'C:\filename.xls', sheet_name= stateChoice)
county_strings = dataArray['County '].values.tolist()
json_county_strings = json.dumps(county_strings)
context['json_county_strings'] = json_county_strings
return render(request, 'StateForm_page.html', context)
StateForm_page.html
<body>
<form action="" method="POST" name="stateChoice">
{% csrf_token %}
{{ State_Form.as_p }}
</form>
<script>
var state;
var county;
$(document).ready(function(){
$('#id_county').empty(); //empties county before state is chosen
$("#id_state").on('change', function(){ //when #id_state is changed...
state = $("#id_state").val(); //assign state with the selection
var countyStrings = JSON.parse('{{ json_county_strings | escapejs }}'); //grabs counties from respective state
var length = countyStrings.length;
var i;
for(i=0; i < length; i++){
county = countyStrings[i]; //update county options with spreadsheet values
$('#id_county').append(
`
<option value ="${county}">
${county}
</option>
`
);
}
});
})
}
</script>
</body>
What the webpage looks like so far:
Next day changes
Hey, after putting a few more hours into it, I'm still not having every luck. Per your suggestions, below is what I've added
views.py
def retrieveState(request):
statePick = request.GET.get('state')
return JsonResponse(statePick, safe = False)
def StateForm_Page(request):
context = {}
stateChoice = []
if request.method == 'POST':
#stateChoice = State_Form.cleaned_data['stateChoice']
State_Form = StateForm(request.POST)
stateChoice = retrieveState(request)
else:
stateChoice = 'Michigan'
State_Form = StateForm()
StateForm_Page.html
$.ajax({
type: 'POST',
url: 'ajax/retrieveState/',
data: state,
dataType: 'json',
});
Good news is something is being triggered on the ajax url I added, but I don't think the function in views is retrieving the ajax data. Any suggestions? Thanks for your help!

How to send a data URI collected via javascript to a Django Model field?

I want to use the signature_pad from https://github.com/szimek/signature_pad to attach an image to a Django Form that gets saved to a server. With the code I have, I can successfully download the image from the signature pad as well as upload an image to the server with a link to the image on the database via Django’s ImageField. However, I can’t get the signature image sent to the server.

I think the two remaining things I need to be able to do are:
Send the image to the signature field form.
Encode the image so I can save it to a server.
I’ve got a couple other non-essential questions: is the image data currently saved in signaturePad.toDataURL until it’s submitted? I don’t know enough to have a preference as to whether the image is sent to the server as a Data URI string or a link to a .PNG file. I imagine a string is easier to deal with down the road, correct?
Here's my models.py
from django.db import models
class testModel(models.Model):
name = models.CharField(max_length=50)
image = models.ImageField(upload_to='signatures/')
signature = models.FileField()
forms.py:
from django import forms
from .models import testModel
class testForm(forms.ModelForm):
class Meta:
model = testModel
fields = '__all__'
widgets = {'signature': forms.HiddenInput()}
views.py:
from django.shortcuts import render
from django.urls import reverse
from django.http import HttpResponse, HttpResponseRedirect
from .Forms import testForm
from .Models import testModel
def testFormPage(request):
if request.method == 'POST':
form = testForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponseRedirect('/thanks/')
else:
form = testForm()
return render(request, 'testFormPage.html', {'form': form})
and the the HTML form:
<form action="{% url 'testFormPage' %}" method="post" enctype="multipart/form-data">
<div id="signature-pad" class="signature-pad">
<div class="signature-pad--body">
<canvas></canvas>
<button type="button" class="button save" data-action="save-png">Save as PNG</button>
</div>
</div>
<input type="hidden" name="signature" value="formSig.value" id="formSig">
<input type="submit" value="Submit">
</form>
Rather than copying the entire js app (https://github.com/szimek/signature_pad/tree/master/docs/js), here are the lines I added. I don't really know javascript so I've just tried to Frankenstein together what I've copied here and there.
var savePNGButton = wrapper.querySelector("[data-action=save-png]");
var formSig = wrapper.querySelector("[data-action=formSig]");
// and...
savePNGButton.addEventListener("click", function (event) {
if (signaturePad.isEmpty()) {
alert("Please provide a signature first.");
} else {
var dataURL = signaturePad.toDataURL();
download(dataURL, "signature.png");
}
});
formSig.addEventListener("submit", function(event) {
if (signaturePad.isEmpty()) {
alert("Please provide a signature first.");
} else {
signature.value = signaturePad.toDataURL();
};

Display intro video first visit only Django

I have a Django app that needs to display a video that gives an into to the website. I only want it to do this on the initial visit as opposed to every time the user refreshes. I feel like sessions would have something to do with this but I’m not sure. Thanks!
I think its best to put this flag directly in your database. You can put a field in your user model(if you are using custom user) or in a model which has OneToOne relation with User. For example:
class Profile(models.Model):
user = models.OneToOneField(User)
has_seen_intro = models.BooleanField(default=False)
And send this information to Template from view like this, for example:
class HomeView(TemplateView):
template_name = 'home.html'
def get_context_data(self, **kwargs):
context = super(HomeView, self).get_context_data(**kwargs)
profile = self.request.user.profile
if not profile.has_seen_intro:
context['show_intro'] = True
profile.has_seen_intro = False
profile.save()
# or use user.has_seen_intro if you have custom model
return context
And update the template like this
{% if show_intro %}
// intro video codes
{% endif %}
Update
for anonymous user, please try like this:
class HomeView(TemplateView):
template_name = 'home.html'
def get_context_data(self, **kwargs):
context = super(HomeView, self).get_context_data(**kwargs)
if self.request.user.is_authenticated:
profile = self.request.user.profile
if not profile.has_seen_intro:
context['show_intro'] = True
profile.has_seen_intro = False
profile.save()
else:
if not self.request.session.get('has_seen_intro', True):
self.request.session['has_seen_intro'] = False
context['show_intro'] = True
return context

How to upload multiple images in Django using Dropzone for multiple image fields

I am working on a project where the functionality is the user can upload his multiple images with a drag-n-drop feature. I am developing using the Django-python. I have implemented the functionality of drag-n-drop in django template, but I am getting error for images while submitting the form data.
My Html template code is :
<form id="newUserForm" name="newUserForm" data-abide action="{% url 'saveNewUserInfo'%}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="section"></div>
some input fields
<!-- The div for uploading the images -->
<div class="dropzone" style="border: 1px solid red;"></div>
<input type="submit" value="save">
</form>
I am using a dropzone.js for implementing the drag-drop-and sortable
The error is coming as MultiValueDictKeyError at /saveNewUserInfo/, Exception Value: "'file'"
My Model is :
class CustomerProfile(models.Model):
customer_id = models.CharField(db_column='customer_id', primary_key=True, max_length=20)
first_name = models.CharField(db_column='first_name', max_length=30, blank=True, null=True)
last_name = models.CharField(db_column='last_name', max_length=30,blank=True,null=True)
user_name = models.CharField(db_column='user_name', max_length=50,unique=True)
phone_number = models.CharField(db_column='phone_number', max_length=15,blank=True,null=True)
email_id = models.EmailField(db_column='email_id', max_length=50,blank=True, null=True)
user_image1 = models.ImageField(upload_to=IMAGES_PATH, db_column='user_image1', max_length=100)
user_image2 = models.ImageField(upload_to=IMAGES_PATH, db_column='user_image2', max_length=100)
user_image3 = models.ImageField(upload_to=IMAGES_PATH, db_column='user_image3', max_length=100)
user_image4 = models.ImageField(upload_to=IMAGES_PATH, db_column='user_image4', max_length=100)
user_image5 = models.ImageField(upload_to=IMAGES_PATH, db_column='user_image5', max_length=100)
forms.py
class CustomerInfoForm(forms.ModelForm):
class Meta:
model = CustomerProfile
Please suggest how to store the dropzone multiple images into these image fields. Appreciate for suggestions..
I am glad that you have solved it. I have spent a few hours on this this is how I solved it:
The main issue with using dropzone is that as soon as files being droped in it, it will start to upload. So the images will not upload along with the rest of the form data.
To deal with this, I had to create the dropzone object programmatically with the following settings:
$(document).ready(function(){
var list_of_files = new Array();
Dropzone.autoDiscover = false; //prevent dropzone to automatically discover the dropzone object in your html
$("div#dropzone").dropzone({
uploadMultiple: true, // allow multiple upload
autoProcessQueue: false, // prevent dropzone from uploading automatically
url: "/", //dropzone needs a url attribute or it complains, what value you put here does not really matter. It is only purpose is to prevent a javascript error message from chrome console
maxFiles: 5, //set max uploads to 5 since you only have 5 image files in your model
init: function(){
//everytime a file is uploaded, save the file object
//for later use
this.on("addedfile", function(file)
{
if (list_of_files.length < 5)
{
list_of_files.push(file)
console.log("file added");
}
});
}
});
// the following function override the "submit" button in the form
$(document).on("click", "button", function(e){
e.preventDefault() //prevent the form from submitting
console.log('num of files: ' + list_of_files.length);
var formData = new FormData(); // construct our own upload data
var inputs = $("#newUserForm input");
//get all of the data from textboxes
$.each(inputs, function(obj, v){
var name = $(v).attr("name")
var val = $(v).val();
console.log('name: ' + name + ' value: ' + val);
formData.append(name, val);
});
//get the file object from dropzone and put it into our formdata
for(i=0;i<list_of_files.length;i++)
{
formData.append('user_image'+(i+1), list_of_files[i]);
}
var request = new XMLHttpRequest();
request.open("POST", "/"); //config your post url here
request.send(formData); //send the post request to server
});
});
Here is the template file:
<form id="newUserForm" name="newUserForm" method="post" enctype="multipart/form-data">
{% csrf_token %}
{% if form %}
{% for field in form %}
<p>{{ field.label_tag }} {{ field }}</p>
{% endfor %}
{% endif %}
<!-- The div for uploading the images -->
<div id="dropzone" class="dropzone"></div>
<button id='save'> save </button>
</form>
I also added exclude to forms.py (so that these fields will not show up in our template, we have dropzone to replace them):
class CustomerInfoForm(forms.ModelForm):
class Meta:
model = CustomerProfile
exclude=('user_image1','user_image2','user_image3','user_image4','user_image5')
All of the code above does is to submit the data from each text box with the images to your views.py together in one step
Here is the views.py:
def index(request):
if request.method == 'POST':
form = CustomerInfoForm(request.POST)
if (form.is_valid()):
instance = form.save(commit=False)
#request.FILES contains all of the uploaded images
#key is 'user_image1', 'user_image2', value is the image file in memory
for key, value in request.FILES.iteritems():
a_path = '/a/b'
save_uploadfile_to_disk(a_path, file)
setattr(instance, key, a_path) //I made up the path here
form.save() //save the form for real
#do not forget to return a response
else:
print form.errors #for debugging only
else:
form = CustomerInfoForm()
context = {'form': form}
return render(request, 'test_dropzone/index.html', context)
def save_uploadfile_to_disk(full_path, file):
with open(full_path, 'w+') as destination:
for chunk in file.chunks():
destination.write(chunk)
I tested this solution using Django 1.8 and it works. I checked the database and the path has been written to the record correctly.
Now, to reflect upon this solution, it kind of defeated the purpose of using dropzone. Because users cannot upload the photos as soon as a file has been selected.
Since you have also solved this problem. Please post your solution and I am looking forward to learn something new from yours :)
Small upgrade on previous post overriding submit, I would like to add options:selected looping.
$('option:selected').each(function(){
var name = $(this).parent().attr('name')
if ($(this).val()) {
var val = $(this).val()
console.log('name: ' + name + ' value: ' + val);
formData.append(name, val);
}
else {
var val = ""
console.log('name: ' + name + ' value: ' + val);
formData.append(name, val);
}
});

Categories