3 from urllib import quote_plus, urlencode
4 from django.db import models, IntegrityError, connection, transaction
5 from django.utils.http import urlquote as django_urlquote
6 from django.utils.html import strip_tags
7 from django.core.urlresolvers import reverse
8 from django.contrib.contenttypes import generic
9 from django.contrib.contenttypes.models import ContentType
10 from django.core.cache import cache
11 from django.template.defaultfilters import slugify
12 from django.db.models.signals import post_delete, post_save, pre_save, pre_delete
13 from django.utils.translation import ugettext as _
14 from django.utils.safestring import mark_safe
15 from django.contrib.sitemaps import ping_google
16 import django.dispatch
17 from django.conf import settings
18 from forum import const
21 from forum.const import *
23 class CachedManager(models.Manager):
24 use_for_related_fields = True
25 int_cache_re = re.compile('^_[\w_]+cache$')
27 def cache_obj(self, obj):
28 int_cache_keys = [k for k in obj.__dict__.keys() if self.int_cache_re.match(k)]
30 for k in int_cache_keys:
33 cache.set(self.model.cache_key(obj.id), obj, 60 * 60)
35 def get(self, *args, **kwargs):
37 pk = [v for (k,v) in kwargs.items() if k in ('pk', 'pk__exact', 'id', 'id__exact'
38 ) or k.endswith('_ptr__pk') or k.endswith('_ptr__id')][0]
43 key = self.model.cache_key(pk)
47 obj = super(CachedManager, self).get(*args, **kwargs)
54 return super(CachedManager, self).get(*args, **kwargs)
56 def get_or_create(self, *args, **kwargs):
58 return self.get(*args, **kwargs)
60 return super(CachedManager, self).get_or_create(*args, **kwargs)
62 denorm_update = django.dispatch.Signal(providing_args=["instance", "field", "old", "new"])
64 class DenormalizedField(models.IntegerField):
65 __metaclass__ = models.SubfieldBase
67 def contribute_to_class(self, cls, name):
68 super (DenormalizedField, self).contribute_to_class(cls, name)
69 if not hasattr(cls, '_denormalizad_fields'):
70 cls._denormalizad_fields = []
72 cls._denormalizad_fields.append(name)
74 class BaseModel(models.Model):
75 objects = CachedManager()
81 def __init__(self, *args, **kwargs):
82 super(BaseModel, self).__init__(*args, **kwargs)
83 self._original_state = dict([(k, v) for k,v in self.__dict__.items() if not k in kwargs])
86 def cache_key(cls, pk):
87 return '%s.%s:%s' % (settings.APP_URL, cls.__name__, pk)
89 def get_dirty_fields(self):
91 return dict([(k, self._original_state.get(k, None)) for k,v in self.__dict__.items()
92 if self._original_state.get(k, missing) == missing or self._original_state[k] != v])
94 def save(self, *args, **kwargs):
97 if hasattr(self.__class__, '_denormalizad_fields'):
98 dirty = self.get_dirty_fields()
99 put_back = [f for f in self.__class__._denormalizad_fields if f in dirty]
103 self.__dict__[n] = models.F(n) + (self.__dict__[n] - dirty[n])
105 super(BaseModel, self).save(*args, **kwargs)
109 self.__dict__.update(
110 self.__class__.objects.filter(id=self.id).values(*put_back)[0]
113 denorm_update.send(sender=self.__class__, instance=self, field=f,
114 old=self._original_state[f], new=self.__dict__[f])
116 #todo: log this properly
119 self._original_state = dict(self.__dict__)
120 self.__class__.objects.cache_obj(self)
123 cache.delete(self.cache_key(self.pk))
124 super(BaseModel, self).delete()
127 class ActiveObjectManager(models.Manager):
128 use_for_related_fields = True
129 def get_query_set(self):
130 return super(ActiveObjectManager, self).get_query_set().filter(canceled=False)
132 class UndeletedObjectManager(models.Manager):
133 def get_query_set(self):
134 return super(UndeletedObjectManager, self).get_query_set().filter(deleted=False)
136 class GenericContent(models.Model):
137 content_type = models.ForeignKey(ContentType)
138 object_id = models.PositiveIntegerField()
139 content_object = generic.GenericForeignKey('content_type', 'object_id')
145 class MetaContent(BaseModel):
146 node = models.ForeignKey('Node', null=True, related_name='%(class)ss')
148 def __init__(self, *args, **kwargs):
149 if 'content_object' in kwargs:
150 kwargs['node'] = kwargs['content_object']
151 del kwargs['content_object']
153 super (MetaContent, self).__init__(*args, **kwargs)
156 def content_object(self):
157 return self.node.leaf
163 from user import User
165 class UserContent(models.Model):
166 user = models.ForeignKey(User, related_name='%(class)ss')
173 marked_deleted = django.dispatch.Signal(providing_args=["instance", "deleted_by"])
175 class DeletableContent(models.Model):
176 deleted = models.BooleanField(default=False)
177 deleted_at = models.DateTimeField(null=True, blank=True)
178 deleted_by = models.ForeignKey(User, null=True, blank=True, related_name='deleted_%(class)ss')
180 active = UndeletedObjectManager()
186 def mark_deleted(self, user):
189 self.deleted_at = datetime.datetime.now()
190 self.deleted_by = user
192 marked_deleted.send(sender=self.__class__, instance=self, deleted_by=user)
197 def unmark_deleted(self):
205 mark_canceled = django.dispatch.Signal(providing_args=['instance'])
207 class CancelableContent(models.Model):
208 canceled = models.BooleanField(default=False)
211 if not self.canceled:
214 mark_canceled.send(sender=self.__class__, instance=self)
224 from node import Node, NodeRevision
227 wiki = models.BooleanField(default=False)
228 wikified_at = models.DateTimeField(null=True, blank=True)
237 self.wikified_at = datetime.datetime.now()