I am trying to have a form to generate thumbnails from images that will be uploaded
I will be using sorl for the thumb generation and I am following the following documentation:
Django multiple file upload: https://docs.djangoproject.com/en/3.0/topics/http/file-uploads/
Sorl Low level API: https://sorl-thumbnail.readthedocs.io/en/latest/examples.html
When I try to generate the thumbnail I get the error of
not enough values to unpack (expected 2, got 1)
I dont understand what I am doing wrong, in summary I upload the image and this get saved in my root directory, then im trying to create the thumb
Also is there a way to void saving this original image in the root? I am planning to send both Image and thumb to google cloud storage
My forms.py:
from django import forms
class FileFieldForm(forms.Form):
file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
My html file: upload.html
<html>
<head></head>
<body>
<h3>Read File Content</h3>
<form enctype="multipart/form-data" action="" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Save">
</form>
</body>
</html>
My views.py looks like:
from sorl.thumbnail import ImageField, get_thumbnail
from .forms import FileFieldForm
class FileFieldView(FormView):
form_class = FileFieldForm
template_name = 'app_workflow/upload.html' # Replace with your template.
success_url = '/photo' # Replace with your URL or reverse().
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('file_field')
if form.is_valid():
for f in files:
with open(f.name, 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
im = get_thumbnail(f.name, '100x100', crop='center', quality=99)
return self.form_valid(form)
else:
return self.form_invalid(form)
As you have said in the question, you do not want to store in root and generate thumbnail. Then I would suggest something like this:
from PIL import Image
class FileFieldView(FormView):
form_class = FileFieldForm
template_name = 'app_workflow/upload.html' # Replace with your template.
success_url = '/photo' # Replace with your URL or reverse().
def form_valid(self, *args, **kwargs):
img_size = (100, 100)
files = self.request.FILES.getlist('file_field')
for f in files:
im = Image.open(f)
im.thumbnail(img_size)
# your thumbnail image is in memory now
# you can now store it in your model and use django-storages to upload it to gcloud
return super().form_valid(*args, **kwargs)
Here I am not storing images and directly loading it in the PIL.Image module to generate thumbnails. You can use django-storages to upload data from FileField to gcloud.
Storing in root:
Then you can change the code like this:
for f in files:
for chunk in f.chunks():
destination.write(chunk)
im = Image.open(f)
im.thumbnail(img_size)
im.save('thumb_{}'.format(f.name))
Related
I'm trying to convert each image instance to base64 using images URL. All images are stored in my amazon-s3 bucket. Unfortunately, my generated encryption is not displaying the image at my recipe_plain.html template. Any help is appreciated.
views.py
...
import base64
class RecipePlainView(DetailView):
model = Recipe
template_name = 'recipes/recipe_plain.html'
def get_context_data(self, **kwargs):
context = super(RecipePlainView, self).get_context_data(**kwargs)
image = self.object.image
image.open(mode='rb')
context['recipe_image_base64'] = base64.b64encode(image.read())
image.close()
return context
recipe_plain.html
<img src="data:image;base64,{{ recipe_image_base64 }}" alt="{{ recipe.image.name }}">
The issue was that context['recipe_image_base64'] variable was returning the base64 in bytes object. This has been resolved using the decode() function.
I also simplified the script using the requests library and included a validation.
import base64, requests
class RecipePlainView(DetailView):
model = Recipe
template_name = 'recipes/recipe_plain.html'
def get_context_data(self, **kwargs):
url = self.object.image.url
r = requests.get(url)
if r.status_code == 200:
byteBase64 = base64.b64encode(requests.get(url).content)
context['recipe_image_base64'] = byteBase64.decode("utf-8")
else:
context['recipe_image_base64'] = False
return context
I need to upload profile images into diferent folders in Django. So, I have a folder for each account, and the profile image have to go to the specific folder. How can I do that?
Here is my uploadprofile.html
<form action="{% url 'uploadimage' %}" enctype="multipart/form-data" method="POST">
{% csrf_token %}
<input type="file" name="avatar" accept="image/gif, image/jpeg, image/png">
<button type="submit">Upload</button>
</form>
And here is my view in views.py
def uploadimage(request):
img = request.FILES['avatar'] #Here I get the file name, THIS WORKS
#Here is where I create the folder to the specified profile using the user id, THIS WORKS TOO
if not os.path.exists('static/profile/' + str(request.session['user_id'])):
os.mkdir('static/profile/' + str(request.session['user_id']))
#Here is where I create the name of the path to save as a VARCHAR field, THIS WORKS TOO
avatar = "../../static/profile/" + str(request.session['user_id']) + "/" + str(img)
#THEN I HAVE TO COPY THE FILE IN img TO THE CREATED FOLDER
return redirect(request, 'myapp/upload.html')
You can pass a callable to upload_to. Basically, what it means is whatever value that callable returns, the image will be uploaded in that path.
Example:
def get_upload_path(instance, filename):
return "%s/%s" % (instance.user.id, filename)
class MyModel:
user = ...
image = models.FileField(upload_to=get_upload_path)
There's more information in the docs and an example too, though similar to what I posted above.
By looking at Django docs what you get when you do img = request.FILES['avatar'] you get a file descriptor that points to an open file with your image.
Then you should to dump the contents in your actual avatar path, right?
#Here is where I create the name of the path to save as a VARCHAR field, THIS WORKS TOO
avatar = "../../static/profile/" + str(request.session['user_id']) + "/" + str(img)
# # # # #
with open(avatar, 'wb') as actual_file:
actual_file.write(img.read())
# # # # #
return redirect(request, 'myapp/upload.html')
Beware: the code is untested.
from django.shortcuts import render
from django.conf import settings
from django.core.files.storage import FileSystemStorage
def uploadimage(request):
if request.method == 'POST' and request.FILES['avatar']:
img = request.FILES['avatar']
fs = FileSystemStorage()
#To copy image to the base folder
#filename = fs.save(img.name, img)
#To save in a specified folder
filename = fs.save('static/profile/'+img.name, img)
uploaded_file_url = fs.url(filename) #To get the file`s url
return render(request, 'myapp/upload.html', {
'uploaded_file_url': uploaded_file_url
})
return render(request, 'myapp/upload.html')
I need to store a file path in db using django via ajax form submission .
Here is my view:
def dashboard(request):
container=[]
DIR = os.path.realpath("/home/user/Desktop/Demo")
WAY = os.listdir(DIR)
for file in WAY:
if file.endswith('.mp4'):
file_name = file
FDIR=os.path.join(DIR, file)
container.append(FDIR)
return render(request, 'dashboard.html', {'container': container})
def new_scheduler(request):
if request.method =='POST':
f_name = request.POST.get('file')
dateAndTime = request.POST.get('dateAndTime')
Scheduled_data = schedulesdb.objects.create(
f_name = file,
dateAndTime = dateAndTime,
)
Scheduled_data.save()
return HttpResponse ('done')
It save in database like <type 'file'> .
Here is my model.py:
class schedulesdb(models.Model):
f_name = models.CharField(max_length=100)
dateAndTime = models.DateTimeField(['%Y-%m-%d %H:%M:%S'],null=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=2)
def __unicode__(self): # on Python 2
return self.f_name
Thanks in advance :)
From your code it's not 100% clear whether you're intending to handle file uploads from the client, or simply store strings that happen to be a file path (potentially for locating a file on some remote filesystem).
1. File uploads
Consider using the FileField model field type rather than the CharField.
The Django documentation has a solid explanation and examples of how to do simple file uploads.
2. Obtaining the actual POST data value for the f_name field
Your code sample is storing "", because you're assigning 'file' (which is a builtin type) rather than the f_name variable that you previously declared. Like this:
def new_scheduler(request):
if request.method =='POST':
f_name = request.POST.get('file')
dateAndTime = request.POST.get('dateAndTime')
Scheduled_data = schedulesdb.objects.create(
f_name = f_name, # Note the use of f_name instead of file
dateAndTime = dateAndTime,
)
Scheduled_data.save()
return HttpResponse ('done')
I haven't even used ajax, but I stored file path in mysql and stored file in media folder in django
Here's my views.py:
from CRUDoperation.models import EmpModel
def upload(request):
if request.method == 'POST':
saverecord=EmpModel()
myfile = request.FILES['myfile']
fs = FileSystemStorage()
filename = fs.save(myfile.name, myfile)
uploaded_file_url = fs.url(filename)
saverecord.file=uploaded_file_url
return render(request,'upload.html')
models.py file
class EmpModel(models.Model):
file=models.CharField(max_length=150)
class Meta:
db_table="employee"
settings.py file
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
my html code:
{% load static %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="myfile">
<button type="submit">Upload</button>
</form>
I'm quite new to using Django and I am trying to develop a website where the user is able to upload a number of excel files, these files are then stored in a media folder Webproject/project/media.
def upload(request):
if request.POST:
form = FileForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return render_to_response('project/upload_successful.html')
else:
form = FileForm()
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('project/create.html', args)
The document is then displayed in a list along with any other document they have uploaded, which you can click into and it will displays basic info about them and the name of the excelfile they have uploaded. From here I want to be able to download the same excel file again using the link:
Download Document
My urls are
urlpatterns = [
url(r'^$', ListView.as_view(queryset=Post.objects.all().order_by("-date")[:25],
template_name="project/project.html")),
url(r'^(?P<pk>\d+)$', DetailView.as_view(model=Post, template_name="project/post.html")),
url(r'^upload/$', upload),
url(r'^download/(?P<path>.*)$', serve, {'document root': settings.MEDIA_ROOT}),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
but I get the error, serve() got an unexpected keyword argument 'document root'. can anyone explain how to fix this?
OR
Explain how I can get the uploaded files to to be selected and served using
def download(request):
file_name = #get the filename of desired excel file
path_to_file = #get the path of desired excel file
response = HttpResponse(mimetype='application/force-download')
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(file_name)
response['X-Sendfile'] = smart_str(path_to_file)
return response
You missed underscore in argument document_root. But it's bad idea to use serve in production. Use something like this instead:
import os
from django.conf import settings
from django.http import HttpResponse, Http404
def download(request, path):
file_path = os.path.join(settings.MEDIA_ROOT, path)
if os.path.exists(file_path):
with open(file_path, 'rb') as fh:
response = HttpResponse(fh.read(), content_type="application/vnd.ms-excel")
response['Content-Disposition'] = 'inline; filename=' + os.path.basename(file_path)
return response
raise Http404
You can add "download" attribute inside your tag to download files.
<a href="/project/download" download> Download Document </a>
https://www.w3schools.com/tags/att_a_download.asp
Reference:
In view.py Implement function like,
def download(request, id):
obj = your_model_name.objects.get(id=id)
filename = obj.model_attribute_name.path
response = FileResponse(open(filename, 'rb'))
return response
When you upload a file using FileField, the file will have a URL that you can use to point to the file and use HTML download attribute to download that file you can simply do this.
models.py
The model.py looks like this
class CsvFile(models.Model):
csv_file = models.FileField(upload_to='documents')
views.py
#csv upload
class CsvUploadView(generic.CreateView):
model = CsvFile
fields = ['csv_file']
template_name = 'upload.html'
#csv download
class CsvDownloadView(generic.ListView):
model = CsvFile
fields = ['csv_file']
template_name = 'download.html'
Then in your templates.
#Upload template
upload.html
<div class="container">
<form action="#" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.media }}
{{ form.as_p }}
<button class="btn btn-primary btn-sm" type="submit">Upload</button>
</form>
#download template
download.html
{% for document in object_list %}
Download
{% endfor %}
I did not use forms, just rendered model but either way, FileField is there and it will work the same.
I've found Django's FileField to be really helpful for letting users upload and download files. The Django documentation has a section on managing files. You can store some information about the file in a table, along with a FileField that points to the file itself. Then you can list the available files by searching the table.
#Biswadp's solution worked greatly for me
In your static folder, make sure to have the desired files you would like the user to download
In your HTML template, your code should look like this :
Download
Using the below approach makes everything less secure since any user can access any user's file.
<a href="/project/download" download> Download Document </a>
Using the below approach makes no sense since Django only handles one requests at the time (unless you are using gunicorn or something else), and believe me, the below approach takes a lot of time to complete.
def download(request, path):
file_path = os.path.join(settings.MEDIA_ROOT, path)
if os.path.exists(file_path):
with open(file_path, 'rb') as fh:
response = HttpResponse(fh.read(), content_type="application/vnd.ms-excel")
response['Content-Disposition'] = 'inline; filename=' + os.path.basename(file_path)
return response
raise Http404
So what is the optimum solution?
Use Nginx authenticated routes. When requesting a file from Nginx you can make a request to a route and depending on the HTTP response Nginx allows to denies that request. This makes it very secure and also scalable and performant.
You can ready about more here
import mimetypes
from django.http import HttpResponse, Http404
mime_type, _ = mimetypes.guess_type(json_file_path)
if os.path.exists(json_file_path):
with open(json_file_path, 'r') as fh:
response = HttpResponse(fh, content_type=mime_type)
response['Content-Disposition'] = "attachment; filename=%s" % 'config.json'
return response
raise Http404
<a href='/your-download-view/' download>Download</a>
In your view:
from django.http import FileResponse
def download(request):
# pre-processing, authorizations, etc.
# ...
return FileResponse(open(path_to_file, 'rb'), as_attachment=True)
Simple using html like this downloads the file mentioned using static keyword
Download CV
1.settings.py:
MEDIA_DIR = os.path.join(BASE_DIR,'media')
#Media
MEDIA_ROOT = MEDIA_DIR
MEDIA_URL = '/media/'
2.urls.py:
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
3.in template:
<a href="{{ file.url }}" download>Download File.</a>
Work and test in django >=3
for more detail use this link:
https://youtu.be/MpDZ34mEJ5Y
If the file is a FileField in the model, this is the way I do it:
try:
download_file = PrintingFile.objects.get(pk=kwargs.get('pk_file', 0))
return FileResponse(download_file.file.open(), as_attachment=True)
except PrintingFile.DoesNotExist:
raise Http404
More here
I use this method:
{% if quote.myfile %}
<div class="">
<a role="button"
href="{{ quote.myfile.url }}"
download="{{ quote.myfile.url }}"
class="btn btn-light text-dark ml-0">
Download attachment
</a>
</div>
{% endif %}
If you hafe upload your file in media than:
media
example-input-file.txt
views.py
def download_csv(request):
file_path = os.path.join(settings.MEDIA_ROOT, 'example-input-file.txt')
if os.path.exists(file_path):
with open(file_path, 'rb') as fh:
response = HttpResponse(fh.read(), content_type="application/vnd.ms-excel")
response['Content-Disposition'] = 'inline; filename=' + os.path.basename(file_path)
return response
urls.py
path('download_csv/', views.download_csv, name='download_csv'),
download.html
a href="{% url 'download_csv' %}" download=""
When trying to upload a file using a FileField, the form doesn't post the file data to the server.
It works perfectly if you use a text field, but for some reason it doesn't recognize the file, as it doesn't
show up on request.FILES, or request.POSTS.
MEDIA_ROOT and MEDIA_URL configuration:
MEDIA_ROOT = '/home/grove/pootleImages/'
MEDIA_URL = '/pootleImages/'
get_unit_context decorator in decorators.py:
def get_unit_context(permission_codes):
def wrap_f(f):
#wraps(f)
def decorated_f(request, uid, *args, **kwargs):
unit = get_object_or_404(
Unit.objects.select_related("store__translation_project",
"store__parent"),
id=uid,
)
_common_context(request, unit.store.translation_project,
permission_codes)
request.unit = unit
request.store = unit.store
request.directory = unit.store.parent
return f(request, unit, *args, **kwargs)
return decorated_f
return wrap_f
My forms.py method:
def unit_image_form_factory(language):
image_attrs = {
'lang': language.code,
'dir': language.direction,
'class': 'images expanding focusthis',
'rows': 2,
'tabindex': 15,
}
class UnitImageForm(forms.ModelForm):
class Meta:
fields = ('image',)
model = Unit
# It works if using a CharField!
#image = forms.CharField(required=True,
# label=_("Image"),
# widget=forms.Textarea(
# attrs=image_attrs))
image= forms.FileField(required=True, label=_('Image'),
widget=forms.FileInput(
attrs=image_attrs))
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(UnitImageForm, self).__init__(*args, **kwargs)
def save(self):
super(UnitImageForm, self).save()
return UnitImageForm
My models.py snippet:
class Unit(models.Model, base.TranslationUnit):
# ...
# ...
# It works if using a TextField!
#image = models.TextField(null=True, blank=True)
image = models.FileField(upload_to=".", blank=True, null=True)
# ...
# ...
My urls.py snippet:
url(r'^xhr/units/(?P<uid>[0-9]+)/image/?$',
'image',
name='pootle-xhr-units-image'),
My views.py method:
#require_POST
#ajax_required
#get_unit_context('translate')
def image(request, unit):
"""Stores a new image for the given ``unit``.
:return: If the form validates, the cleaned image is returned.
An error message is returned otherwise.
"""
# Update current unit instance's attributes
unit.uploaded_by = request.profile
unit.uploaded_on = timezone.now()
language = request.translation_project.language
form = unit_image_form_factory(language)(request.POST, request.FILES, instance=unit,
request=request)
if form.is_valid():
form.save()
context = {
'unit': unit,
'language': language,
}
t = loader.get_template('unit/image.html')
c = RequestContext(request, context)
json = {'image': t.render(c)}
rcode = 200
else:
json = {'msg': _("Image submission failed.")}
rcode = 400
response = simplejson.dumps(json)
return HttpResponse(response, status=rcode, mimetype="application/json")
My HTML template for the image upload:
<div id="upload-image">
<form enctype="multipart/form-data" method="post" action="{% url 'pootle-xhr-units-image' unit.id %}" id="image-form">
{% csrf_token %}
<input type="file" name="image" id="id_image" />
<p><input type="submit" value="{% trans 'Upload' %}" /></p>
</form>
</div>
When the form is instantiated, request.POST does not return the file browsed by the user, neither request.FILES.
form.errors just returns "This field is required"
The form object returns the following:
<tr><th><label for="id_image">Image:</label></th><td><ul class="errorlist"><li>This field is required.</li>
</ul><input lang="pl" rows="2" name="image" id="id_image" type="file" class="images expanding focusthis" dir="ltr" tabindex="15" /></td></tr>
And when the user clicks the submit button, the following POST error occurs:
"POST /xhr/units/74923/image HTTP/1.1" 400 35
I could bypass it by including required=False to the image property, but the file is not posted anyway.
More output debug information:
POST when fileField is required=True:
Status Code: 400 BAD REQUEST
Form Data:
csrfmiddlewaretoken: yoTqPAAjy74GH
form.errors:
"msg": "imageThis field is required."}
If change required=True to required=False:
Status Code: 200 OK
Form Data:
csrfmiddlewaretoken: yoTqPAAjy74GH
But the imagefield still doesn't show up in the form data.
Thank you,
Alex
I added a gist hub containing all files related to this problem, to ease visualization:
https://gist.github.com/alex-silva/40313734b9f1cd37f204
It looks like you've forgotten to add the {% csrf_token %} in your form. Add that between the tag.
OR...
You can add the csrf_exempt decorator to your processing view:
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
#csrf_exempt
def my_view(request):
return HttpResponse('Hello world')
More info: https://docs.djangoproject.com/en/dev/ref/contrib/csrf/
Finally managed to get it to work in some way. The form wasn't working at all when using it in the main page, so I created a link to a blank page containing just the upload image form. When separate from the main page, the upload works fine. Then I just redirect to the main page after uploading. Why the form doesn't work in the main page, that's a mystery.