# *coding: utf-8*

from __future__ import unicode_literals
from django.core.files import File
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.hashers import get_hasher, identify_hasher
from django.db import models
from django.contrib.gis.db import models
from django.contrib.gis.geos import Point
from backoffice.signals import *
from django.db.models.signals import *
from django.core.urlresolvers import reverse
from django.db.models import FileField
from django.db.models.signals import post_delete, post_save, pre_save,pre_delete
from django.dispatch.dispatcher import receiver
from django.utils.translation import ugettext_lazy as _
from backoffice.UserManager import *
from helpers import *
from Agender import settings
from autoslug import AutoSlugField
from cacheops import invalidate_obj, invalidate_model, invalidate_all
import pgcrypto
from tinymce.models import HTMLField


# Models here.
class Category(models.Model):
    name = models.CharField(max_length=100,db_index=True)
    icons = models.ImageField(upload_to="media/%Y/%m/%d", null=True, blank=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)
    def __unicode__(self):
       return u'%s' % (self.name)


    def save(self, *args, **kwargs):
        invalidate_model(Category)
        super(Category, self).save(*args, **kwargs)

class Town(models.Model):
    name = models.CharField(max_length=100,db_index=True)
    city = models.CharField(max_length=100)
    country = models.CharField(max_length=100)
    valid = models.BooleanField(default=True)
    location = models.PointField(null=True, blank=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)
    def __unicode__(self):
       return u'%s - %s' % (self.name,self.country)

    def save(self, *args, **kwargs):
        invalidate_model(Town)
        super(Town, self).save(*args, **kwargs)


class Photo(models.Model):
    picture = models.ImageField(upload_to="media/%Y/%m/%d", null=True, blank=True)
    valid = models.BooleanField(default=True)
    waitingValidation = models.BooleanField(default=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)

    def __unicode__(self):
       return u'%s' % (self.picture)
post_save.connect(signal_add_photo, dispatch_uid="signal_add_photo",sender=Photo)



class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(_('email address'), unique=True,db_index=True)
    refTown = models.ForeignKey(Town, null=True, blank=True)
    username = models.CharField(max_length=100, null=True, blank=True)
    first_name = models.CharField(max_length=100, null=True, blank=True)
    last_name = models.CharField(max_length=100, null=True, blank=True)
    facebookId = models.CharField(max_length=100, null=True, blank=True,db_index=True)
    twitterId = models.CharField(max_length=100, null=True, blank=True, db_index=True)
    instagram = models.CharField(max_length=100, null=True, blank=True)
    pushToken = models.CharField(max_length=255, null=True, blank=True)
    mainPhoto = models.ImageField(upload_to="media/%Y/%m/%d",default='media/avatar.png', null=True, blank=True)
    photos = models.ManyToManyField(Photo, through='PhotoProfile')
    street = models.CharField(max_length=100, null=True, blank=True)
    zipcode = models.CharField(max_length=20, null=True, blank=True)
    location  = models.PointField(null=True, blank=True)
    is_active = models.BooleanField(_('active'), default=True)
    is_staff = models.BooleanField(_('staff'), default=False)
    birthday = models.DateTimeField(null=True, blank=True)
    nbFollower = models.IntegerField(default=0)
    nbFollowing = models.IntegerField(default=0)
    nbPost = models.IntegerField(default=0)
    nbLike = models.IntegerField(default=0)
    valid = models.BooleanField(default=True)
    removeAd = models.BooleanField(default=False)
    online = models.BooleanField(default=False)
    android = models.BooleanField(blank=True, default=False)
    ios = models.NullBooleanField(blank=True, default=False,null=True)

    acceptCommunity = models.BooleanField(default=False)
    acceptCGU = models.BooleanField(default=True)
    bio = models.TextField(blank=True,null=True)
    acceptPush = models.BooleanField(default=False)

    objects = UserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    class Meta:
        verbose_name = _('User')
        verbose_name_plural = _('Users')

        ## Geocode using full address

    def _get_full_address(self):
        if self.refTown:
            return u'%s, %s %s %s' % (
                self.street, self.zipcode, self.refTown.city, self.refTown.country)
        else:
            return "none"

    full_address = property(_get_full_address)

    def save(self, *args, **kwargs):
        locationFound = get_lng_lat(self.full_address)
        try:
            if len(locationFound)>0:
                lat = str(locationFound['lat'])
                lng = str(locationFound['lng'])
                self.location = Point(float(lng), float(lat))
        except Exception as e:
            logger.info(e)
        super(User, self).save(*args, **kwargs)

    def get_full_name(self):
        '''
        Returns the first_name plus the last_name, with a space in between.
        '''
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        '''
        Returns the short name for the user.
        '''
        return self.first_name

    def check_password(self, raw_password):
        def setter(raw_password):
            self.set_password(raw_password)
            self.save(update_fields=["password"])

        if raw_password is None:
            return False

        hasher = get_hasher("default")
        must_update = False
        if self.password.find('$') > 0:
            hasher = identify_hasher(self.password)
            must_update = True

        is_correct = hasher.verify(raw_password, self.password)
        if is_correct and must_update:
            self.set_password(raw_password)
            self.save(update_fields=["password"])

        return is_correct




class Follow(models.Model):
    following = models.ForeignKey(User, related_name="who_is_followed")
    follower = models.ForeignKey(User, related_name="who_follows")
    follow_time = models.DateTimeField(auto_now=True)

    def __unicode__(self):
        return str(self.follow_time)
#post_save.connect(signal_add_follow, dispatch_uid="signal_add_follow", sender=Follow)
#pre_delete.connect(signal_delete_follow, dispatch_uid="signal_delete_follow", sender=Follow)

class PhotoProfile(models.Model):
    refPhoto = models.ForeignKey(Photo)
    refUser = models.ForeignKey(User)
    public = models.BooleanField(default=False)
    private = models.BooleanField(default=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)
    valid = models.BooleanField(default=True)
    waitingValidation = models.BooleanField(default=True)

    def __unicode__(self):
       return u'%s - %s' % (self.refUser,self.refPhoto)




######## Ces tables sont partitionnées ##########################

class Article(models.Model):
    refTown = models.ForeignKey(Town)
    titre = models.CharField(max_length=255, blank=True, null=True)
    slug = AutoSlugField(null=True, default=None, unique=True, populate_from='titre')
    chapeau = models.CharField(max_length=512, blank=True, null=True)
    texte = HTMLField()
    image = models.ImageField(upload_to="media/%Y/%m/%d", null=True, blank=True)
    lienWeb = models.CharField(max_length=255, blank=True, null=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)
    valid = models.BooleanField(default=True)

    def __unicode__(self):
        return u'%s - %s' % (self.refTown, self.titre)

    def save(self, *args, **kwargs):
        invalidate_model(Article)
        # self.get_remote_image()
        super(Article, self).save(*args, **kwargs)  # Call the "real" save() method.


class Post(models.Model):
    refUser = models.ForeignKey(User)
    refTown = models.ForeignKey(Town)
    categoryType = (
        (0, "news"),
        (1, "music"),
        (2, "clubbing"),
        (3, "culture"),
        (4, "photography"),
        (5, "newuserincommunity"),
        (6, "notset"),
    )
    postCategory = models.IntegerField(choices=categoryType, default=0)
    mediaTypeChoices = (
        (1, "photo"),
        (2, "video"),
    )
    mediaType = models.IntegerField(choices=mediaTypeChoices, null=True, blank=True)
    mediaUrl = models.ImageField(upload_to="media/%Y/%m/%d", null=True, blank=True)
    webSite = models.CharField(max_length=255, blank=True, null=True)
    webSiteTitle = models.CharField(max_length=200, blank=True, null=True)
    webSiteDescription = models.CharField(max_length=1024, blank=True, null=True)
    webSiteImageUrl = models.ImageField(upload_to="media/%Y/%m/%d", null=True, blank=True)
    webSiteImageUrl_remoteURL = models.URLField(max_length=500,null=True, blank=True)
    message = models.TextField(blank=True, null=True)
    nbLike = models.IntegerField(default=0)
    nbComment = models.IntegerField(default=0)
    valid = models.BooleanField(db_index=True,default=True)
    waitingValidation = models.BooleanField(default=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)
    def __unicode__(self):
       return u'%s - %s' % (self.refUser,self.refTown)

    def get_remote_image(self):
        if self.webSiteImageUrl_remoteURL and not self.webSiteImageUrl:
            result = urllib.urlretrieve(self.webSiteImageUrl_remoteURL)
            try:
                path = settings.MEDIA_ROOT+os.path.basename(self.webSiteImageUrl_remoteURL)
                logger.info("==== OUVRE FICHIER %s" %(path))
                self.webSiteImageUrl.save(
                    os.path.basename(path),
                    File(open(result[0]))
                 )
            except Exception as e:
                logger.info("FICHIER %s" % e)
            #self.save()

    def save(self, *args, **kwargs):
        invalidate_model(Post)
        #self.get_remote_image()
        super(Post, self).save(*args, **kwargs)  # Call the "real" save() method.
post_save.connect(signal_add_post, dispatch_uid="signal_add_post",sender=Post)
pre_delete.connect(signal_delete_post, dispatch_uid="signal_delete_post", sender=Post)

class PostComments(models.Model):
    refPost = models.ForeignKey(Post)
    refUser = models.ForeignKey(User)
    userDisplayName = models.CharField(max_length=200, blank=True, null=True)
    isMediaMessage = models.BooleanField(default=True)
    mediaTypeChoices = (
        (0, "location"),
        (1, "photo"),
    )
    mediaType = models.IntegerField(choices=mediaTypeChoices, null=True, blank=True)
    #Peut contenir soit la latitude/longitude (location) soit l'url vers la photo
    mediaValue = models.CharField(max_length=200, blank=True, null=True)
    comment = models.TextField(blank=True, null=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)
    valid = models.BooleanField(default=True)
    waitingValidation = models.BooleanField(default=True)

    def __unicode__(self):
       return u'%s - %s' % (self.refPost,self.refUser)

post_save.connect(signal_add_postcomment, dispatch_uid="signal_add_postcomment", sender=PostComments)


#######################################################################

class UserLike(models.Model):
    refUser = models.ForeignKey(User)
    refPost = models.ForeignKey(Post)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)

    def __unicode__(self):
        return u'%s - %s' % (self.refPost, self.refUser)
post_save.connect(signal_add_userlike, dispatch_uid="signal_add_userlike", sender=UserLike)
pre_delete.connect(signal_predelete_userlike, dispatch_uid="signal_predelete_userlike", sender=UserLike)

class Chat(models.Model):
    refUserOne = models.ForeignKey(User,related_name="user_one")
    refUserTwo = models.ForeignKey(User, related_name="user_two")
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)

    def __unicode__(self):
        return u'%s - %s' % (self.refUserOne, self.refUserTwo)

class ChatMessage(models.Model):
    refChat = models.ForeignKey(Chat)
    senderId = models.ForeignKey(User)
    senderDisplayName = pgcrypto.EncryptedCharField(max_length=200, blank=True, null=True)
    isMediaMessage = pgcrypto.EncryptedIntegerField(default=0)
    mediaTypeChoices = (
        (0, "location"),
        (1, "photo"),
    )
    mediaType = pgcrypto.EncryptedIntegerField(choices=mediaTypeChoices, null=True, blank=True)
    # Peut contenir soit la latitude/longitude (location) soit l'url vers la photo
    mediaValue = pgcrypto.EncryptedCharField(max_length=200, blank=True, null=True)
    message = pgcrypto.EncryptedTextField(blank=True, null=True)
    isRead = pgcrypto.EncryptedIntegerField(default=0)
    createdAt = pgcrypto.EncryptedDateTimeField(auto_now_add=True)
    updatedAt = pgcrypto.EncryptedDateTimeField(auto_now=True)
    def __unicode__(self):
        return u'%s - %s' % (self.refChat, self.senderId)

post_save.connect(signal_add_message, dispatch_uid="signal_add_message", sender=ChatMessage)

class ChatMedia(models.Model):
    refChat = models.ForeignKey(Chat)
    media = models.ImageField(upload_to="media/%Y/%m/%d", null=True, blank=True)
    createdAt = pgcrypto.EncryptedDateTimeField(auto_now_add=True)
    updatedAt = pgcrypto.EncryptedDateTimeField(auto_now=True)

    def __unicode__(self):
        return u'%s - %s' % (self.refChat, self.createdAt)

class Place(models.Model):
    name = models.CharField(max_length=100)
    slug = AutoSlugField(null=True, default=None, unique=True, populate_from='name')
    refCategory = models.ForeignKey(Category,related_name="placescategories")
    street = models.CharField(max_length=100)
    zipCode = models.CharField(max_length=100)
    refTown = models.ForeignKey(Town)
    mainPhoto = models.ImageField(upload_to="media/%Y/%m/%d", null=True, blank=True)
    location = models.PointField(null=True, blank=True)
    email = models.CharField(max_length=100, blank=True, null=True)
    phone = models.CharField(max_length=30,blank=True, null=True)
    webSite = models.CharField(max_length=200,blank=True, null=True)
    description = models.TextField(blank=True, null=True)
    facebookId = models.CharField(max_length=100,blank=True, null=True)
    tagInstagram = models.CharField(max_length=100, blank=True, null=True)
    openingTime = models.CharField(max_length=100,blank=True, null=True)
    freeAccess = models.BooleanField(default=False)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)
    noteAverage = models.IntegerField(default=0)
    nbVote = models.IntegerField(default=0)
    valid = models.BooleanField(db_index=True,default=True)
    highlighted = models.BooleanField(db_index=True, default=False)
    googlePlaceId = models.CharField(max_length=100, blank=True, null=True)
    googlePlaceJson = models.TextField(blank=True,null=True)
    def thumbnail(self):
        return """<img src="%s" width="240" height="150"/>""" % (settings.MEDIA_URL+self.mainPhoto.name)
    thumbnail.allow_tags = True

    ## Geocode using full address
    def _get_full_address(self):
        return u'%s, %s %s %s' % (
        self.street, self.zipCode, self.refTown.city, self.refTown.country)

    full_address = property(_get_full_address)

    def save(self, *args, **kwargs):
        invalidate_model(Place)
        locationFound = get_lng_lat(self.full_address)
        if len(locationFound)>0:
            lat = str(locationFound['lat'])
            lng = str(locationFound['lng'])
            self.location = Point(float(lng), float(lat))
        super(Place, self).save(*args, **kwargs)

    def __unicode__(self):
       return u'%s' % (self.name)





class PlacesVote(models.Model):
    refPlace = models.ForeignKey(Place)
    refUser = models.ForeignKey(User)
    note = models.FloatField(null=True, blank=True, default=0)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)

    def __unicode__(self):
       return u'%s - %s' % (self.refPlace,self.refUser)
post_save.connect(signal_addvote, dispatch_uid="signal_addvote",sender=PlacesVote)

class PlacesComments(models.Model):
    refPlace = models.ForeignKey(Place)
    refUser = models.ForeignKey(User)
    note = models.IntegerField(default=0)
    comment = models.TextField(blank=True, null=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)
    valid = models.BooleanField(default=True)
    waitingValidation = models.BooleanField(default=True)

    def __unicode__(self):
       return u'%s - %s' % (self.refPlace,self.refUser)

#Users who are going to the place
class PlacesUsers(models.Model):
    refPlace = models.ForeignKey(Place)
    refUser = models.ForeignKey(User)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)

    def __unicode__(self):
       return u'%s - %s' % (self.refPlace,self.refUser)

class EventCategory(models.Model):
    name = models.CharField(max_length=100,db_index=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)
    def __unicode__(self):
       return u'%s' % (self.name)

    def save(self, *args, **kwargs):
        invalidate_model(EventCategory)
        super(EventCategory, self).save(*args, **kwargs)  # Call the "real" save() method.

class Event(models.Model):
    name = models.CharField(max_length=100)
    slug = AutoSlugField(null=True, default=None, unique=True, populate_from='name')
    placeName = models.CharField(max_length=100,null=True)
    dateEvent = models.DateTimeField(null=True,db_index=True)
    startDateEvent = models.DateTimeField(blank=True,null=True, db_index=True)
    endDateEvent = models.DateTimeField(blank=True,null=True, db_index=True)
    refPlace = models.ForeignKey(Place,related_name="eventsplace")
    refEventCategory = models.ForeignKey(EventCategory, default=1,related_name="categoriesevent")
    street = models.CharField(max_length=100,blank=True, null=True)
    zipCode = models.CharField(max_length=100,blank=True, null=True)
    refTown = models.ForeignKey(Town)
    picture = models.ImageField(upload_to="media/%Y/%m/%d", null=True, blank=True,max_length=1020)
    photos = models.ManyToManyField(Photo, through='PhotoEvents')
    location = models.PointField(null=True, blank=True)
    email = models.CharField(max_length=100, blank=True, null=True)
    phone = models.CharField(max_length=30,blank=True, null=True)
    pageFacebook = models.CharField(max_length=1020, blank=True, null=True)
    description = models.TextField(blank=True, null=True)
    facebookId = models.CharField(max_length=100,blank=True, null=True)
    openingTime = models.CharField(max_length=100,blank=True, null=True)
    isWeekly = models.BooleanField(default=False)
    isDaily = models.BooleanField(default=False)
    tagInstagram = models.CharField(max_length=100,blank=True, null=True)
    ticketUrl = models.URLField(blank=True,null=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)
    valid = models.BooleanField(default=True,db_index=True)
    automaticDelete = models.BooleanField(default=True)
    requireAttention = models.BooleanField(default=False)
    jsonStripe = models.TextField(blank=True, null=True)
    jsonDate = models.TextField(blank=True, null=True)
    onIndexPage = models.BooleanField(default=True)
    @property
    def duration(self):
        if self.startDateEvent:
            timeDuration = self.endDateEvent - self.startDateEvent
            tempsTotal = timeDuration.total_seconds() / 86400
            return tempsTotal
        else:
            return 1


    def thumbnail(self):
        if "http" not in self.picture.name:
            return """<img src="%s" width="240" height="150"/>""" % (settings.MEDIA_URL+self.picture.name)
        else:
            return """<img src="%s" width="240" height="150"/>""" % (self.picture.name)
    thumbnail.allow_tags = True

    ## Geocode using full address
    def _get_full_address(self):
        return u'%s, %s %s %s' % (
            self.street, self.zipCode, self.refTown.city, self.refTown.country)

    full_address = property(_get_full_address)

    def save(self, *args, **kwargs):
        invalidate_model(Event)
        locationFound = get_lng_lat(self.full_address)
        if len(locationFound) > 0:
            lat = str(locationFound['lat'])
            lng = str(locationFound['lng'])
            self.location = Point(float(lng), float(lat))
        super(Event, self).save(*args, **kwargs)

    def __unicode__(self):
       return u'%s' % (self.name)

class EventComments(models.Model):
    refEvent = models.ForeignKey(Event)
    refUser = models.ForeignKey(User)
    comment = models.TextField(blank=True, null=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)
    valid = models.BooleanField(default=True)
    waitingValidation = models.BooleanField(default=True)

    def __unicode__(self):
       return u'%s - %s' % (self.refEvent,self.refUser)

class EventUsers(models.Model):
    refEvent = models.ForeignKey(Event)
    refUser = models.ForeignKey(User)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)

    def __unicode__(self):
       return u'%s - %s' % (self.refEvent,self.refUser)


class PhotoEvents(models.Model):
    refEvent = models.ForeignKey(Event)
    refPhoto = models.ForeignKey(Photo)
    refUser = models.ForeignKey(User)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)
    valid = models.BooleanField(default=True)
    waitingValidation = models.BooleanField(default=True)

    def __unicode__(self):
       return u'%s - %s' % (self.refEvent,self.refPhoto)

class CheckInEvent(models.Model):
    refEvent = models.ForeignKey(Event)
    refUser = models.ForeignKey(User)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)

    def __unicode__(self):
       return u'%s - %s' % (self.refEvent,self.refUser)


class GoToEvent(models.Model):
    refEvent = models.ForeignKey(Event)
    refUser = models.ForeignKey(User)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)

    def __unicode__(self):
        return u'%s - %s' % (self.refEvent, self.refUser)


class Subscription(models.Model):
    refUser = models.ForeignKey(User)
    subscriptionType = (
        (0, "one_month"),
        (1, "three_months"),
        (2, "six_months"),
        (3, "one_year"),

    )
    subscription = models.IntegerField(choices=subscriptionType, null=True, blank=True)
    startDate = models.DateTimeField()
    endDate = models.DateTimeField()
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)

    def __unicode__(self):
        return u'%s - %s' % (self.refUser, self.subscription)

class Promo(models.Model):
    code = models.CharField(max_length=100)
    refUser = models.ForeignKey(User,blank=True,null=True)
    burn = models.BooleanField(default=False)
    usedOn = models.DateTimeField(auto_now=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)


#Will be set by signals
#Partitions will be automatically created by month by PostgreSQL thru a procedure and a trigger
#Penser a regrouper les notifications par userID pour un meme userFollowed dans un intervalle de temps (pour ne pas envoyer 100 notifs par exemple)
class NotificationToSend(models.Model):
    toUserId = models.ForeignKey('User', db_index=True,related_name="toUser")
    message = models.CharField(max_length=2048,null=True,blank=True)
    notif_type_choice = (
        (0, "PUSH"),
        (1, "EMAIL"),
    )
    notifyType = models.IntegerField(choices=notif_type_choice)
    status_choice = (
        (0, "TO_SEND"),
        (1, "SENDING"),
        (2, "SENT"),
        (3, "ERROR_SENDING"),
        (4,"REFUSED"),
    )
    status = models.IntegerField(choices=status_choice)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)

    def __unicode__(self):
        return u'%s - %s' % (self.message,self.toUserId.username)


###################################
# Delete files on disk
# on signals delete or on update
###################################
@receiver(post_delete)
def handle_files_on_delete(sender, instance, **kwargs):
    # presumably you want this behavior only for your apps, in which case you will have to specify them
    delete_files([getattr(instance, field_.name, None) for field_ in sender._meta.fields if isinstance(field_, FileField)])


@receiver(pre_save)
def set_instance_cache(sender, instance, **kwargs):
    # prevent errors when loading files from fixtures
    from_fixture = 'raw' in kwargs and kwargs['raw']
    if not from_fixture:
        # retrieve the old instance from the database to get old file values
        # for Django 1.8+, you can use the *refresh_from_db* method
        try:
            old_instance = sender.objects.filter(pk=instance.id).first()
            if old_instance is not None:
                # for each FileField, we will keep the original value inside an ephemeral `cache`
                instance.files_cache = {
                    field_.name: getattr(old_instance, field_.name, None) for field_ in sender._meta.fields if
                    isinstance(field_, FileField)
                    }
        except Exception as e:
            logger.debug("PreSave Error %s", e)

@receiver(post_save)
def handle_files_on_update(sender, instance, **kwargs):
    if hasattr(instance, 'files_cache') and instance.files_cache:
        deletables = []
        for field_name in instance.files_cache:
            old_file_value = instance.files_cache[field_name]
            new_file_value = getattr(instance, field_name, None)
            # only delete the files that have changed
            if old_file_value and old_file_value != new_file_value:
                deletables.append(old_file_value)
        delete_files(deletables)
        instance.files_cache = {field_name: getattr(instance, field_name, None) for field_name in instance.files_cache}
