Image and File Uploads
New in 1.1 Drag and drop images or files into a form
This example shows how to upload an image or file to the server using AngularJS with Ajax.
How does it work?
If we want to upload an image or file to the server through an Ajax request, we have to divide
the submission into two steps. This is because browsers can not serialize submitted file payload to
JSON. Instead, we first must upload the image or file to the server encoded as
multipart/form-data
. The server then creates a temporary copy of the uploaded image
or file and returns a reference to it. This temporary reference is stored inside a hidden field
of our form.
When the complete form is submitted by the client, only that reference to the temporary file
will be sent through Ajax. The server then moves the previously uploaded copy of that file into
its MEDIA_ROOT
directory and stores its location inside a model field.
from djng.forms import fields, NgModelFormMixin, NgFormValidationMixin
from djng.styling.bootstrap3.forms import Bootstrap3Form
class SubscribeForm(NgModelFormMixin, NgFormValidationMixin, Bootstrap3Form):
scope_prefix = 'subscribe_data'
form_name = 'my_form'
use_required_attribute = False
full_name = fields.CharField(
label='Full name',
min_length=3,
max_length=99,
required=True,
)
avatar = fields.ImageField(
label='Photo of yourself',
required=True,
)
permit = fields.FileField(
label='Your permit as PDF',
accept='application/pdf',
required=False,
)
def clean_avatar(self):
"""
For instance, here you can move the temporary file stored in
`self.cleaned_data['avatar'].file` to a permanent location.
"""
self.cleaned_data['avatar'].file
import json
from django.http import JsonResponse
from django.core.urlresolvers import reverse_lazy
from django.utils.encoding import force_text
from django.views.generic.edit import FormView
class SubscribeView(FormView):
template_name = 'image-file-upload.html'
form_class = SubscribeForm
success_url = reverse_lazy('form_data_valid')
def post(self, request, **kwargs):
assert request.is_ajax()
return self.ajax(request)
def ajax(self, request):
request_data = json.loads(request.body)
form = self.form_class(data=request_data.get(self.form_class.scope_prefix, {}))
if form.is_valid():
return JsonResponse({'success_url': force_text(self.success_url)})
else:
return JsonResponse({form.form_name: form.errors}, status=422)
<script type="text/javascript">
angular.module('djangular-demo', ['djng.forms', 'djng.urls', 'djng.fileupload']).config(function($httpProvider) {
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
$httpProvider.defaults.headers.common['X-CSRFToken'] = '{{ csrf_token }}';
});
</script>
<form name="{{ form.form_name }}" djng-endpoint="." ng-model-options="{allowInvalid: true}" novalidate>
{% csrf_token %}
{{ form.as_div }}
<button type="button" ng-click="do(update()).then(redirectTo())" ng-disabled="isDisabled()">Submit</button>
</form>
from django.db import models
from djng.forms import NgModelFormMixin, NgFormValidationMixin
from djng.styling.bootstrap3.forms import Bootstrap3ModelForm
class SubscribeUser(models.Model):
full_name = models.CharField(
"Full name",
max_length=99)
avatar = models.ImageField("Avatar", blank=False, null=True)
permit = models.FileField("Permit", blank=True, null=True)
class SubscribeForm(NgModelFormMixin, NgFormValidationMixin, Bootstrap3ModelForm):
use_required_attribute = False
scope_prefix = 'subscribe_data'
form_name = 'my_form'
class Meta:
model = SubscribeUser
fields = ['full_name', 'avatar', 'permit']
A form accepting files and images without a permanent storage does not make
much sense. Therefore you normally would create a Django model using an django.models.ImageField
and/or django.models.FileField
. From this model, you can create a form inheriting from
django.forms.ModelForm
, as usual. Image and file fields then are replaced by their
AngularJS enabled counterparts, enabling drag & drop and immediate image preview.
A sample implementation is shown in the last tab labeled Model.