@@ -153,18 +153,15 @@ class StdImageField(ImageField):
153153 Django ImageField that is able to create different size variations.
154154
155155 Extra features are:
156- - Django-Storages compatible (S3)
157- - Python 2, 3 and PyPy support
158- - Django 1.5 and later support
159- - Resize images to different sizes
160- - Access thumbnails on model level, no template tags required
161- - Preserves original image
162- - Asynchronous rendering (Celery & Co)
163- - Multi threading and processing for optimum performance
164- - Restrict accepted image dimensions
165- - Rename files to a standardized name (using a callable upload_to)
166-
167- :param variations: size variations of the image
156+
157+ - Django-Storages compatible (S3)
158+ - Access thumbnails on model level, no template tags required
159+ - Preserves original image
160+ - Asynchronous rendering (Celery & Co)
161+ - Multi threading and processing for optimum performance
162+ - Restrict accepted image dimensions
163+ - Rename files to a standardized name (using a callable upload_to)
164+
168165 """
169166
170167 descriptor_class = StdImageFileDescriptor
@@ -177,19 +174,34 @@ class StdImageField(ImageField):
177174 }
178175
179176 def __init__ (self , verbose_name = None , name = None , variations = None ,
180- render_variations = True , force_min_size = False ,
181- * args , * *kwargs ):
177+ render_variations = True , force_min_size = False , delete_orphans = False ,
178+ ** kwargs ):
182179 """
183180 Standardized ImageField for Django.
184181
185- Usage: StdImageField(upload_to='PATH',
186- variations={'thumbnail': {"width", "height", "crop", "resample"}})
187- :param variations: size variations of the image
188- :rtype variations: StdImageField
189- :param render_variations: boolean or callable that returns a boolean.
190- The callable gets passed the app_name, model, field_name and pk.
191- Default: True
192- :rtype render_variations: bool, callable
182+ Usage::
183+
184+ StdImageField(
185+ upload_to='PATH',
186+ variations={
187+ 'thumbnail': {"width", "height", "crop", "resample"},
188+ },
189+ delete_orphans=True,
190+ )
191+
192+ Args:
193+ variations (dict):
194+ Different size variations of the image.
195+ render_variations (bool, callable):
196+ Boolean or callable that returns a boolean. If True, the built-in
197+ image render will be used. The callable gets passed the ``app_name``,
198+ ``model``, ``field_name`` and ``pk``. Default: ``True``
199+ delete_orphans (bool):
200+ If ``True``, files orphaned files will be removed in case a new file
201+ is assigned or the field is cleared. This will only remove work for
202+ Django forms. If you unassign or reassign a field in code, you will
203+ need to remove the orphaned files yourself.
204+
193205 """
194206 if not variations :
195207 variations = {}
@@ -207,6 +219,7 @@ def __init__(self, verbose_name=None, name=None, variations=None,
207219 self .force_min_size = force_min_size
208220 self .render_variations = render_variations
209221 self .variations = {}
222+ self .delete_orphans = delete_orphans
210223
211224 for nm , prm in list (variations .items ()):
212225 self .add_variation (nm , prm )
@@ -219,7 +232,7 @@ def __init__(self, verbose_name=None, name=None, variations=None,
219232 key = lambda x : x ["height" ])["height" ]
220233 )
221234
222- super ().__init__ (verbose_name , name , * args , ** kwargs )
235+ super ().__init__ (verbose_name = verbose_name , name = name , ** kwargs )
223236
224237 def add_variation (self , name , params ):
225238 variation = self .def_variation .copy ()
@@ -253,12 +266,24 @@ def set_variations(self, instance=None, **kwargs):
253266 variation_name )
254267 setattr (field , name , variation_field )
255268
269+ def post_delete_callback (self , sender , instance , ** kwargs ):
270+ getattr (instance , self .name ).delete (False )
271+
256272 def contribute_to_class (self , cls , name ):
257273 """Generate all operations on specified signals."""
258274 super ().contribute_to_class (cls , name )
259275 signals .post_init .connect (self .set_variations , sender = cls )
276+ if self .delete_orphans :
277+ signals .post_delete .connect (self .post_delete_callback , sender = cls )
260278
261279 def validate (self , value , model_instance ):
262280 super ().validate (value , model_instance )
263281 if self .force_min_size :
264282 MinSizeValidator (self .min_size [0 ], self .min_size [1 ])(value )
283+
284+ def save_form_data (self , instance , data ):
285+ if self .delete_orphans and self .blank and (data is False or data is not None ):
286+ file = getattr (instance , self .name )
287+ if file and file ._committed and file != data :
288+ file .delete (save = False )
289+ super ().save_form_data (instance , data )
0 commit comments