web/lib/django_extensions/db/fields/__init__.py
changeset 3 526ebd3988b0
equal deleted inserted replaced
1:ebaad720f88b 3:526ebd3988b0
       
     1 """
       
     2 Django Extensions additional model fields
       
     3 """
       
     4 
       
     5 from django.template.defaultfilters import slugify
       
     6 from django.db.models import DateTimeField, CharField, SlugField
       
     7 import datetime
       
     8 import re
       
     9 
       
    10 try:
       
    11     import uuid
       
    12 except ImportError:
       
    13     from django_extensions.utils import uuid
       
    14 
       
    15 class AutoSlugField(SlugField):
       
    16     """ AutoSlugField
       
    17 
       
    18     By default, sets editable=False, blank=True.
       
    19 
       
    20     Required arguments:
       
    21 
       
    22     populate_from
       
    23         Specifies which field or list of fields the slug is populated from.
       
    24 
       
    25     Optional arguments:
       
    26 
       
    27     separator
       
    28         Defines the used separator (default: '-')
       
    29 
       
    30     overwrite
       
    31         If set to True, overwrites the slug on every save (default: False)
       
    32 
       
    33     Inspired by SmileyChris' Unique Slugify snippet:
       
    34     http://www.djangosnippets.org/snippets/690/
       
    35     """
       
    36     def __init__(self, *args, **kwargs):
       
    37         kwargs.setdefault('blank', True)
       
    38         kwargs.setdefault('editable', False)
       
    39 
       
    40         populate_from = kwargs.pop('populate_from', None)
       
    41         if populate_from is None:
       
    42             raise ValueError("missing 'populate_from' argument")
       
    43         else:
       
    44             self._populate_from = populate_from
       
    45         self.separator = kwargs.pop('separator',  u'-')
       
    46         self.overwrite = kwargs.pop('overwrite', False)
       
    47         super(AutoSlugField, self).__init__(*args, **kwargs)
       
    48 
       
    49     def _slug_strip(self, value):
       
    50         """
       
    51         Cleans up a slug by removing slug separator characters that occur at
       
    52         the beginning or end of a slug.
       
    53 
       
    54         If an alternate separator is used, it will also replace any instances
       
    55         of the default '-' separator with the new separator.
       
    56         """
       
    57         re_sep = '(?:-|%s)' % re.escape(self.separator)
       
    58         value = re.sub('%s+' % re_sep, self.separator, value)
       
    59         return re.sub(r'^%s+|%s+$' % (re_sep, re_sep), '', value)
       
    60 
       
    61     def slugify_func(self, content):
       
    62         return slugify(content)
       
    63 
       
    64     def create_slug(self, model_instance, add):
       
    65         # get fields to populate from and slug field to set
       
    66         if not isinstance(self._populate_from, (list, tuple)):
       
    67             self._populate_from = (self._populate_from, )
       
    68         slug_field = model_instance._meta.get_field(self.attname)
       
    69 
       
    70         if add or self.overwrite:
       
    71             # slugify the original field content and set next step to 2
       
    72             slug_for_field = lambda field: self.slugify_func(getattr(model_instance, field))
       
    73             slug = self.separator.join(map(slug_for_field, self._populate_from))
       
    74             next = 2
       
    75         else:
       
    76             # get slug from the current model instance and calculate next
       
    77             # step from its number, clean-up
       
    78             slug = self._slug_strip(getattr(model_instance, self.attname))
       
    79             next = slug.split(self.separator)[-1]
       
    80             if next.isdigit():
       
    81                 slug = self.separator.join(slug.split(self.separator)[:-1])
       
    82                 next = int(next)
       
    83             else:
       
    84                 next = 2
       
    85 
       
    86         # strip slug depending on max_length attribute of the slug field
       
    87         # and clean-up
       
    88         slug_len = slug_field.max_length
       
    89         if slug_len:
       
    90             slug = slug[:slug_len]
       
    91         slug = self._slug_strip(slug)
       
    92         original_slug = slug
       
    93 
       
    94         # exclude the current model instance from the queryset used in finding
       
    95         # the next valid slug
       
    96         queryset = model_instance.__class__._default_manager.all()
       
    97         if model_instance.pk:
       
    98             queryset = queryset.exclude(pk=model_instance.pk)
       
    99 
       
   100         # form a kwarg dict used to impliment any unique_together contraints
       
   101         kwargs = {}
       
   102         for params in model_instance._meta.unique_together:
       
   103             if self.attname in params:
       
   104                 for param in params:
       
   105                     kwargs[param] = getattr(model_instance, param, None)
       
   106         kwargs[self.attname] = slug
       
   107 
       
   108         # increases the number while searching for the next valid slug
       
   109         # depending on the given slug, clean-up
       
   110         while not slug or queryset.filter(**kwargs):
       
   111             slug = original_slug
       
   112             end = '%s%s' % (self.separator, next)
       
   113             end_len = len(end)
       
   114             if slug_len and len(slug)+end_len > slug_len:
       
   115                 slug = slug[:slug_len-end_len]
       
   116                 slug = self._slug_strip(slug)
       
   117             slug = '%s%s' % (slug, end)
       
   118             kwargs[self.attname] = slug
       
   119             next += 1
       
   120         return slug
       
   121 
       
   122     def pre_save(self, model_instance, add):
       
   123         value = unicode(self.create_slug(model_instance, add))
       
   124         setattr(model_instance, self.attname, value)
       
   125         return value
       
   126 
       
   127     def get_internal_type(self):
       
   128         return "SlugField"
       
   129 
       
   130 class CreationDateTimeField(DateTimeField):
       
   131     """ CreationDateTimeField
       
   132 
       
   133     By default, sets editable=False, blank=True, default=datetime.now
       
   134     """
       
   135 
       
   136     def __init__(self, *args, **kwargs):
       
   137         kwargs.setdefault('editable', False)
       
   138         kwargs.setdefault('blank', True)
       
   139         kwargs.setdefault('default', datetime.datetime.now)
       
   140         DateTimeField.__init__(self, *args, **kwargs)
       
   141 
       
   142     def get_internal_type(self):
       
   143         return "DateTimeField"
       
   144 
       
   145 class ModificationDateTimeField(CreationDateTimeField):
       
   146     """ ModificationDateTimeField
       
   147 
       
   148     By default, sets editable=False, blank=True, default=datetime.now
       
   149 
       
   150     Sets value to datetime.now() on each save of the model.
       
   151     """
       
   152 
       
   153     def pre_save(self, model, add):
       
   154         value = datetime.datetime.now()
       
   155         setattr(model, self.attname, value)
       
   156         return value
       
   157 
       
   158     def get_internal_type(self):
       
   159         return "DateTimeField"
       
   160 
       
   161 class UUIDVersionError(Exception):
       
   162     pass
       
   163 
       
   164 class UUIDField(CharField):
       
   165     """ UUIDField
       
   166 
       
   167     By default uses UUID version 1 (generate from host ID, sequence number and current time)
       
   168 
       
   169     The field support all uuid versions which are natively supported by the uuid python module.
       
   170     For more information see: http://docs.python.org/lib/module-uuid.html
       
   171     """
       
   172 
       
   173     def __init__(self, verbose_name=None, name=None, auto=True, version=1, node=None, clock_seq=None, namespace=None, **kwargs):
       
   174         kwargs['max_length'] = 36
       
   175         if auto:
       
   176             kwargs['blank'] = True
       
   177             kwargs.setdefault('editable', False)
       
   178         self.auto = auto
       
   179         self.version = version
       
   180         if version==1:
       
   181             self.node, self.clock_seq = node, clock_seq
       
   182         elif version==3 or version==5:
       
   183             self.namespace, self.name = namespace, name
       
   184         CharField.__init__(self, verbose_name, name, **kwargs)
       
   185 
       
   186     def get_internal_type(self):
       
   187         return CharField.__name__
       
   188 
       
   189     def create_uuid(self):
       
   190         if not self.version or self.version==4:
       
   191             return uuid.uuid4()
       
   192         elif self.version==1:
       
   193             return uuid.uuid1(self.node, self.clock_seq)
       
   194         elif self.version==2:
       
   195             raise UUIDVersionError("UUID version 2 is not supported.")
       
   196         elif self.version==3:
       
   197             return uuid.uuid3(self.namespace, self.name)
       
   198         elif self.version==5:
       
   199             return uuid.uuid5(self.namespace, self.name)
       
   200         else:
       
   201             raise UUIDVersionError("UUID version %s is not valid." % self.version)
       
   202 
       
   203     def pre_save(self, model_instance, add):
       
   204         if self.auto and add:
       
   205             value = unicode(self.create_uuid())
       
   206             setattr(model_instance, self.attname, value)
       
   207             return value
       
   208         else:
       
   209             value = super(UUIDField, self).pre_save(model_instance, add)
       
   210             if self.auto and not value:
       
   211                 value = unicode(self.create_uuid())
       
   212                 setattr(model_instance, self.attname, value)
       
   213         return value