Saving a model containing a FileField in Django - django

I have the below model;
class AudioFile(models.Model):
name = models.CharField(max_length=100, default='')
audio_file = models.FileField()
uploader = models.ForeignKey(User, default='')
def __unicode__(self):
return self.name
The below form;
class AudioFileForm(forms.ModelForm):
class Meta:
model = AudioFile
fields = ['name', 'audio_file']
def clean_audio_file(self):
audio = self.cleaned_data.get('audio_file',False)
if audio:
if audio._size > 5*1024*1024:
raise ValidationError("File too large ( > 5mb )")
if os.path.splitext(audio.name)[1] != ".mp3":
raise ValidationError("We only support mp3!")
return audio
else:
raise validationError("Couldn't read uploaded file")
As you can see there's some custom validations
Then I have the below html with the AudioFileForm passed as form;
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<form method="post" action="{% url 'actual_upload_audio' %}">
{% csrf_token %}
{{ form }}
<input type="submit" value="Upload"/>
</form>
</body>
</html>
Now I know how to save the object had it not been containing a FileField. I would do something like this;
form = AudioFileForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
//Say it contained another field, foo
foo = form.cleaned_data['foo']
uploader = request.user //view requires login
audio_file = Audio_File.objects.create(name=name,
foo=foo,
uploader=uploader)
audio_file.save()
return HttpResponse("Success!")
But there being a FileField in question I donno how to handle this. I read Django's documentation but don't really get it. A much easier elaborate explanation would be lovely.
For a start I have MEDIA_ROOT = '/home/afzalsh/works/openradio/audio_files/' in my settings.py
Update
Thanks to #Rohan I now have <form method="post" action="{% url 'actual_upload_audio' %}" enctype="multipart/form-data"> instead of <form method="post" action="{% url 'actual_upload_audio' %}">
Also have the below view thanks to #dzejdzej;
form = AudioFileForm(request.POST, request.FILES)
if form.is_valid():
form.cleaned_data['uploader'] = request.user
form.save()
return HttpResponseRedirect(reverse('home_audio',kwargs={'pk':audio_file.pk}))
else:
return HttpResponse("Form Invalid!")
But Form Invalid! :/

Your form should have enctype="multipart/form-data" attribute so that it submits files.
<form method="post" action="{% url 'actual_upload_audio' %}"
enctype="multipart/form-data" >
...
</form>

Since you're using ModelForm, why not let Django handle saving the whole model?
from django.shortcuts import redirect
form = AudioFileForm(request.POST)
if form.is_valid():
form.cleaned_data['uploader'] = request.user //view requires login
form.save()
return redirect(success_url)
If you're using Django >=1.7, upload_to field is no longer required. However, I consider it a good practice.
https://docs.djangoproject.com/en/1.8/ref/models/fields/#django.db.models.FileField.upload_to
Also redirect user after successful POST to avoid issues with csrf.

you should mention the upload_to attribute as : audio_file = models.FileField(upload_to='uploads')
here 'uploads' is the name of the directory you would like to upload your files to.

As Rohan seaid, you must defile your form in template like this.
<form method="post" action="{% url 'actual_upload_audio' %}"
enctype="multipart/form-data" >
...
</form>
This allows you to send files with request to view. Now in view you must have something like this:
def my_view(request):
if request.method == 'POST':
my_form = MyForm(request.POST, request.FILES)
if my_form.is_valid():
my_form.save()
You can initial your form FileField and ImageField with request.FILES

Related

Tags are not being stored in the database even after saving form in django

views.py
def post(request):
if request.method == 'POST':
form = PostModelForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
post.save()
# using the for loop i am able to save the tags data.
# for tag in form.cleaned_data['tags']:
# post.tags.add(tag)
images = request.FILES.getlist('images')
for image in images:
ImagesPostModel.objects.create(post=post, images=image)
return redirect('/Blog/home/')
else:
form = PostModelForm(request.POST)
return render(request, 'post.html', {'form': form})
models.py
class PostModel(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
date_time = models.DateTimeField(auto_now_add=True)
title = models.TextField(null=True)
body = models.TextField(null=True)
tags = TaggableManager()
def __str__(self):
return str(self.user)
post.html
{% extends 'base.html' %}
{% block content %}
<form action="{% url 'post' %}" enctype="multipart/form-data" method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="file" multiple name="images">
<input type="submit">
</form>
{% endblock %}
After giving the input the data is stored in the tags field but, not saving in the database.
I can manually insert data through the admin panel successfully but not as a non-staff user.
I have installed taggit and placed it in the installed_apps in settings.py.
Tags are being saved using post.tags.add(tag) inside for loop. What is the issue with the code?
This is because you use commit=False for the form: then the form has no means to save the many-to-many fields. It is also not necessary to do that, you can work with:
def post(request):
if request.method == 'POST':
form = PostModelForm(request.POST)
if form.is_valid():
form.instance.user = request.user # set the user
post = form.save() # save the form
ImagesPostModel.objects.bulk_create([
ImagesPostModel(post=post, images=image)
for image in request.FILES.getlist('images')
])
return redirect('/Blog/home/')
else:
form = PostModelForm()
return render(request, 'post.html', {'form': form})
Note: Models normally have no Model suffix. Therefore it might be better to rename PostModel to Post.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

Image uploading from Django-template to model is not working

I am trying to upload profile image of user from userprofile.html template but it is not uploading to that model on admin panel. I already configured the static and media settings correctly. I am able to upload from admin panel.But uploading from template isn't working please help..
#forms.py
class ImageUploadForm(forms.ModelForm):
class Meta:
model = accountUser
fields = ['profile_photo',]
#views.py
def upload_pic(request):
if request.method == 'POST':
form = ImageUploadForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('success')
else:
form = ImageUploadForm()
return HttpResponseForbidden('allowed only via POST')
#user.html
<form method = "post" enctype="multipart/form-data">
{% csrf_token %}
<p>
<input id="id_image" type="file" class="" name="image">
</p>
<button type="submit">Choose Picture</button>
</form>

Django form error:local variable 'context' referenced before assignment

I'm collecting data from form, processing data(right now I'm not) and displaying the result on the same HTML page from where the user submits the form.
Here is my views.py file:
def index(request):
template = 'predictor/index.html'
if request.method =='POST':
form = EvalForm(request.POST)
if form.is_valid():
text ='thank you for submitting form'
else:
text='something wrong.'
context: {
'text':text,
}
return render(request,template,context)
else:
form = EvalForm()
return render(request,template)
Here is my index.html file
<form method="POST" action="{% url 'predictor' %}">
{% csrf_token %}
//all input fields including submit button here
</form>
<div class="result">
{{ text }}
</div>
All other things like urls are configured properly.
What I'm doing wrong here?
You have typo in your code.
Should be context = {'text':text,} instead of context: {'text':text,}.

How does the upload_to work in django model FileField?

I'm trying to submit a form containing a file without django model form.
Can I still use upload_to?
How does it work?
example:
models.py
blabla = models.FileField(upload_to='')
views.py
form = BlaForm(request.POST or None, request.FILES or None)
templates.html
<form method="post" class="form-horizontal" action='' enctype="multipart/form-data">

Trying to use django and dropzone/

I'm trying to use dropzone.js with django.
I'm following the somewhat dated guide here (https://amatellanes.wordpress.com/2013/11/05/dropzonejs-django-how-to-build-a-file-upload-form/)
I strongly suspect My view is at issue.
def test(request):
print "test view has been called"
if request.method == 'POST':
print "test request method is POST"
form = UploadFileForm(request.POST, request.FILES)
print request
print request.FILES
if form.is_valid():
new_file = AttachedFiles(attachedfile=request.FILES['file'])
new_file.save()
id = new_file.pk
print id
print "test form valid"
return HttpResponse(json.dumps({'id': id}), content_type="application/json")
print "test form not valid"
else:
form = UploadFileForm()
data = {'form': form}
return render_to_response('mediamanager/test.html', data, context_instance=RequestContext(request))
I've tested submitting to it with the dropzone code
<!-- IMPORTANT enctype attribute! -->
<form id="my_dropzone" class="dropzone" action="/mediamanager/test/" method="post" enctype="multipart/form-data">
{% csrf_token %}
<button id="submit-all">
Submit all files
</button>
</form>
<script src="{% static 'dropzone/js/dropzone.js' %}"></script>
<script type="text/javascript">
Dropzone.options.myDropzone = {
// Prevents Dropzone from uploading dropped files immediately
autoProcessQueue : true,
init : function() {
var submitButton = document.querySelector("#submit-all")
myDropzone = this;
submitButton.addEventListener("click", function() {
myDropzone.processQueue();
// Tell Dropzone to process all queued files.
});
// You might want to show the submit button only when
// files are dropped here:
this.on("addedfile", function() {
// Show submit button here and/or inform user to click it.
console.log("blah")
});
}
};
</script>
and a basic form
<form action="{% url "test" %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="file" />
<input type="submit" value="Submit">
</form>
And the form is never valid.
I'm using a modelform as suggested
class UploadFileForm(forms.ModelForm):
class Meta:
model = AttachedFiles
You can handle Dropzone posts like any other multipart form post.
Here's how I proceed:
#login_required
#usertype_required
def upload_picture(request, uid=None):
"""
Photo upload / dropzone handler
:param request:
:param uid: Optional picture UID when re-uploading a file.
:return:
"""
form = PhotoUploadForm(request.POST, request.FILES or None)
if form.is_valid():
pic = request.FILES['file']
# [...] Process whatever you do with that file there. I resize it, create thumbnails, etc.
# Get an instance of picture model (defined below)
picture = ...
picture.file = pic
picture.save()
return HttpResponse('Image upload succeeded.')
return HttpResponseBadRequest("Image upload form not valid.")
The form is dead simple
class PhotoUploadForm(forms.Form):
# Keep name to 'file' because that's what Dropzone is using
file = forms.ImageField(required=True)
In your model you need the upload_to set:
class Picture(models.Model):
[...]
# Original
file = models.ImageField(upload_to=get_upload_path)
And here's my upload path builder, but you can put anything
def get_upload_path(instance, filename):
""" creates unique-Path & filename for upload """
ext = filename.split('.')[-1]
filename = "%s.%s" % (instance.p_uid, ext)
d = datetime.date.today()
username = instance.author.username
#Create the directory structure
return os.path.join(
'userpics', username, d.strftime('%Y'), d.strftime('%m'), filename
)
Don't forget the csrf_token in the html form itself (I'm using an angularJS directive on top of it so will be different for you)
<form action="{% url 'upload_picture' %}" class="dropzone" drop-zone>
{% csrf_token %}
<div class="fallback">
<h3>Your browser is not supported.</h3>
<strong>
Click here for instructions on how to update it.
</strong>
<p>You can still try to upload your pictures through this form: </p>
<p>
<input name="file" type="file" multiple />
<input type="submit" value="Upload" />
</p>
</div>
</form>
I got dropzone js working by modifying the bootstrap example (I am using bootstrap) here: http://www.dropzonejs.com/bootstrap.html
I do not use any forms. I got a view handling the in coming ajax posts from dropzone. Here is the gist of my view code:
class AjaxUploadView(View):
"""
View for uploading via AJAX.
"""
def post_ajax(self, request, *args, **kwargs):
uploaded_file = request.FILES['file']
# Do stuff with file
# Return appropriate response
Hope it helps.