Commits

Jason Goldstein  committed f986fbe

Mixins, helpers, and improvements all around

  • Participants
  • Parent commits ddb2f9c

Comments (0)

Files changed (4)

File README.markdown

         # Args in order: request, app_label, model name, model instance id, field name, template, and the url to go post save or delete
         return create_crop(request, 'my_app_label', 'foo', obj.id, 'photo', 'my_crop_template.html', post_save_redirect=next)
 
+### Helpers
+
+I've included some helper methods to help keep GFK dirt out of your code. They are...
+
+    from cropper.helpers import get_or_create_crop, get_cropped_image, delete_crop
+
+They all take a model instance and a field name (as a string).
+
+
 ### Meanwhile, back at the models.py
 
+Your model should subclass CroppableImageMixin. It overrides save to wipe out any obsolete crops if you update one of your croppable images.
+
 Now we're in a state where we may or may not have a cropped image... so let's add a property of the model to handle that.
 
-    class Foo(models.Model):
+    from cropper.models import CroppableImageMixin
+    from cropper.helpers import get_cropped_image
+
+    class Foo(CroppableImageMixin):
         photo = models.ImageField(...)
 
         @property
         def cropped_photo(self):
         """ Return the photo, look for a cropped version first. """
-        crops = Crop.objects.filter(content_type=ContentType.objects.get(app_label="my_app", model="foo"), 
-            object_id=self.id, field='photo')
-        if crops:
-            return crops[0].image
-        return self.photo
+        crop = get_cropped_image(self, 'photo')  # Returns None if there's no cropped version.
+        return crop or self.photo
 
 And then in the template - or anywhere else for that matter - foo.cropped_photo will always return what you want.
 
-I also override save to ensure old crops are removed if the photo changes:
-
-    def save(self, *args, **kwargs):
-        """ Delete any crops if an image changes. """
-
-        if self.id: # only existing versions
-            croppable_fields = ['photo',]
-            old = Foo.objects.get(id=self.id)  # Existing version of the object we're about to save
-            for field in croppable_fields:
-                if getattr(self, field).name != getattr(old, field).name: # If the file is different
-                    Crop.objects.filter(
-                        content_type=ContentType.objects.get(app_label="my_app", model="foo"), 
-                        object_id=self.id, 
-                        field=field
-                    ).delete()  # Wipe out whatever applies
-
-        return super(Foo, self).save(*args, **kwargs)
-
-In the future some of this stuff might be inheritable... that'd be cool.
 
 ### Other requirements
 

File cropper/helpers.py

 from cropper.models import Crop
 from django.contrib.contenttypes.models import ContentType
 
+
 def get_or_create_crop(obj, field_name):
-    # Get any existing crop coordinates to pass along
+    """ Makes the crop. """
     crop = Crop.objects.get_or_create(object_id = obj.id, 
             content_type = ContentType.objects.get_for_model(obj),
             field = field_name,
         )[0]
     return crop
 
+
+def get_cropped_image(obj, field_name):
+    """ Returns a cropped image or None. """
+    crop = Crop.objects.filter(object_id = obj.id, 
+            content_type = ContentType.objects.get_for_model(obj),
+            field = field_name,
+        )
+    if crop:
+        return crop[0].image
+    return None
+
+
 def delete_crop(obj, field_name):    
     crop = Crop.objects.filter(object_id=obj.id, content_type=ContentType.objects.get_for_model(obj), field=field_name)
     if not crop:

File cropper/models.py

 from django.contrib.contenttypes import generic
 
 
-# class CroppableImageMixin(models.Model):
-#     """ 
-#     EXPERTIMENTAL: DO NOT USE.
-
-#     Handles post save logic for models that have croppable image fields.
-
-#     """
-
-#     class Meta:
-#         abstract = True
-
-#     def clear_old_crops(self, *args, **kwargs):
-#         """ Delete any crops if an image changes. """
-#         from cropper.helpers import delete_crop
-
-#         if self.id: # only existing versions
-#             fields = self._meta.fields
-#             croppable_fields = [field for field in fields if field.__class__.__name__ == "ImageField"]
-
-#             old = self.__class__.objects.get(id=self.id)  # Before this save
-#             for field in croppable_fields:
-#                 if getattr(self, field.name) != getattr(old, field.name): # If the file is different
-#                     delete_crop(self, field)
-
+class CroppableImageMixin(models.Model):
+    """ Handles post save logic for models that have croppable image fields. """
+
+    class Meta:
+        abstract = True
+
+    def save(self, *args, **kwargs):
+        """ Delete any crops if an image changes. """
+        from cropper.helpers import delete_crop
+        if self.id: # only existing versions
+            croppable_fields = [field for field in self._meta.fields if field.__class__.__name__ == "ImageField"]
+            old = self.__class__.objects.get(id=self.id)  # Before this save
+            for field in croppable_fields:
+                if getattr(self, field.name) != getattr(old, field.name): # If the file is different
+                    delete_crop(self, field.name)  # Wipe out whatever applies
+        return super(CroppableImageMixin, self).save(*args, **kwargs)
+        
 
 class Crop(models.Model):
     """ A cropped version of an imagefield from another model. """
 
 setup(
     name='Scruffy Cropper',
-    version="0.2.3",
+    version="0.3.0",
     author='Jason Goldstein',
     author_email='jason@betheshoe.com',
     url='https://bitbucket.org/whatisjasongoldstein/django-cropper',