from django.db import IntegrityError
from django.template.defaultfilters import slugify

class AutoSlugField (SlugField):
    """
    A SlugField that automatically populates itself at save-time from
    the value of another field.

    Accepts argument populate_from, which should be the name of a single
    field which the AutoSlugField will populate from (default = 'name').

    By default, also sets unique=True, db_index=True, and
    editable=False.

    Accepts additional argument, overwrite_on_save.  If True, will
    re-populate on every save, overwriting any existing value.  If
    False, will not touch existing value and will only populate if
    slug field is empty.  Default is False.

    """
    def __init__ (self, populate_from='name', overwrite_on_save=False,
                  *args, **kwargs):
        kwargs.setdefault('unique', True)
        kwargs.setdefault('db_index', True)
        kwargs.setdefault('editable', False)
        self._save_populate = populate_from
        self._overwrite_on_save = overwrite_on_save
        super(AutoSlugField, self).__init__(*args, **kwargs)

    def _populate_slug(self, model_instance):
        value = getattr(model_instance, self.attname, None)
        prepop = getattr(model_instance, self._save_populate, None)
        if (prepop is not None) and (not value or self._overwrite_on_save):
            value = slugify(prepop)
            setattr(model_instance, self.attname, value)
        return value

    def contribute_to_class (self, cls, name):
        # apparently in inheritance cases, contribute_to_class is called more
        #  than once, so we have to be careful not to overwrite the original
        #  save method.
        if not hasattr(cls, '_orig_save'):
            cls._orig_save = cls.save
            def _new_save (self_, *args, **kwargs):
                counter = 1
                orig_slug = self._populate_slug(self_)
                slug_len = len(orig_slug)
                if slug_len > self.max_length:
                    orig_slug = orig_slug[:self.max_length]
                    slug_len = self.max_length
                setattr(self_, name, orig_slug)
                while True:
                    try:
                        self_._orig_save(*args, **kwargs)
                        break
                    except IntegrityError, e:
                        # check to be sure a slug fight caused the IntegrityError
                        s_e = str(e)
                        if name in s_e and 'unique' in s_e:
                            counter += 1
                            max_len = self.max_length - (len(str(counter)) + 1)
                            if slug_len > max_len:
                                orig_slug = orig_slug[:max_len]
                            setattr(self_, name, "%s-%s" % (orig_slug, counter))
                        else:
                            raise
            cls.save = _new_save
        super(AutoSlugField, self).contribute_to_class(cls, name)
