1

I have put an ImageField on my Django model, and with the pre-existent profiles it works (the image upload), but I can't delete the image from the admin panel and I can't create a new user.
The thrown error is:

ValueError at /admin/custom_user/customuser/1/
The 'avatar' attribute has no file associated with it.
Request Method: POST
Request URL: http://localhost:8000/admin/custom_user/customuser/1/
Django Version: 1.8
Exception Type: ValueError
Exception Value: The 'avatar' attribute has no file associated with it.
Exception Location: /home/stefano/projects/blog-project/blogprojectenv/local/lib/python2.7/site-packages/django/db/models/fields/files.py in _require_file, line 46
Python Executable: /home/stefano/projects/blog-project/blogprojectenv/bin/python
Python Version: 2.7.6
Python Path:
['/home/stefano/projects/blog-project',
'/home/stefano/projects/blog-project/blogprojectenv/lib/python2.7',
'/home/stefano/projects/blog-project/blogprojectenv/lib/python2.7/plat-x86_64-linux-gnu',
'/home/stefano/projects/blog-project/blogprojectenv/lib/python2.7/lib-tk',
'/home/stefano/projects/blog-project/blogprojectenv/lib/python2.7/lib-old',
'/home/stefano/projects/blog-project/blogprojectenv/lib/python2.7/lib-dynload',
'/usr/lib/python2.7',
'/usr/lib/python2.7/plat-x86_64-linux-gnu',
'/usr/lib/python2.7/lib-tk',
'/home/stefano/projects/blog-project/blogprojectenv/local/lib/python2.7/site-packages'] Server time: Thu, 24 Mar 2016 13:12:38 +0000

This is the model

class CustomUser(AbstractBaseUser, PermissionsMixin):
    first_name   = models.CharField(max_length=254, blank=True)
    second_name  = models.CharField(max_length=254, blank=True)
    email        = models.EmailField(blank=True, unique=True)
    date_joined  = models.DateTimeField(_('date joined'), default=datetime.now())
    avatar       = models.ImageField('profile picture', upload_to='images/avatars/', null=True, blank=True)
    is_active    = models.BooleanField(default=True)
    is_admin     = models.BooleanField(default=False)
    is_staff     = models.BooleanField(default=False)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['first_name', 'second_name']

    objects = CustomUserManager()

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')

    def save(self, *args, **kwargs):
        pil_image_obj = Image.open(self.avatar)
        new_image = resizeimage.resize_width(pil_image_obj, 300)

        new_image_io = BytesIO()
        new_image.save(new_image_io, format='JPEG')

        temp_name = self.avatar.name
        self.avatar.delete(save=False)

        self.avatar.save(
            temp_name,
            content=ContentFile(new_image_io.getvalue()),
            save=False
        )

        super(CustomUser, self).save(*args, **kwargs)

I've tried to put null=True and blank=True on the model, but the error is still there. I've tried to put required=False on "init" method in forms.py

class CustomUserChangeForm(forms.ModelForm):
    email = forms.EmailField(label='', required=True, widget = forms.TextInput(
        attrs = {
            'placeholder': 'E-Mail',
            'class': 'form-control'
        }
    ))

    first_name = forms.CharField(label='', required=True, widget=forms.TextInput(
        attrs = {
            'placeholder': 'First name',
            'class': 'form-control'
        }
    ))

    second_name = forms.CharField(label='', required=True, widget=forms.TextInput(
        attrs = {
            'placeholder': 'Second name',
            'class': 'form-control'
        }
    ))

    avatar = forms.ImageField(label='', required=False, widget=forms.FileInput(
        attrs = {
            'class': 'form-control',
            'label': 'Profile pic'
        }
    ))

    class Meta:
        model = CustomUser
        fields = ('email', 'first_name', 'second_name', 'avatar')

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user', None)
        super(CustomUserChangeForm, self).__init__(*args, **kwargs)
        self.fields['avatar'].required = False

    def clean_email_address(self):
        email = self.cleaned_data.get('email')
        if self.user and self.user.email == email:
            return email
        if CustomUser.objects.filter(email=email).count():
            raise forms.ValidationError(u'That email address already exists.')
        return email

    def save(self, commit=True):
        user = super(CustomUserChangeForm, self).save(commit=False)
        user.email = self.cleaned_data['email']

        if commit:
            user.save()

        return user

Still there.

What can be done?

EDIT: Here's the complete traceback

Environment:


Request Method: POST
Request URL: http://localhost:8000/admin/custom_user/customuser/1/

Django Version: 1.8
Python Version: 2.7.6
Installed Applications:
('django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'blog',
 'custom_user',
 'django_markdown',
 'parsley')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'django.middleware.security.SecurityMiddleware')


Traceback:
File "/home/stefano/projects/blog-project/blogprojectenv/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  132.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/stefano/projects/blog-project/blogprojectenv/local/lib/python2.7/site-packages/django/contrib/admin/options.py" in wrapper
  616.                 return self.admin_site.admin_view(view)(*args, **kwargs)
File "/home/stefano/projects/blog-project/blogprojectenv/local/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapped_view
  110.                     response = view_func(request, *args, **kwargs)
File "/home/stefano/projects/blog-project/blogprojectenv/local/lib/python2.7/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
  57.         response = view_func(request, *args, **kwargs)
File "/home/stefano/projects/blog-project/blogprojectenv/local/lib/python2.7/site-packages/django/contrib/admin/sites.py" in inner
  233.             return view(request, *args, **kwargs)
File "/home/stefano/projects/blog-project/blogprojectenv/local/lib/python2.7/site-packages/django/contrib/admin/options.py" in change_view
  1519.         return self.changeform_view(request, object_id, form_url, extra_context)
File "/home/stefano/projects/blog-project/blogprojectenv/local/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapper
  34.             return bound_func(*args, **kwargs)
File "/home/stefano/projects/blog-project/blogprojectenv/local/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapped_view
  110.                     response = view_func(request, *args, **kwargs)
File "/home/stefano/projects/blog-project/blogprojectenv/local/lib/python2.7/site-packages/django/utils/decorators.py" in bound_func
  30.                 return func.__get__(self, type(self))(*args2, **kwargs2)
File "/home/stefano/projects/blog-project/blogprojectenv/local/lib/python2.7/site-packages/django/utils/decorators.py" in inner
  145.                     return func(*args, **kwargs)
File "/home/stefano/projects/blog-project/blogprojectenv/local/lib/python2.7/site-packages/django/contrib/admin/options.py" in changeform_view
  1467.                 self.save_model(request, new_object, form, not add)
File "/home/stefano/projects/blog-project/blogprojectenv/local/lib/python2.7/site-packages/django/contrib/admin/options.py" in save_model
  1078.         obj.save()
File "/home/stefano/projects/blog-project/custom_user/models.py" in save
  66.       pil_image_obj = Image.open(self.avatar)
File "/home/stefano/projects/blog-project/blogprojectenv/local/lib/python2.7/site-packages/PIL/Image.py" in open
  2261.         fp.seek(0)
File "/home/stefano/projects/blog-project/blogprojectenv/local/lib/python2.7/site-packages/django/core/files/utils.py" in <lambda>
  20.     seek = property(lambda self: self.file.seek)
File "/home/stefano/projects/blog-project/blogprojectenv/local/lib/python2.7/site-packages/django/db/models/fields/files.py" in _get_file
  49.         self._require_file()
File "/home/stefano/projects/blog-project/blogprojectenv/local/lib/python2.7/site-packages/django/db/models/fields/files.py" in _require_file
  46.             raise ValueError("The '%s' attribute has no file associated with it." % self.field.name)

Exception Type: ValueError at /admin/custom_user/customuser/1/
Exception Value: The 'avatar' attribute has no file associated with it.
Stefano De Rosso
  • 1,309
  • 1
  • 14
  • 27

2 Answers2

1

I think that a condition asking if self.avatar before opening the avatar image could work because if there is not an avatar there is not reason to delete it, from my point of view. Something like this:

def save(self, *args, **kwargs):
    if self.avatar:
        pil_image_obj = Image.open(self.avatar)
        new_image = resizeimage.resize_width(pil_image_obj, 300)

        new_image_io = BytesIO()
        new_image.save(new_image_io, format='JPEG')

        temp_name = self.avatar.name
        self.avatar.delete(save=False)

        self.avatar.save(
            temp_name,
            content=ContentFile(new_image_io.getvalue()),
            save=False
        )

    super(CustomUser, self).save(*args, **kwargs)

I hope this can help you to find the solution. Thanks :)

Vladir Parrado Cruz
  • 2,301
  • 21
  • 27
0

When you creating an instance there is no avatar file stored on disk. So you try to open not existed file.

I see you want here to delete old file after update?

So you don't need to override save method. What you should do is to use pre_save, post_save and post_delete methods.

On pre_save you store current avatar, and after update on post_save you should check new instance's file path with old one. If it is different then delete it by os.remove

On post_delete just remove file.

Signals

Andrey Nelubin
  • 3,084
  • 1
  • 20
  • 25