ImageField Upload with Django - python

I am trying to upload an image with django and so far i can't see the upload_to directory get created or any file source in the database field.
My Model.
class Person(models.Model):
photo = models.ImageField(upload_to='profiles/', null=True, blank=True)
My View
def create_profile(request):
if request.method == "POST":
form = PartialPersonForm(request.POST, request.FILES,)
addressForm = PartialAddressForm(request.POST)
When i upload and save, i dont get any sort of errors. Anyway i can debug this and find out if the photo field is set.
<div class="photo-upload">{{ form.photo|attr:"onchange:readURL(this);" }}

Unless you're doing something special with regard to media storage, you should set up MEDIA_ROOT to the directory where you want the uploaded files to be saved.
When dealing with file-type fields, you will need to following instructions in the Django documentation for FileField.storage.
In your view, you will need to save() the uploaded form data.
def create_profile(request):
if request.method == "POST":
form = PartialPersonForm(request.POST, request.FILES,)
addressForm = PartialAddressForm(request.POST)
form.save()
addressForm.save()

i believe you should create the upload_to directory yourself. Also make sure to set the appropriate permissions for the directory, otherwise it won't be able to write in it.

Related

Displaying user uploaded image in Python Django

I am creating a website using the Django python framework and am currently stuck on a problem.
I am using function views to display a page called myaccount, and on 'myaccount' i would like all user details to be displayed using context objects, for this page it is 'user'. I also have another model called Profile, which holds the profile picture and date of birth of the user. However when i attempt to display the image which has been uploaded during the account creation into the media folder named '/media/%y/%m/%d/imagename.filextension' i receive an error saying "The 'profilepicture' attribute has no file associated with it." I have been searching vastly for fixes to this issue and have so far found no result which has worked, i have tried to create a property function which gets the url from the image called 'get_absolute_url' by doing user.profile.profilepicture.get_absolute_url but it fails to work and displays the same error. I was wondering if anyone could point me in the direction of a fix for this or a solution.
I also do have pillow installed.
The code to display the image, views.py and urls.py is down below
views.py
#login_required
def myaccount(request):
return render(request, 'account/myaccount.html', {'section': 'myaccount'})
urls.py
path('', views.myaccount, name='myaccount'),
myaccount.html
<img src="{{ user.profile.profilepicture.url }}" width="260" height="234"/>
Profile model
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
date_of_birth = models.DateField(blank =True, null =True)
profilepicture = models.ImageField(upload_to='users/%Y/%m/%d', blank=True)
def __str__(self):
return f'Profile for user {self.user.username}'
The model for User is from 'django.contrib.auth.models'
To confirm whether it was a problem with the context object I tried to display the users first name which worked as expected.
The account register view
def register(request):
if request.method == 'POST':
user_form = UserRegistrationForm(request.POST)
if user_form.is_valid():
# Create a new user object but avoid saving it yet
new_user = user_form.save(commit=False)
# Set the chosen password
new_user.set_password(
user_form.cleaned_data['password'])
# Save the User object
new_user.save()
Profile.objects.create(user=new_user)
# Create the user profile
return render(request,
'account/register_done.html',
{'new_user': new_user})
else:
user_form = UserRegistrationForm()
return render(request,
'account/register.html',
{'user_form': user_form})
If any other code or information is needed please ask.
After some using some common sense I realised that I actually didn’t have a photo uploaded for the profile picture attribute which would explain there being no url for it... Thanks for the help anyway and sorry for wasting time, I’ve also used an if block now to stop the error from being ran if there is no picture associated with the profile.
Since you are using ImageField, have you also installed the Pillow library using PIP install Pillow?

How to upload multiple images from a single "Choose Files" selector in django admin

I'm doing this little project simulating a clothing ordering system to brush up my django skills.
I have to add several pictures of a certain item(not a fixed number). So, using a fixed number of image fields in Item model is not ideal. So, I thought of another model consisting only of images connected to the model 'items' by a key.
Now I know that django has the inline option for adding model objects one by one as per requirement. But that seems a bit of a sucker as you have to open that dialog box and select the images one by one.
Is it possible that there be a selector via which we could choose multiple files at once and they be added to the model??
Yes, there is a handy way.
First declare an image class, something like:
class Image(models.Model):
condo = models.ForeignKey(Condo, on_delete=models.CASCADE, related_name='images')
image = models.FileField(upload_to="images/")
uploaded_at = models.DateTimeField(auto_now_add=True)
In the form creation don't add a line for presenting a FileField
class ListingForm(forms.ModelForm):
...
...
#photos = forms.FileField(required=False) #Don't do it.
But rather in the template, among the other lines, add this too:
<form action="" method="post" autocomplete="off" enctype="multipart/form-data">
{% csrf_token %}
{{ your_form|crispy }} <br/>
<input type="file" name="images" multiple>
<input type="submit" value="Submit">
</form>
In the view that handles your POST request, you can process the uploaded images with:
from app.forms import Image
if request.method == 'POST':
...
...
for file in request.FILES.getlist('images'):
instance = Image(
condo=Condo.objects.get(your_parent_objects_id),
image=file
)
instance.save()
This creates new Image object for each image and saves the actual files to your media folder.
Additionally, I'd suggest to rename the files your users are uploading due to possible malicious acts.
I've done this by creating a small function and using it instead of the "images/" part for it.
import uuid
def images_directory_path(instance, filename):
return '/'.join(['images', str(instance.condo_id.id), str(uuid.uuid4().hex + ".png")])
# in Image class (models.py)
image = models.FileField(upload_to=images_directory_path)
This last modification generates new names for the images as uuid4, adds .png extension to them and creates full link to the media/ folder.
Of course, there is no validation present whether the format or size of the uploaded files are valid, but that is a topic for another question.
You can access the images with the variable images as it is the related_name for all connected images to the given object (in this case Condo).
Lastly, this is a a great article by Vitor Freitas about using Javascript plugins for uploading multiple images with progress bar.
from the docs (https://docs.djangoproject.com/en/2.2/topics/http/file-uploads/):
Uploading multiple files
If you want to upload multiple files using one form field, set the multiple HTML attribute of field’s widget:
forms.py
from django import forms
class FileFieldForm(forms.Form):
file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple':
True}))
Then override the post method of your FormView subclass to handle multiple file uploads:
views.py
from django.views.generic.edit import FormView
from .forms import FileFieldForm
class FileFieldView(FormView):
form_class = FileFieldForm
template_name = 'upload.html' # Replace with your template.
success_url = '...' # 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:
instance = Image(image=file) # match the model.
instance.save()
return self.form_valid(form)
else:
return self.form_invalid(form)
and add a model:
from django.db import models
class Image(models.Model):
image = models.FileField(upload_to='images/%Y/%m/%d')
uploaded_at = models.DateTimeField(auto_now_add=True)
and add to urls.py urlpatterns:
path(‘upload/’,views.FileFieldView.as_view(),name=‘FileFieldView’),

"Upload" file from disk in Django

I generate a file in python, and want to "upload" that file to the django database. This way it is automatically put inside the media folder, and organized neatly with all other files of my application.
Now here is what I tried: (type hinting used, since it's python 3.6)
# forms.py
class UploadForm(forms.ModelForm):
class Meta:
model = UploadedFile
fields = ('document',)
# models.py
class UploadedFile(models.Model):
document = models.FileField(upload_to=get_upload_path)
# mimetype is generated by filename on save
mimetype = models.CharField(max_length=255)
# ... additional fields like temporary
def get_upload_path(instance: UploadedFile, filename):
if instance.temporary:
return "uploaded_files/temp/" + filename
return "uploaded_files/" + filename
# views.py, file_out has been generated
with open(file_out, 'rb') as local_file:
from django.core.files import File
form = UploadForm(dict(), {'document': File(local_file)})
print(form.errors)
if form.is_valid():
file = form.save(commit=False)
# ... set additional fields
file.save()
form.save_m2m()
return file
Now this is not the only thing I've tried. First I've gone with setting the FileField directly, but that resulted in the save() to fail, while the mimetype field is set. Because the original file sits outside the media folder, and thus a suspicious file action is triggered.
Also, the form gives some feedback about the "upload", through the form.errors.
Depending on my approach, either the save() fails as mentioned above -- meaning the "uploading" does not actually copy the file in the media folder -- or the form returns the error that no file was transmitted, and tells to check the form protocol.
Now my theory is, that I would have to go and initialize my own instance of InMemoryUploadedFile, but I could not figure out how to do that myself, and no documentation was available on the internet.
It feels like I'm taking the wrong approach from the get go. How would one do this properly?
Do you have get_upload_path defined? If not, that would explain the errors you're getting.
From what I can see you're on the right track. If you don't need a dynamic path for your uploads, if you just want them in media/uploads, you can pass in a string value for upload_to (from the Django docs):
# file will be uploaded to MEDIA_ROOT/uploads
document = models.FileField(upload_to='uploads/')
First of all, thanks to Franey for pointing me at storage documentation which lead me to contentfile documentation.
The ContentFile actually solves the problem, because it basically is the self-instantiated version of InMemoryUploadedFile that I was looking for. It's a django File that is not stored on disk.
Here's the full solution:
# views.py, file_out has been generated
with open(file_out, 'rb') as local_file:
from django.core.files.base import ContentFile
# we need to provide a name. Otherwise the Storage.save
# method reveives a None-parameter and breaks.
form = UploadForm(dict(), {'document': ContentFile(local_file.read(), name=name)})
if form.is_valid():
file = form.save(commit=False)
# ... set additional fields
file.save()
form.save_m2m()
return file

User Provided CSV in Django Web Project

I'm using Django to host a web application, and I would like to obtain a CSV file from the user, and process this information (using Python). I am currently using this line of code in the HTML to obtain the CSV file:
<input type="file" accept="text/csv" id="mycsv">
Where in the Django project should I obtain the information from the CSV file, and how would I go about doing this? (I know the question is broad and doesn't give context for my specific project, but I figure that once I know how to access the data in the CSV I can figure the rest out).
Step 1: upload the file
# forms.py
from django import forms
class UploadFileForm(forms.Form):
file = forms.FileField()
Step 2: parse data and update database
# views.py
import csv
from .models import YourModel
def myview(request):
if request.method == "POST":
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
reader = csv.reader(form.cleaned_data['file'])
for row in reader:
try:
some_instance = YourModel.objects.get_or_create(row[...])
...
Files uploaded by the user will go to the media folder that you have defined in your settings.py file.
You should be able to access user uploaded files in the media directory from your python code with something like this:
file_ = open(os.path.join(settings.MEDIA_ROOT, 'name_of_file'))
More info on MEDIA_ROOT and MEDIA_URL can be found here.

How can I let users upload images to photologue?

I've set up the Photologue, but I don't know how to let a (normal) user upload an image without using the admin interface.
I'd like to let users upload base64-encoded images from a HTML5 canvas, but in the first step it would be fine to upload files from the user's filesystem.
I guess I could modify this general example on how to upload files to use photologue's photo model. I assume this would mean somehow filling "ImageModel"'s ImageField attribute "image'.
I've actually used the linked file upload guide and adapted it to photologue. From my canvas element I extracted a base64 data url and set a form's field to its value then I could interpret it on Django's server side in a view:
def upload_base64(request):
# Handle file upload
if request.method == 'POST':
form = PhotoCodeForm(request.POST, request.FILES)
if form.is_valid():
uploaded_photo_data = base64.b64decode(request.POST['photocode'])
uploaded_photo_file = ContentFile(uploaded_photo_data)
title_str = "Untitled"
slug = slugify( title_str + str( datetime.now() ) )
uploaded_photo = Photo.objects.create( image = default_storage.save(slug, uploaded_photo_file),
title = slug,
title_slug = slug )
name_upload_gallery = "user-upload-queue"
try:
upload_gallery = Gallery.objects.get(title_slug__exact=name_upload_gallery)
except ObjectDoesNotExist:
return HttpResponseBadRequest('<html><body><p>The gallery "'+name_upload_gallery+'" does not exist.</p></body></html>')
upload_gallery.photos.add(uploaded_photo)
# Redirect to the photo gallery after POST
return HttpResponseRedirect('/canvas/')
else:
return HttpResponseBadRequest('<html><body><p>The entered data was not correct.</p></body></html>')
else:
form = PhotoCodeForm() # A empty, unbound form
return render_to_response(
'photologue_upload/upload_base64.html',
{'form': form},
context_instance=RequestContext(request)
)
upload_base64.html is a very simple form that has a field photocode where the base64 string is pasted to.

Categories

Resources