Index: django/db/models/fields/__init__.py
===================================================================
--- django/db/models/fields/__init__.py (revision 4421)
+++ django/db/models/fields/__init__.py (working copy)
@@ -660,6 +665,12 @@
f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename)))
return os.path.normpath(f)
+ def formfield(self, **kwargs):
+ defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'widget':forms.FileInput}
+ defaults.update(kwargs)
+ return forms.FileField(**defaults)
+
+
class FilePathField(Field):
def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs):
self.path, self.match, self.recursive = path, match, recursive
@@ -710,6 +721,14 @@
empty_strings_allowed = False
def get_manipulator_field_objs(self):
return [oldforms.IntegerField]
+
+ def to_python(self, value):
+ if value is None:
+ return value
+ try:
+ return int(value)
+ except (TypeError, ValueError):
+ raise validators.ValidationError, gettext("This value must be an integer.")
def formfield(self, **kwargs):
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name)}
Index: django/newforms/models.py
===================================================================
--- django/newforms/models.py (revision 4421)
+++ django/newforms/models.py (working copy)
@@ -13,11 +18,36 @@
This method is created for any form_for_model Form.
"""
+ from django.db import models
+
if self.errors:
raise ValueError("The %s could not be created because the data didn't validate." % self._model._meta.object_name)
- obj = self._model(**self.clean_data)
+ # The default initializer does not understand FileField data, we need to intercept.
+ init_data = self.clean_data.copy()
+ extra_data = {}
+
+ # Strip out data the model initializer can't handle, saving it in extra_data
+ opts = self._model._meta
+ for f in opts.fields:
+ if isinstance(f, models.FileField):
+ extra_data[f.name] = init_data.pop(f.name, [None, ''])
+
+ obj = self._model(**init_data)
+
if commit:
obj.save()
+
+ # Now process that data we stripped out.
+ # (We do this after the object is saved because we want to ensure the object
+ # gets saved before we save the file. Otherwise it could become orphaned.)
+ for f in opts.fields:
+ if isinstance(f, models.FileField):
+ file_data, path_val_data = extra_data[f.name]
+ # only save if there is a file
+ if file_data is not None:
+ func = getattr(obj, 'save_%s_file' % f.name)
+ func(file_data['filename'], file_data['content'])
+
return obj
def save_instance(form, instance, commit=True):
@@ -36,9 +66,25 @@
for f in opts.fields + opts.many_to_many:
if isinstance(f, models.AutoField):
continue
- setattr(instance, f.attname, clean_data[f.name])
+ # Skip FileFields, we don't want to process the upload yet, and that's
+ # the only way to change the value anyways.
+ if isinstance(f, models.FileField) and commit:
+ continue
+ setattr(instance, f.name, clean_data[f.name])
if commit:
instance.save()
+
+ # Now save any uploaded files.
+ for f in opts.fields:
+ if isinstance(f, models.FileField):
+ file_data = clean_data[f.name][0]
+ # if there was an upload, save the file
+ if file_data is not None:
+ func = getattr(instance, 'save_%s_file' % f.name)
+ func(file_data['filename'], file_data['content'])
+ # otherwise we just want to leave the value intact...
+ # (but if we did want to set it, it is in clean_data[f.name][1]
+
return instance
def make_instance_save(instance):
Index: django/newforms/fields.py
===================================================================
--- django/newforms/fields.py (revision 4421)
+++ django/newforms/fields.py (working copy)
@@ -10,7 +10,7 @@
import time
__all__ = (
- 'Field', 'CharField', 'IntegerField',
+ 'Field', 'CharField', 'FileField', 'IntegerField',
'DEFAULT_DATE_INPUT_FORMATS', 'DateField',
'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
@@ -105,6 +105,14 @@
if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
return {'maxlength': str(self.max_length)}
+class FileField(Field):
+ def __init__(self, required=True, widget=None, label=None, initial=None):
+ super(FileField, self).__init__(required, widget, label, initial)
+
+ def clean(self, value):
+ super(FileField, self).clean(value)
+ return value
+
class IntegerField(Field):
def __init__(self, max_value=None, min_value=None, required=True, widget=None, label=None, initial=None):
self.max_value, self.min_value = max_value, min_value
Index: django/newforms/widgets.py
===================================================================
--- django/newforms/widgets.py (revision 4421)
+++ django/newforms/widgets.py (working copy)
@@ -108,6 +108,23 @@
class FileInput(Input):
input_type = 'file'
+ def render(self, name, value, attrs=None, choices=()):
+ if value is None: value = ''
+ file_attrs = self.build_attrs(attrs, type='file', name=name+'_file')
+ final_attrs = self.build_attrs(attrs, type='hidden', name=name)
+ if value != '': final_attrs['value'] = smart_unicode(value) # Only add the 'value' attribute if a value is non-empty.
+
+ if value != '':
+ currently = u'Currently: %s Change: ' % smart_unicode(value)
+ else:
+ currently = u''
+
+ return u'%s ' % (currently, flatatt(file_attrs), flatatt(final_attrs))
+
+ def value_from_datadict(self, data, name):
+ return [data.get(name+'_file', None), data.get(name, None)]
+
+
class Textarea(Widget):
def render(self, name, value, attrs=None):
if value is None: value = ''