/usr/share/pyshared/model_utils/fields.py is in python-django-model-utils 1.3.1-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | from datetime import datetime
from django.db import models
from django.conf import settings
try:
from django.utils.timezone import now as now
except ImportError:
now = datetime.now
class AutoCreatedField(models.DateTimeField):
"""
A DateTimeField that automatically populates itself at
object creation.
By default, sets editable=False, default=datetime.now.
"""
def __init__(self, *args, **kwargs):
kwargs.setdefault('editable', False)
kwargs.setdefault('default', now)
super(AutoCreatedField, self).__init__(*args, **kwargs)
class AutoLastModifiedField(AutoCreatedField):
"""
A DateTimeField that updates itself on each save() of the model.
By default, sets editable=False and default=datetime.now.
"""
def pre_save(self, model_instance, add):
value = now()
setattr(model_instance, self.attname, value)
return value
class StatusField(models.CharField):
"""
A CharField that looks for a ``STATUS`` class-attribute and
automatically uses that as ``choices``. The first option in
``STATUS`` is set as the default.
Also has a default max_length so you don't have to worry about
setting that.
Also features a ``no_check_for_status`` argument to make sure
South can handle this field when it freezes a model.
"""
def __init__(self, *args, **kwargs):
kwargs.setdefault('max_length', 100)
self.check_for_status = not kwargs.pop('no_check_for_status', False)
super(StatusField, self).__init__(*args, **kwargs)
def prepare_class(self, sender, **kwargs):
if not sender._meta.abstract and self.check_for_status:
assert hasattr(sender, 'STATUS'), \
"To use StatusField, the model '%s' must have a STATUS choices class attribute." \
% sender.__name__
self._choices = sender.STATUS
if not self.has_default():
self.default = tuple(sender.STATUS)[0][0] # set first as default
def contribute_to_class(self, cls, name):
models.signals.class_prepared.connect(self.prepare_class, sender=cls)
super(StatusField, self).contribute_to_class(cls, name)
class MonitorField(models.DateTimeField):
"""
A DateTimeField that monitors another field on the same model and
sets itself to the current date/time whenever the monitored field
changes.
"""
def __init__(self, *args, **kwargs):
kwargs.setdefault('default', now)
monitor = kwargs.pop('monitor', None)
if not monitor:
raise TypeError(
'%s requires a "monitor" argument' % self.__class__.__name__)
self.monitor = monitor
super(MonitorField, self).__init__(*args, **kwargs)
def contribute_to_class(self, cls, name):
self.monitor_attname = '_monitor_%s' % name
models.signals.post_init.connect(self._save_initial, sender=cls)
super(MonitorField, self).contribute_to_class(cls, name)
def get_monitored_value(self, instance):
return getattr(instance, self.monitor)
def _save_initial(self, sender, instance, **kwargs):
setattr(instance, self.monitor_attname,
self.get_monitored_value(instance))
def pre_save(self, model_instance, add):
value = now()
previous = getattr(model_instance, self.monitor_attname, None)
current = self.get_monitored_value(model_instance)
if previous != current:
setattr(model_instance, self.attname, value)
self._save_initial(model_instance.__class__, model_instance)
return super(MonitorField, self).pre_save(model_instance, add)
SPLIT_MARKER = getattr(settings, 'SPLIT_MARKER', '<!-- split -->')
# the number of paragraphs after which to split if no marker
SPLIT_DEFAULT_PARAGRAPHS = getattr(settings, 'SPLIT_DEFAULT_PARAGRAPHS', 2)
_excerpt_field_name = lambda name: '_%s_excerpt' % name
def get_excerpt(content):
excerpt = []
default_excerpt = []
paras_seen = 0
for line in content.splitlines():
if not line.strip():
paras_seen += 1
if paras_seen < SPLIT_DEFAULT_PARAGRAPHS:
default_excerpt.append(line)
if line.strip() == SPLIT_MARKER:
return '\n'.join(excerpt)
excerpt.append(line)
return '\n'.join(default_excerpt)
class SplitText(object):
def __init__(self, instance, field_name, excerpt_field_name):
# instead of storing actual values store a reference to the instance
# along with field names, this makes assignment possible
self.instance = instance
self.field_name = field_name
self.excerpt_field_name = excerpt_field_name
# content is read/write
def _get_content(self):
return self.instance.__dict__[self.field_name]
def _set_content(self, val):
setattr(self.instance, self.field_name, val)
content = property(_get_content, _set_content)
# excerpt is a read only property
def _get_excerpt(self):
return getattr(self.instance, self.excerpt_field_name)
excerpt = property(_get_excerpt)
# has_more is a boolean property
def _get_has_more(self):
return self.excerpt.strip() != self.content.strip()
has_more = property(_get_has_more)
# allows display via templates without .content necessary
def __unicode__(self):
return self.content
class SplitDescriptor(object):
def __init__(self, field):
self.field = field
self.excerpt_field_name = _excerpt_field_name(self.field.name)
def __get__(self, instance, owner):
if instance is None:
raise AttributeError('Can only be accessed via an instance.')
content = instance.__dict__[self.field.name]
if content is None:
return None
return SplitText(instance, self.field.name, self.excerpt_field_name)
def __set__(self, obj, value):
if isinstance(value, SplitText):
obj.__dict__[self.field.name] = value.content
setattr(obj, self.excerpt_field_name, value.excerpt)
else:
obj.__dict__[self.field.name] = value
class SplitField(models.TextField):
def __init__(self, *args, **kwargs):
# for South FakeORM compatibility: the frozen version of a
# SplitField can't try to add an _excerpt field, because the
# _excerpt field itself is frozen as well. See introspection
# rules below.
self.add_excerpt_field = not kwargs.pop('no_excerpt_field', False)
super(SplitField, self).__init__(*args, **kwargs)
def contribute_to_class(self, cls, name):
if self.add_excerpt_field and not cls._meta.abstract:
excerpt_field = models.TextField(editable=False)
cls.add_to_class(_excerpt_field_name(name), excerpt_field)
super(SplitField, self).contribute_to_class(cls, name)
setattr(cls, self.name, SplitDescriptor(self))
def pre_save(self, model_instance, add):
value = super(SplitField, self).pre_save(model_instance, add)
excerpt = get_excerpt(value.content)
setattr(model_instance, _excerpt_field_name(self.attname), excerpt)
return value.content
def value_to_string(self, obj):
value = self._get_val_from_obj(obj)
return value.content
def get_prep_value(self, value):
try:
return value.content
except AttributeError:
return value
# allow South to handle these fields smoothly
try:
from south.modelsinspector import add_introspection_rules
# For a normal MarkupField, the add_excerpt_field attribute is
# always True, which means no_excerpt_field arg will always be
# True in a frozen MarkupField, which is what we want.
add_introspection_rules(rules=[
(
(SplitField,),
[],
{'no_excerpt_field': ('add_excerpt_field', {})}
),
(
(MonitorField,),
[],
{'monitor': ('monitor', {})}
),
(
(StatusField,),
[],
{'no_check_for_status': ('check_for_status', {})}
),
], patterns=['model_utils\.fields\.'])
except ImportError:
pass
|