Ticket #2983: 2983.diff
File 2983.diff, 4.9 KB (added by , 15 years ago) |
---|
-
django/db/models/fields/files.py
22 22 self.field = field 23 23 self.storage = field.storage 24 24 self._committed = True 25 self._replaced = [] 25 26 26 27 def __eq__(self, other): 27 28 # Older code may be expecting FileField values to be simple strings. … … 206 207 return instance.__dict__[self.field.name] 207 208 208 209 def __set__(self, instance, value): 210 if self.field.delete_replaced: 211 previous_file = instance.__dict__.get(self.field.name) 209 212 instance.__dict__[self.field.name] = value 213 if self.field.delete_replaced and previous_file: 214 if previous_file: 215 # Rather than just using value, we get the file from the 216 # instance, so that the __get__ logic of the file descriptor is 217 # processed. This ensures we will be dealing with a FileField 218 # (or subclass of FileField) instance. 219 file = getattr(instance, self.field.name) 220 # Remember that the previous file was replaced (along with any 221 # files which the previous file replaced too). 222 file._replaced += previous_file._replaced 223 file._replaced.append(previous_file) 210 224 211 225 class FileField(Field): 212 226 # The class to wrap instance attributes in. Accessing the file object off … … 216 230 # The descriptor to use for accessing the attribute off of the class. 217 231 descriptor_class = FileDescriptor 218 232 219 def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs): 233 def __init__(self, verbose_name=None, name=None, upload_to='', 234 storage=None, delete_replaced=False, **kwargs): 220 235 for arg in ('primary_key', 'unique'): 221 236 if arg in kwargs: 222 237 raise TypeError("'%s' is not a valid argument for %s." % (arg, self.__class__)) … … 225 240 self.upload_to = upload_to 226 241 if callable(upload_to): 227 242 self.generate_filename = upload_to 243 self.delete_replaced = delete_replaced 228 244 229 245 kwargs['max_length'] = kwargs.get('max_length', 100) 230 246 super(FileField, self).__init__(verbose_name, name, **kwargs) … … 247 263 def pre_save(self, model_instance, add): 248 264 "Returns field's value just before saving." 249 265 file = super(FileField, self).pre_save(model_instance, add) 250 if file and not file._committed: 251 # Commit the file to storage prior to saving the model 252 file.save(file.name, file, save=False) 266 if file: 267 if self.delete_replaced: 268 # Delete any files which this one replaced. 269 queryset = model_instance._default_manager.exclude( 270 pk=model_instance.pk) 271 for replaced_file in file._replaced: 272 if replaced_file._committed: 273 self.safe_delete_file(replaced_file, queryset) 274 file._replaced = [] 275 if not file._committed: 276 # Commit the file to storage prior to saving the model 277 file.save(file.name, file, save=False) 253 278 return file 254 279 255 280 def contribute_to_class(self, cls, name): … … 258 283 signals.post_delete.connect(self.delete_file, sender=cls) 259 284 260 285 def delete_file(self, instance, sender, **kwargs): 286 """ 287 Signal receiver which deletes an attached file from the backend when 288 the model is deleted. 289 """ 261 290 file = getattr(instance, self.attname) 262 # If no other object of this type references the file, 263 # and it's not the default value for future objects, 264 # delete it from the backend. 265 if file and file.name != self.default and \ 266 not sender._default_manager.filter(**{self.name: file.name}): 291 self.safe_delete_file(file, sender._default_manager.all()) 292 293 def safe_delete_file(self, file, queryset): 294 """ 295 Deletes the file from the backend if no objects in the queryset 296 reference the file and it's not the default value for future objects. 297 298 Otherwise, the file is simply closed so it doesn't tie up resources. 299 """ 300 if file: 301 queryset = queryset.filter(**{self.name: file.name}) 302 if file.name != self.default and not queryset: 267 303 file.delete(save=False) 268 elif file: 269 # Otherwise, just close the file, so it doesn't tie up resources. 270 file.close() 304 else: 305 file.close() 271 306 272 307 def get_directory_name(self): 273 308 return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to))))