@@ -153,18 +153,15 @@ class StdImageField(ImageField):
153
153
Django ImageField that is able to create different size variations.
154
154
155
155
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
+
168
165
"""
169
166
170
167
descriptor_class = StdImageFileDescriptor
@@ -177,19 +174,34 @@ class StdImageField(ImageField):
177
174
}
178
175
179
176
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 ):
182
179
"""
183
180
Standardized ImageField for Django.
184
181
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
+
193
205
"""
194
206
if not variations :
195
207
variations = {}
@@ -207,6 +219,7 @@ def __init__(self, verbose_name=None, name=None, variations=None,
207
219
self .force_min_size = force_min_size
208
220
self .render_variations = render_variations
209
221
self .variations = {}
222
+ self .delete_orphans = delete_orphans
210
223
211
224
for nm , prm in list (variations .items ()):
212
225
self .add_variation (nm , prm )
@@ -219,7 +232,7 @@ def __init__(self, verbose_name=None, name=None, variations=None,
219
232
key = lambda x : x ["height" ])["height" ]
220
233
)
221
234
222
- super ().__init__ (verbose_name , name , * args , ** kwargs )
235
+ super ().__init__ (verbose_name = verbose_name , name = name , ** kwargs )
223
236
224
237
def add_variation (self , name , params ):
225
238
variation = self .def_variation .copy ()
@@ -253,12 +266,24 @@ def set_variations(self, instance=None, **kwargs):
253
266
variation_name )
254
267
setattr (field , name , variation_field )
255
268
269
+ def post_delete_callback (self , sender , instance , ** kwargs ):
270
+ getattr (instance , self .name ).delete (False )
271
+
256
272
def contribute_to_class (self , cls , name ):
257
273
"""Generate all operations on specified signals."""
258
274
super ().contribute_to_class (cls , name )
259
275
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 )
260
278
261
279
def validate (self , value , model_instance ):
262
280
super ().validate (value , model_instance )
263
281
if self .force_min_size :
264
282
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