Upload and manipulate xml and image file in django - django

This code return a TypeError as expected str, bytes or os.PathLike object, not InMemoryUploadedFile
I don't know how to pass user data in the form of file and image to my code.py file for making changes to the original.
views.py
def home(request):
new_image = None
file = None
form = ScanForm()
if request.method == 'POST':
form = ScanForm(request.POST, request.FILES)
if form.is_valid():
image = request.FILES['image']
xml_file = request.FILES['xml_file']
new_image = code.create(image, code.search(
xml_file)[0], code.search(xml_file)[1])
form.save()
return render(request, 'app/home.html', {'form': form, 'new_image': new_image})
else:
form = ScanForm()
return render(request, 'app/home.html', {'form': form, 'new_image': new_image})
printing image and xml_file successfully prints out their names
forms.py
class ScanForm(forms.ModelForm):
class Meta:
model = Scan
fields = '__all__'
models.py
class Scan(models.Model):
image = models.ImageField(upload_to='images')
xml_file = models.FileField(upload_to='files')
processed_at = models.DateTimeField(auto_now_add=True)
description = models.CharField(max_length=500, null=True)
class Meta:
ordering = ['-processed_at']
def __str__(self):
return self.description
Here is the code for manipulation of image according to the data in the xml
code.py
def search(path):
new = []
object_names = []
object_values = []
txt = Path(path).read_text()
txt.strip()
names = et.fromstring(txt).findall('object')
for i in names:
object_names.append(i[0].text)
values = et.fromstring(txt).findall('object/bndbox')
for i in values:
for j in i:
object_values.append(int(j.text))
return object_names, object_values
def create(image, object_names, object_values):
img = cv.imread(image)
on = len(object_names)
ov = len(object_values)
for i in list(range(0, ov, on)):
cv.rectangle(img, (object_values[i], object_values[i+1]),
(object_values[i+2], object_values[i+3]), (0, 0, 255), thickness=5)
return img
This code.py works fine if tested by passing data manually using local folder.
Here is the Traceback:
Traceback image

pathlib.Path() handles file paths, not memory objects. request.FILES are the data attached to the POST request. During your handling of a POST request, you can validate this data and decide to save it to the server disk.
If you would like your image processing to read the file from the server disk, you have to save the new model instance first. You can then access the file's path on disk through the name attribute of the model's ImageField, see Using files in models.
If you want to handle the uploaded data before saving it to disk, you can read it as follows:
txt = request.FILES["xml_file"].read()
See UploadedFile.read()

Related

create pdf with each post django

Currently working on a django social media application where next to posting every post to the feed, the information of each upload should create a pdf document, containing caption, image, created_at and image_id.
I´ve put the canvas into the upload functions, so that both will be created on the same click. So far, i get a pdf (can't get the attributes from the post into the pdf tho) and the post is uploaded just fine.
How do I get the posted data into the pdf?
And how do save that pdf to a folder within the project instead of starting an automatic download? The user should not be able to notice the pdf converting. It is just for back office - but very necessary due to the social media website being a part of a big installation. So how do I get these pdfs?
Here is the code:
views.py
def upload(request):
if request.method == 'POST':
#user = request.user.username
image = request.FILES.get('image_upload')
caption = request.POST['caption']
new_post = Post.objects.create( image=image, caption=caption)
new_post.save()
#create pdf
buffer = io.BytesIO()
p = canvas.Canvas(buffer)
p.drawString(100, 100, "Hello world.")
p = request.FILES.get('post_pdf')
p.drawText('caption')
p.drawImage('image')
p.showPage()
p.save()
buffer.seek(0)
return FileResponse(buffer, as_attachment=True, filename='hello.pdf')
return redirect('/')
else:
return redirect('/')
models.py
class Post(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
image = models.ImageField(upload_to='post_images')
caption = models.TextField(max_length=300)
created_at = models.DateTimeField(auto_now_add=True)
number_of_likes = models.IntegerField(default=0)
number_of_dislikes = models.IntegerField(default=0)
#interaction_count = models.IntegerField(default=0)
#engagement_count = models.IntegerField(null=True)#number_of_dislikes + number_of_likes
def __str__(self):
return self.caption
To add the value of Post.caption into the pdf, use the value of new_post.caption, change this:
p.drawText('caption')
for this:
p.drawText(new_post.caption)
Same for other fields.
This is not as hard as it seems,
so Let's see if you are successful in creating a pdf and now you have to store it in background instead of downloading.
file_name = request.FILES["file_name"]. #suppose file_name is file name
file_name = default_storage.save(rf"{basePath}/media/whatsapp/file_name.pdf", file_name) #{basePath}/media/whatsapp/ is the path name where we want it to be stored

How to upload and view images in django

models.py
class FileUpload(models.Model):
File_Name = models.CharField(max_length=255, blank=True)
File_path = models.FileField(upload_to='')
Description = models.CharField(max_length=255, blank=True)
Upload_Date = models.DateTimeField(auto_now_add=True)
forms.py
class FileUploadForm(forms.Form):
class Meta:
model = FileUpload
File_Name = forms.CharField(label="File Name",max_length=255)
Description = forms.CharField(label="Description", max_length=255)
I'm new in Django.I need help. How to upload images in the database and view those images? Thanks in advance!
here paths are stored in database and images are stored in a folder. But I don't need that. I want to save images and path to the database and I need to view that image. Please help!
views.py:
def uploadfile(request):
print('inside upload logic')
if request.method == 'POST':
form = FileUploadForm(request.POST, request.FILES)
if form.is_valid():
# ImageUpload(request.FILES['File_Name'])
myfile = request.FILES['File_Name']
fs = FileSystemStorage()
filename = fs.save(myfile.name, myfile)
uploaded_file_url = fs.url(filename)
newdoc = FileUpload(File_Name=myfile.name, File_path=uploaded_file_url, Description=request.POST['Description'])
newdoc.save()
#return HttpResponse("File uploaded successfuly")
return render(request, 'Login/fileupload.html')
else:
form = FileUploadForm()
return render(request, 'Login/fileupload.html', {
'form': form
})
You normally shouldn't store the image data in your database. If you need to upload and store images, use the ImageField or FileField and follow the instructions from the docs to save the image. The only thing you need to do is:
form = FileUploadForm(request.POST, request.FILES)
if form.is_valid():
uploaded_file = FileUpload(
File_path=request.FILES['File_path'], # or whatever you've called the file input
File_name=form.cleaned_data['File_Name'],
Description=form.cleaned_data['Description'])
uploaded_file.save()
It would be easier to use a ModelForm in your case, then you only need to save the form:
if form.is_valid():
form.save()
This will automatically save the file in the correct way. No need to do the saving manually like you are doing.
To view the image is as simple as this:
file_upload = FileUpload.objects.get(id=34)
file_url = file_upload.File_path.url # url relative to `MEDIA_ROOT`
# or in a template
{% load static %}
{% get_media_prefix %}{{ file_upload.File_path.url }}
If you really need to store the images as binary blobs to your database (but beware that it almost never makes sense to do so), use Django's BinaryField, as described here.
This also means you will have to handle transforming the image back and forth from a binary blob to a proper image file. Consider also storing the content type (image/jpg or image/png or image/webp) since you will need that to properly re-create the file.

Django with PIL - '_io.BytesIO' object has no attribute 'name'

I'm using PIL to resize an uploaded photo before saving. Note that I'm using formsets to upload the pictures. I'm using BytesIO to open the file. At the last step, I get the error - '_io.BytesIO' object has no attribute 'name'. Why is this?
def fsbo_create_listing(request):
PhotoFormSet = formset_factory(OwnerListingPhotoForm, extra=15)
if request.method == 'POST':
form = OwnerListingForm(request.POST)
photo_formset = PhotoFormSet(request.POST, request.FILES)
if form.is_valid() and photo_formset.is_valid():
form.instance.user = request.user
form.save()
for i in photo_formset:
if i.instance.pk and i.instance.photo == '':
i.instance.delete()
elif i.cleaned_data:
temp = i.save(commit=False)
temp.listing = form.instance
temp.save() # Where the error happens
def clean_photo(self):
picture = self.cleaned_data.get('photo')
# I had to import ImageFieldFile. If picture is already uploaded, picture would still be retrieved as ImageFieldFile. The following line checks the variable type of `picture` to determine whether the cleaning should proceed.
if type(picture) != ImageFieldFile:
image_field = self.cleaned_data.get('photo')
image_file = BytesIO(image_field.read())
image = Image.open(image_file)
image = ImageOps.fit(image, (512,512,), Image.ANTIALIAS)
image_file = BytesIO()
image.save(image_file, 'JPEG', quality=90)
image_field.file = image_file
#if picture._size > 2*1024*1024:
#raise ValidationError("Image file too large. Max size is 2MB.")
return picture
class OwnerListingPhoto(models.Model):
listing = models.ForeignKey(OwnerListing, on_delete=models.CASCADE, related_name='owner_listing_photo')
photo = models.ImageField(upload_to=owner_listing_folder_name)
The issue is that new versions of Django default to using MemoryFileUploadHandler, which doesn't create a temporary file, and therefore there is no file "name." See related Django ticket.
You'll probably have to modify your code a bit to make this work, but you can at least start getting the name property by setting:
FILE_UPLOAD_HANDLERS = [
'django.core.files.uploadhandler.TemporaryFileUploadHandler',
]
In your settings.py file.
You may find the code I've used to solve almost the exact same issue as helpful.
def clean_logo_file(self):
logo_file_field = self.cleaned_data.get('logo_file')
if logo_file_field:
try:
logo_file = logo_file_field.file
with Image.open(logo_file_field.file.name) as image:
image.thumbnail((512, 512), Image.ANTIALIAS)
image.save(logo_file, format=image.format)
logo_file_field.file = logo_file
return logo_file_field
except IOError:
logger.exception("Error during image resize.")
Additional information on upload handlers.
If file is bigger than 2.5mb (2621440 bytes) - Django will
use TemporaryFileUploadHandler.
Otherwise Django will use MemoryFileUploadHandler.
You can change FILE_UPLOAD_MAX_MEMORY_SIZE (doc) in settings.py
Or change FILE_UPLOAD_HANDLERS (doc) as Nostalg.io mentioned above.
My example with Django Rest Framework serializers:
Broken code:
# models.py
class ImageModel(Model):
image = models.ImageField(upload_to='images/', null=False, blank=False)
# serializers.py
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = ImageModel
fields = ["id", "image"]
read_only_fields = ["id"]
def validate_image(self, user_img):
img = Image.open(user_img)
... # process image here
img_io = io.BytesIO()
img.save(img_io, format='JPEG', quality=100)
filename = "%s.jpg" % user_img.name.split('.')[0]
user_img.name = "%s.jpg" % user_img.name.split('.')[0]
user_img.file = img_io # BAD IDEA!!!
# This overrides django's tempfile._TemporaryFileWrapper() with _io.BytesIO() !!!
...
return user_img # if picture bigger than 2.5mb -> gives an error!
Fixed code:
#settings.py
FILE_UPLOAD_HANDLERS = [
'django.core.files.uploadhandler.TemporaryFileUploadHandler',
]
# serializers.py
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = ImageModel
fields = ["id", "image"]
read_only_fields = ["id"]
def validate_image(self, user_img):
img = Image.open(user_img)
... # process image here
# override old TemporaryFile image with edited image
path_to_tmp = user_img.file.name
new_filename = "%s.jpeg" % user_img.name.split('.')[0]
# set new image name
img.save(path_to_tmp, format='JPEG', quality=100)
user_img.name = new_filename
...
return user_img # no errors more :)
It might be more rational to process image by rewriting save() method in models.py, but I convert images in serializers.py because of handly ValidationError() :)

Django Form validate PIL Image as ImageField

Say I have a model and form that support file uploads:
class Foo(Document):
name = StringField()
file = FileField()
class FooForm(Form):
name = CharField()
file = ImageField()
def save(self):
Foo(name=self.cleaned_data['name'], file=self.cleaned_data['file']).save()
When POSTing from the actual browser form.is_valid() returns True, so we can call save()
When I go to use FooForm to take a PIL Image (specifically a <PIL.Image._ImageCrop image mode=RGB size=656x677 at 0x10F6812D8>), is_valid() is False because form.errors says:
load a valid image. The file you uploaded was either not an image or a corrupted image.
Here's what I'm trying to do to save the form:
img = ... our PIL image ...
post = {'name': name}
file = {'file': img}
form = FooForm(post, file)
if form.is_valid():
form.save()
See what I'm doing wrong that's causing is_valid() to be False?
Edit: I think this issue is more about coercing PIL Image to something BaseForm's files parameter accepts.
This ended up being my solution to get the FooForm to properly validate. I'm sure there's a better method.
img = ... our PIL image ...
buffer = StringIO()
img.save(buffer, 'png')
buffer.seek(0)
image_file = SimpleUploadedFile('foo.png', buffer.read(), content_type="image/png")
buffer.close()
post = {'name': name}
file = {'file': image_file}
form = FooForm(post, file)
if form.is_valid():
form.save()
I would recommend changing the form initialisation to use a simple dictionary like this:
img = ... our PIL image ...
form = FooForm({'name': name, 'file': img})
if form.is_valid():
form.save()

Import csv data into database in Django Admin

I've tried to import a csv file into a database by tweaking the modelform inside the admin doing this:
models.py:
class Data(models.Model):
place = models.ForeignKey(Places)
time = models.DateTimeField()
data_1 = models.DecimalField(max_digits=3, decimal_places=1)
data_2 = models.DecimalField(max_digits=3, decimal_places=1)
data_3 = models.DecimalField(max_digits=4, decimal_places=1)
Forms.py:
import csv
class DataImport(ModelForm):
file_to_import = forms.FileField()
class Meta:
model = Data
fields = ("file_to_import", "place")
def save(self, commit=False, *args, **kwargs):
form_input = DataImport()
self.place = self.cleaned_data['place']
file_csv = request.FILES['file_to_import']
datafile = open(file_csv, 'rb')
records = csv.reader(datafile)
for line in records:
self.time = line[1]
self.data_1 = line[2]
self.data_2 = line[3]
self.data_3 = line[4]
form_input.save()
datafile.close()
Admin.py:
class DataAdmin(admin.ModelAdmin):
list_display = ("place", "time")
form = DataImport
admin.site.register(Data, DataAdmin)
But i'm stuck trying to import the file i put in "file_to_import" field. Getting AttributeError in forms.py : 'function' object has no attribute 'FILES'.
What i'm doing wrong?
After a long search i found an answer: Create a view inside the admin using a standard form
Form:
class DataInput(forms.Form):
file = forms.FileField()
place = forms.ModelChoiceField(queryset=Place.objects.all())
def save(self):
records = csv.reader(self.cleaned_data["file"])
for line in records:
input_data = Data()
input_data.place = self.cleaned_data["place"]
input_data.time = datetime.strptime(line[1], "%m/%d/%y %H:%M:%S")
input_data.data_1 = line[2]
input_data.data_2 = line[3]
input_data.data_3 = line[4]
input_data.save()
The view:
#staff_member_required
def import(request):
if request.method == "POST":
form = DataInput(request.POST, request.FILES)
if form.is_valid():
form.save()
success = True
context = {"form": form, "success": success}
return render_to_response("imported.html", context,
context_instance=RequestContext(request))
else:
form = DataInput()
context = {"form": form}
return render_to_response("imported.html", context,
context_instance=RequestContext(request))
The rest is part of this post:
http://web.archive.org/web/20100605043304/http://www.beardygeek.com/2010/03/adding-views-to-the-django-admin/
Take a look at django-admin-import, it does more or less exactly what you want -- you can upload a XLS (not a CSV, but that should not matter) and lets you assign columns to model fields. Default values are also supported.
https://pypi.org/project/django-admin-import/
Additionally, it does not take away the possibility to modify individual records by hand because you don't have to replace the default model form used in the administration.
In the save() method, you don't have any access to the request object - you can see that it's not passed in. Normally you would expect to have a NameError there, but I suspect that you've got a function elsewhere in the file called request().
At the point of saving, all the relevant data should be in cleaned_data: so you should be able to do
file_csv = self.cleaned_data['file_to_import']
At that point you'll have another problem, which is when you get to open - you can't do that, as file_to_import is not a file on the server filesystem, it's an in-memory file that has been streamed from the client. You should be able to pass file_csv directly to csv.reader.