Django Value Error "seek of closed file" using PIL for image resize when updating a record
Question:
I have the following model:
class Players(models.Model):
team = models.ForeignKey(Teams, verbose_name=_('Team'), on_delete=models.CASCADE)
player_name = models.CharField(_('Player Name'),max_length=200)
player_description = models.TextField(_('Player Description'))
player_image = models.ImageField(_('Profile Pic'),upload_to='upload/player_image', null=True, blank=True)
player_social = models.CharField(_('Social Media Tag'),max_length=200)
class Meta:
verbose_name = _("Players")
verbose_name_plural = _("Players")
def __str__(self):
return self.team.team_name + ' - ' + self.player_name
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.player_image:
image_resize(self.player_image, 250)
The last function will call another for image resizing taking the file and an expected max width:
# Use PIL to resize images; set target width on each
def image_resize(image, tgt_width):
img = PIL.Image.open(image)
img.load()
width, height = img.size
target_width = tgt_width
h_coefficient = width/tgt_width
target_height = height/h_coefficient
img = img.resize((int(target_width), int(target_height)), PIL.Image.ANTIALIAS)
img.save(image.path, quality=100)
img.close()
image.close()
My view for updating is as follows:
@method_decorator(superuser_required, name='dispatch')
class PlayerUpdateView(UpdateView):
model = Players
template_name = 'scoreboard/players/player_update.html'
form_class = PlayersForm
context_object_name: str = 'player'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
team_id = Players.objects.get(pk=self.kwargs['pk']).team.id
context["team"] = Teams.objects.get(pk=team_id)
return context
def form_valid(self, form):
form.save()
return super().form_valid(form)
def get_success_url(self):
team_id = Players.objects.get(pk=self.kwargs['pk']).team.id
return reverse_lazy('team_detail', kwargs={'pk': team_id})
It doesn’t matter if I provide a new image file or not, I get the same "seek of closed file" error:
File "D: 0_wwwhts-scoreoverlayscoreboardmodels.py", line 62, in save
image_resize(self.player_image, 250)
File "D: 0_wwwhts-scoreoverlayscoreboardmodels.py", line 15, in image_resize
img = PIL.Image.open(image)
File "D: 0_wwwhts-scorevenvlibsite-packagesPILImage.py", line 3096, in open
fp.seek(0)
ValueError: seek of closed file
How can I get the image to be processed anyway? What am I missing?
I tried adding the if method so in case there is no file, it won’t trigger the resize function. I was expecting the function to check for the presence of an image file, then process it. Adding img.load() doesn’t help.
Answers:
So, after a few tries I found this answer. I’m not very sure why it works, but it seems like the with
method opens and closes the file correctly. Found this on PIL Documentation. The image_resize()
function will look like this:
def image_resize(image, tgt_width):
with Image.open(image) as img:
width, height = img.size
ratio = width / height
tgt_height = int(tgt_width / ratio)
img = img.resize((tgt_width, tgt_height), Image.ANTIALIAS)
img.save(image.path)
With the if
condition at the save method, it works every time.
I have the following model:
class Players(models.Model):
team = models.ForeignKey(Teams, verbose_name=_('Team'), on_delete=models.CASCADE)
player_name = models.CharField(_('Player Name'),max_length=200)
player_description = models.TextField(_('Player Description'))
player_image = models.ImageField(_('Profile Pic'),upload_to='upload/player_image', null=True, blank=True)
player_social = models.CharField(_('Social Media Tag'),max_length=200)
class Meta:
verbose_name = _("Players")
verbose_name_plural = _("Players")
def __str__(self):
return self.team.team_name + ' - ' + self.player_name
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.player_image:
image_resize(self.player_image, 250)
The last function will call another for image resizing taking the file and an expected max width:
# Use PIL to resize images; set target width on each
def image_resize(image, tgt_width):
img = PIL.Image.open(image)
img.load()
width, height = img.size
target_width = tgt_width
h_coefficient = width/tgt_width
target_height = height/h_coefficient
img = img.resize((int(target_width), int(target_height)), PIL.Image.ANTIALIAS)
img.save(image.path, quality=100)
img.close()
image.close()
My view for updating is as follows:
@method_decorator(superuser_required, name='dispatch')
class PlayerUpdateView(UpdateView):
model = Players
template_name = 'scoreboard/players/player_update.html'
form_class = PlayersForm
context_object_name: str = 'player'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
team_id = Players.objects.get(pk=self.kwargs['pk']).team.id
context["team"] = Teams.objects.get(pk=team_id)
return context
def form_valid(self, form):
form.save()
return super().form_valid(form)
def get_success_url(self):
team_id = Players.objects.get(pk=self.kwargs['pk']).team.id
return reverse_lazy('team_detail', kwargs={'pk': team_id})
It doesn’t matter if I provide a new image file or not, I get the same "seek of closed file" error:
File "D: 0_wwwhts-scoreoverlayscoreboardmodels.py", line 62, in save
image_resize(self.player_image, 250)
File "D: 0_wwwhts-scoreoverlayscoreboardmodels.py", line 15, in image_resize
img = PIL.Image.open(image)
File "D: 0_wwwhts-scorevenvlibsite-packagesPILImage.py", line 3096, in open
fp.seek(0)
ValueError: seek of closed file
How can I get the image to be processed anyway? What am I missing?
I tried adding the if method so in case there is no file, it won’t trigger the resize function. I was expecting the function to check for the presence of an image file, then process it. Adding img.load() doesn’t help.
So, after a few tries I found this answer. I’m not very sure why it works, but it seems like the with
method opens and closes the file correctly. Found this on PIL Documentation. The image_resize()
function will look like this:
def image_resize(image, tgt_width):
with Image.open(image) as img:
width, height = img.size
ratio = width / height
tgt_height = int(tgt_width / ratio)
img = img.resize((tgt_width, tgt_height), Image.ANTIALIAS)
img.save(image.path)
With the if
condition at the save method, it works every time.