Annotate function with input param field value - python

I have a model
class Audiopart(models.Model):
book_id = models.ForeignKey(Audiobook, on_delete=models.CASCADE)
part_title = models.CharField(max_length = 255)
part_text = models.TextField(blank = True, null=True)
part_reader = models.CharField(max_length = 255)
filename = models.FileField(upload_to=get_upload_path, blank=True)
with function
def get_upload_path(instance, filename):
return os.path.join(
'audio',
instance.book_id.folder,
filename
)
I want to upload files in the directory and save in the database just filename (without path). But not found any way to do it.
So, I make a function for cleaning filename
def clear_filename(_filename):
if '/' in _filename:
_filename = _filename.split('/')[2]
return _filename
How can I print my objects in view with cleaned filename?
def audioparts(request, _book_id):
all_audioparts = list(Audiopart.objects.filter(book_id=_book_id, published=True).values())
return JsonResponse(all_audioparts, safe=False)

First of all, you're not understanding what the FileField is. It's actually for storing and accessing the file itself. So your field should not be called filename but file or audio_file or something that describes a file.
So when you have an instance of AudioPart, e.g. part, you can actually access the file directly: part.filename is the file in your code, not its name.
If you want a method to access just the name of the file, define a #property on your model:
import os
class AudioPart(Model):
file = FileField(...)
...
#property
def filename(self):
return os.path.basename(self.file.name)
then part.file is the file and part.filename is the name of the file. This way you can read() the file or return its url so people can download it (part.file.url) or manipulate it in other ways.
In your view, you can just loop over your files to add the filename to the list before returning your JSON.

Related

How can I avoid repeating a for loop in two different functions

I am writing an ImageCollection class in python that should hold a dictionary with a name and the image-object (pygame.image object).
In one case I want to load all images inside a folder to the dictionary and in another case just specific files, for example only button-files.
What I have written so far is this:
class ImageCollection:
def __init__(self):
self.dict = {}
def load_images(self, path):
directory = os.fsencode(path)
for file in os.listdir(directory):
file_name = os.fsdecode(file)
img_path = path + "/" + file_name
if file_name.endswith(".jpg") or file_name.endswith(".png"):
# Remove extension for dictionary entry name and add image to dictionary
#-----------------------------------------------------------------------
dict_entry_name = file_name.removesuffix(".jpg").removesuffix(".png")
self.dict.update({dict_entry_name: image.Image(img_path, 0)})
def load_specific_images(self, path, contains_str):
directory = os.fsencode(path)
for file in os.listdir(directory):
file_name = os.fsdecode(file)
img_path = path + "/" + file_name
if file_name.endswith(".jpg") or file_name.endswith(".png"):
if file_name.rfind(contains_str):
# Remove extension for dictionary entry name and add image to dictionary
#-----------------------------------------------------------------------
dict_entry_name = file_name.removesuffix(".jpg").removesuffix(".png")
self.dict.update({dict_entry_name: image.Image(img_path, 0)})
The only problem is that this is probably bad programming pattern, right? In this case it probably doesnt matter but I would like to know what the best-practice in this case would be.
How can I avoid repeating myself in two different functions when the only difference is just a single if condition?
I have tried creating a "dict_add" function that creates the entry.
Then I was thinking I could create two different functions, one which directly calls "dict_add" and the other one checks for the specific condition and then calls "dict_add".
Then I thought I could add create just a single function with the for-loop but pass a function as an argument (which would be a callback I assume?). But one callback would need an additional argument so thats where I got stuck and wondered if my approach was correct.
You could make the contains_str an optional argument.
In cases where you want to load_images - you just provide the path
In cases where you want to load specific images - you provide the path and the contains_str argument
In both cases you call load_images(...)
Code:
class ImageCollection:
def __init__(self):
self.dict = {}
def load_images(self, path, contains_str=""):
directory = os.fsencode(path)
for file in os.listdir(directory):
file_name = os.fsdecode(file)
img_path = path + "/" + file_name
if file_name.endswith(".jpg") or file_name.endswith(".png"):
if contains_str == "" or (contains_str != "" and file_name.rfind(contains_str)):
# Remove extension for dictionary entry name and add image to dictionary
#-----------------------------------------------------------------------
dict_entry_name = file_name.removesuffix(".jpg").removesuffix(".png")
self.dict.update({dict_entry_name: image.Image(img_path, 0)})

How do I get the orignal name of the file that is uploaded in the filefield in django

I want to access the name of the file uploaded to models.FileField() in my class.
Here is my class
class extract(models.Model):
doc = models.FileField(upload_to='files/')
Is there a function or some method, to access the original name of the uploaded file, without modifying the name during upload.
like something like this
name = doc.filename()
Edit - 2:
I'm very new to django and programming in general. So this might seem stupid but I want something like this:
class extract(models.Model):
def get_name(instance,filename):
path = 'files'
name = filename
return name,path
doc = models.FileField(fname,upload_to==get_name)
def ex(fname):
path0 = "E:/Projects/PDF Converter/pdf/files/"
fullpath = path0 + fname
document = open(fullpath)
reader = PyPDF2.PdfFileReader(document)
page1 = reader.getPage(0)
data = page1.extractText()
return data
text = models.TextField(default=ex(fname),editable=False)
Per the documentation, this can be accomplished by passing a callback function as the upload_to argument, and the callback function will be called with the given filename as the second argument:
def get_filename(_, filename):
print(filename) # or do whatever you want with the original filename here
return 'files/'
class extract(models.Model):
doc = models.FileField(upload_to=get_filename)

how to override django files?

I have a function that crops user images but I don't want the model to have 2 fields so I made a function that overrides the original file and I noticed that the function works well on normal files but when I add the function to the view new file is made but at the media directory not even the the specified folder so how can i override files by Django ?
models.py
# defining directory for every patient
def user_directory_path(instance, filename):
# file will be uploaded to MEDIA_ROOT/patient_<id>/<filename>
return 'patient_{0}/{1}'.format(instance.patient.id, filename)
class UploadedImages(models.Model):
patient = models.ForeignKey(Patient,on_delete=models.CASCADE,related_name='images')
pre_analysed = models.ImageField(upload_to = user_directory_path ,
verbose_name = 'Image')
upload_time = models.DateTimeField(default=timezone.now)
the cropping function:
import os
from PIL import Image
def crop(corrd, file ,path,pk):
image = Image.open(file) #To open the image as file.path won't open
path_1 , fn = os.path.split(path) #which is MEDIA_ROOT/and <filename>
patient_dir = 'patient_{}'.format(pk) #to get the user directory
path_ = path_1+patient_dir+fn #MEDIA_ROOT/patient_<id>/<filename>
cropped_image = image.crop(corrd)
resized_image = cropped_image.resize((384, 384), Image.ANTIALIAS)
resized_image.save(path_)
return path_
views.py
if form.is_valid():
image = form.save(commit=False)
x = float(request.POST.get('x'))
y = float(request.POST.get('y'))
w = float(request.POST.get('width'))
h = float(request.POST.get('height'))
print(x)
print(y)
print(w)
print(h)
crop((x,y,w+x,y+h),image.pre_analysed,image.pre_analysed.path)
image.patient = patient
messages.success(request,"Image added successfully!")
image.save()
forms.py
class ImageForm(ModelForm):
x = forms.FloatField(widget=forms.HiddenInput())
y = forms.FloatField(widget=forms.HiddenInput())
width = forms.FloatField(widget=forms.HiddenInput())
height = forms.FloatField(widget=forms.HiddenInput())
class Meta:
model = UploadedImages
fields = ('pre_analysed', 'x', 'y', 'width', 'height', )
so what do I need to do here?
thanks in advance.
The path you get from .path is relative to your MEDIA_ROOT folder, which may prevent your crop function from being able to open() the image file.
You can then make a new cropped file somewhere in your MEDIA_ROOT. Be sure to use os.makedirs() to create all the directories between here and the file. Also I don't see you saving the path returned from crop() back into the ImageField.

Thumbnail for ImageField in django model, override save

I am trying to create a thumbnail from ImageField and save it to my model. I am using this as reference: http://www.yilmazhuseyin.com/blog/dev/create-thumbnails-imagefield-django/
Files are created correctly, I get no errors but fields are not saved in model.
I think i could messed things up in save method. What am I doing wrong?
Here is my code:
def get_avatar_path(instance, filename):
# file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
return 'avatars/{0}/{1}'.format(instance.id, filename)
class User(AbstractUser):
# First Name and Last Name do not cover name patterns
# around the globe.
name = models.CharField(_('Name of User'), blank=True, max_length=255)
avatar = models.ImageField(upload_to=get_avatar_path, blank=True)
small_avatar = models.ImageField(upload_to=get_avatar_path,
blank=True)
def create_thumbnail(self):
# If there is no image associated with this.
# do not create thumbnail
if not self.avatar:
return
# Set our max thumbnail size in a tuple (max width, max height)
THUMBNAIL_SIZE = (200, 200)
DJANGO_TYPE = self.avatar.file.content_type
print DJANGO_TYPE
if DJANGO_TYPE == 'image/jpeg':
PIL_TYPE = 'jpeg'
FILE_EXTENSION = 'jpg'
elif DJANGO_TYPE == 'image/png':
PIL_TYPE = 'png'
FILE_EXTENSION = 'png'
elif DJANGO_TYPE == 'image/gif':
PIL_TYPE = 'gif'
FILE_EXTENSION = 'gif'
# Open original photo which we want to thumbnail using PIL's Image
image = Image.open(StringIO(self.avatar.read()))
# use our PIL Image object to create the thumbnail, which already
image.thumbnail(THUMBNAIL_SIZE, Image.ANTIALIAS)
# Save the thumbnail
temp_handle = StringIO()
image.save(temp_handle, PIL_TYPE)
temp_handle.seek(0)
# Save image to a SimpleUploadedFile which can be saved into ImageField
print os.path.split(self.avatar.name)[-1]
suf = SimpleUploadedFile(os.path.split(self.avatar.name)[-1],
temp_handle.read(), content_type=DJANGO_TYPE)
# Save SimpleUploadedFile into image field
print os.path.splitext(suf.name)[0]
self.small_avatar.save(
'%s_thumbnail.%s' % (os.path.splitext(suf.name)[0], FILE_EXTENSION),
suf, save=False)
def save(self, *args, **kwargs):
self.create_thumbnail()
super(User, self).save()
Thanks for feedback. Sorry for newbie problems, I am a beginner developer.
Problem seems to be resolved by invalidating caches, restarting IDE and services.
In my opinion code above should be persisted as some of solutions on the web are outdated.

how to change the title of an image upload to a name in a class in django

I know this has been asked a couple of times, but I am new to django and couldn't find that in the documentation or here. I want to upload images to a class and the name should have the same name as the name of the class.
models:
import os
def upload_to_company(instance, filename):
blocks = filename.split('.')
ext = blocks[-1]
filename = "%s.%s" % (Cars.name, ext)
instance.title = blocks[0]
return os.path.join('media/', filename)
class Cars(models.Model):
image_file = models.ImageField(upload_to=upload_to_company, null=True, blank=True)
name = models.CharField(max_length=200)
How do I get the name of the cars class? I thought Cars.name will do it but it doesn't.
Can anyone help?
Thanks !
to get the class' name use
Cars.__name__
If you go through an object (e.g. camaro = Cars()), use
camaro.__class__.__name__
In case you want to name of the image to depend on one of the other model attributes (e.g. name)
def upload_to_company(instance, filename):
if hasattr(instance, 'imgpath'):
if os.path.exists(instance.imgpath):
os.remove(instance.imgpath)
blocks = filename.split('.')
ext = blocks[-1]
filename = "%s.%s" % (instance.name, ext)
instance.title = blocks[0]
instance.imgpath = os.path.join('media', filename)
return instance.imgpath
Note: that will always replace the existing file.
And finally, get your indentation right: the Cars class appears to be defined within the upload_to_company function.

Categories