Opened 3 years ago

Last modified 6 months ago

#24823 new Bug

FileField with callable default raises error with forms

Reported by: Haussonne Yves-Marie Owned by: nobody
Component: Database layer (models, ORM) Version: 1.8
Severity: Normal Keywords: FileField, models, field, callable, default, form
Cc: real.human@… Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Haussonne Yves-Marie)

Hello,

When a FileField has a callable default value, the following error is raised when a new object is created using a form and no value is provided for the field:

'function' object has no attribute '_committed' in db/models/fields/files.py in pre_save, line 316

This bug can be reproduced with the content of the following gist : https://gist.github.com/ymph/cd2b684f3f2f38a680d5 and by following the instruction in the README.

The problem does not appear when creating the object without forms.

I was able to naively "correct" the problem by adding the following lines in the file db/models/fields/files.py, line 194 in FileDescriptor.__get__:

        if callable(file):
            file = file()

here is a complete traceback:

Environment:


Request Method: POST
Request URL: http://localhost:8000/admin/testdefault/testobject/add/

Django Version: 1.8.1
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',
 'testdefault')
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 "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  132.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/contrib/admin/options.py" in wrapper
  616.                 return self.admin_site.admin_view(view)(*args, **kwargs)
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapped_view
  110.                     response = view_func(request, *args, **kwargs)
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
  57.         response = view_func(request, *args, **kwargs)
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/contrib/admin/sites.py" in inner
  233.             return view(request, *args, **kwargs)
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/contrib/admin/options.py" in add_view
  1516.         return self.changeform_view(request, None, form_url, extra_context)
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapper
  34.             return bound_func(*args, **kwargs)
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapped_view
  110.                     response = view_func(request, *args, **kwargs)
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/utils/decorators.py" in bound_func
  30.                 return func.__get__(self, type(self))(*args2, **kwargs2)
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/utils/decorators.py" in inner
  145.                     return func(*args, **kwargs)
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/contrib/admin/options.py" in changeform_view
  1467.                 self.save_model(request, new_object, form, not add)
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/contrib/admin/options.py" in save_model
  1078.         obj.save()
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/db/models/base.py" in save
  710.                        force_update=force_update, update_fields=update_fields)
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/db/models/base.py" in save_base
  738.             updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/db/models/base.py" in _save_table
  822.             result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/db/models/base.py" in _do_insert
  861.                                using=using, raw=raw)
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/db/models/manager.py" in manager_method
  127.                 return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/db/models/query.py" in _insert
  920.         return query.get_compiler(using=using).execute_sql(return_id)
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in execute_sql
  970.             for sql, params in self.as_sql():
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in as_sql
  928.                 for obj in self.query.objs
File "/Users/ymh/dev/venvs/testdefault/lib/python2.7/site-packages/django/db/models/fields/files.py" in pre_save
  316.         if file and not file._committed:

Exception Type: AttributeError at /admin/testdefault/testobject/add/
Exception Value: 'function' object has no attribute '_committed'

regards.

Change History (8)

comment:1 Changed 3 years ago by Haussonne Yves-Marie

Description: modified (diff)

comment:2 Changed 3 years ago by Haussonne Yves-Marie

Hello,

a attempt for a correction can be found in the branch ticket_24823 of my fork : https://github.com/IRI-Research/django/tree/ticket_24823

regards,

ymph

comment:3 Changed 3 years ago by Tim Graham

Resolution: needsinfo
Status: newclosed

Currently the behavior of how default should work on FileField isn't well defined (see #17224). Let's address that one first and then reopen this if we decide it makes sense to support this. By the way, any patch should be against the master branch, as this wouldn't be backported to 1.8. Thanks!

comment:4 Changed 6 months ago by Tai Lee

Just spent a few hours stepping through tracebacks trying to find out exactly what Django is doing here, before coming to the conclusion that it must be a Django bug and searching for an existing ticket.

Perhaps I haven't read the documentation recently or thoroughly enough, but they don't seem that ambiguous regarding the behaviour of default specifically:

The default value for the field. This can be a value or a callable object. If callable it will be called every time a new object is created.

There's no special caveat for FileField and ImageField saying that callables cannot be used.

Whether or not additional special handling of the evaluated default value (as per discussion on #17224) is ever implemented, that should not make any difference at all as to whether or not a callable can be used as a default.

I think this ticket should be re-opened, and can and should be resolved much quicker and simpler than #17224, without much debate?

It's valid to assign a string to a FileField (on an instance, or as a default value). Is there any reason why a callable that returns a string shouldn't be valid?

comment:5 Changed 6 months ago by Tai Lee

Resolution: needsinfo
Status: closednew

comment:6 Changed 6 months ago by Tai Lee

Keywords: callable default form added

Plus, the fact that this actually does work as expected for FileField (models) is *only* broken with forms, makes the case stronger that this is a clear bug. The forms docs even say (of Field.initial):

Instead of a constant, you can also pass any callable

And when we stepped through form cleaning, we noticed that form data *did* contain a properly evaluated default as initial-{fieldname}, but the field cleaning ignored it and assigned the raw callable from the model instead.

comment:7 Changed 6 months ago by Tai Lee

Cc: real.human@… added

comment:8 Changed 6 months ago by Tim Graham

Summary: FileField with callable default raise error with formsFileField with callable default raises error with forms
Triage Stage: UnreviewedAccepted
Note: See TracTickets for help on using tickets.
Back to Top