3
votes

I'm using Cloudinary in my Django application to store and serve images that users upload in one view of my site. The images are getting uploaded and shown correctly; however, in my UpdateView when a user checks 'clear' to remove the previous image and then submits the form this error is shown:

TypeError: expected string or bytes-like object

The error page in the browser also shows these highlighted messages:

...\lib\site-packages\cloudinary\models.py in to_python return self.parse_cloudinary_resource(value) ...

...\lib\site-packages\cloudinary\models.py in parse_cloudinary_resource m = re.match(CLOUDINARY_FIELD_DB_RE, value) ...

...\AppData\Local\Programs\Python\Python36-32\lib\re.py in match return _compile(pattern, flags).match(string)

These are what my model, view and form look like:

models.py:

class Item(models.Model):
    name = models.CharField(max_length=255)
    image1 = CloudinaryField('image', blank=True, null=True)

views.py

class ItemUpdateView(LoginRequiredMixin, UpdateView):
    model = models.Item
    form_class = forms.ItemForm

forms.py

class ItemForm(forms.ModelForm):
    image1 = CloudinaryFileField(
            required=False,
            options = {'crop': 'limit', 'width': 546, 'height': 1000,})
    class Meta:
        model = models.Item
        fields = ("image1", "name")

I think Cloudinary is still expecting something when the field's value is empty. I have looked at the docs and searched the web and I just can't figure out how to fix this.

Edit: I checked my admin and tried to edit an item from there and got the same error when I checked the 'clear' checkbox and hit Save. So it seems that the problem is with how I've created the image1 field on my model, since the Admin view would only be relying on that. But I still don't know how to fix it.

Edit2: Full Traceback of when I check 'clear' on the image field and click Save, from the admin:

Environment:

Request Method: POST Request URL: http://127.0.0.1:8000/admin/items/item/4/change/

Django Version: 1.11.1 Python Version: 3.6.1 Installed Applications: ['django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.humanize',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'django.contrib.sites',

'cloudinary',

'allauth',

'allauth.account',

'allauth.socialaccount',

'allauth.socialaccount.providers.google',

'allauth.socialaccount.providers.twitter',

'haystack',

'items']

Installed Middleware: ['django.middleware.security.SecurityMiddleware',

'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']

Traceback:

File "D:\projects\django\gia\lib\site-packages\django\core\handlers\exception.py" in inner 41. response = get_response(request)

File "D:\projects\django\gia\lib\site-packages\django\core\handlers\base.py" in _get_response 187. response = self.process_exception_by_middleware(e, request)

File "D:\projects\django\gia\lib\site-packages\django\core\handlers\base.py" in _get_response 185. response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "D:\projects\django\gia\lib\site-packages\django\contrib\admin\options.py" in wrapper 551. return self.admin_site.admin_view(view)(*args, **kwargs)

File "D:\projects\django\gia\lib\site-packages\django\utils\decorators.py" in _wrapped_view 149. response = view_func(request, *args, **kwargs)

File "D:\projects\django\gia\lib\site-packages\django\views\decorators\cache.py" in _wrapped_view_func 57. response = view_func(request, *args, **kwargs)

File "D:\projects\django\gia\lib\site-packages\django\contrib\admin\sites.py" in inner 224. return view(request, *args, **kwargs)

File "D:\projects\django\gia\lib\site-packages\django\contrib\admin\options.py" in change_view 1511. return self.changeform_view(request, object_id, form_url, extra_context)

File "D:\projects\django\gia\lib\site-packages\django\utils\decorators.py" in _wrapper 67. return bound_func(*args, **kwargs)

File "D:\projects\django\gia\lib\site-packages\django\utils\decorators.py" in _wrapped_view 149. response = view_func(request, *args, **kwargs)

File "D:\projects\django\gia\lib\site-packages\django\utils\decorators.py" in bound_func 63. return func.get(self, type(self))(*args2, **kwargs2)

File "D:\projects\django\gia\lib\site-packages\django\contrib\admin\options.py" in changeform_view 1408. return self._changeform_view(request, object_id, form_url, extra_context)

File "D:\projects\django\gia\lib\site-packages\django\contrib\admin\options.py" in _changeform_view 1440. if form.is_valid():

File "D:\projects\django\gia\lib\site-packages\django\forms\forms.py" in is_valid 183. return self.is_bound and not self.errors

File "D:\projects\django\gia\lib\site-packages\django\forms\forms.py" in errors 175. self.full_clean()

File "D:\projects\django\gia\lib\site-packages\django\forms\forms.py" in full_clean 386. self._post_clean()

File "D:\projects\django\gia\lib\site-packages\django\forms\models.py" in _post_clean 396. self.instance.full_clean(exclude=exclude, validate_unique=False)

File "D:\projects\django\gia\lib\site-packages\django\db\models\base.py" in full_clean 1226. self.clean_fields(exclude=exclude)

File "D:\projects\django\gia\lib\site-packages\django\db\models\base.py" in clean_fields 1268. setattr(self, f.attname, f.clean(raw_value, self))

File "D:\projects\django\gia\lib\site-packages\django\db\models\fields__init__.py" in clean 601. value = self.to_python(value)

File "D:\projects\django\gia\lib\site-packages\cloudinary\models.py" in to_python 74. return self.parse_cloudinary_resource(value)

File "D:\projects\django\gia\lib\site-packages\cloudinary\models.py" in parse_cloudinary_resource 50. m = re.match(CLOUDINARY_FIELD_DB_RE, value)

File "C:\Users\samee\AppData\Local\Programs\Python\Python36-32\lib\re.py" in match 172. return _compile(pattern, flags).match(string)

Exception Type: TypeError at /admin/items/item/4/change/ Exception Value: expected string or bytes-like object

2
Post the full traceback.Håken Lid
Im a bit new to Django. Do you mean the full error page or some kind of error log? Thanks.Sameer Zahid
Yes. The part of the error page with the heading "traceback"Håken Lid
Please see Edit2.Sameer Zahid
@SameerZahid can you debug and check what type and value is the argument value in model.py line 50? parse_cloudinary_resource is invoked by to_python which checks value for None. Your error message indicates that some other value/type is being passed.tocker

2 Answers

2
votes

Maybe a bit late here but I had the same issue and in my search for a solution I stumbled upon this.

What I worked out was that the validation method was still looking for a object that no longer existed. By clicking the clear button you effectively remove it from the post back. Validation is set up by default to flag this up because it is trying to clea (validate) something that is now no longer there.

You get around it quite easily. Simply create the clean method that the is_valid method invokes. The picture wont be there anymore so just return None. Here is a code example:

From my is_valid():

if request.method == 'POST':
    if picture_form_set.is_valid():

which in turn calls the clean methods, so create one for the Cloudaniry field:

    def clean_picture(self):
    return self.cleaned_data['picture'] or None

Hope that helps

0
votes

The issue is with the to_python method in the the CloudinaryField, it expects the value of image to a none but instead gets a False. Solution create a new field that inherits from the parent CloudinaryField apply the fix and use that as your models field.

Solution by Marco Silva on this github issue, https://github.com/cloudinary/pycloudinary/issues/98.

class CloudinaryFieldFix(CloudinaryField):
    def to_python(self, value):
        if value is False:
            return value
        else:
            return super(FixCloudinaryField, self).to_python(value)