]> git.openstreetmap.org Git - osqa.git/blob - forum/models/base.py
OSQA - 19
[osqa.git] / forum / models / base.py
1 import datetime
2 import re
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
19 import logging
20
21 from forum.const import *
22
23 class CachedManager(models.Manager):
24     use_for_related_fields = True
25     int_cache_re = re.compile('^_[\w_]+cache$')
26
27     def cache_obj(self, obj):
28         int_cache_keys = [k for k in obj.__dict__.keys() if self.int_cache_re.match(k)]
29
30         for k in int_cache_keys:
31             del obj.__dict__[k]
32
33         cache.set(self.model.cache_key(obj.id), obj, 60 * 60)
34
35     def get(self, *args, **kwargs):
36         try:
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]
39         except:
40             pk = None
41
42         if pk is not None:
43             key = self.model.cache_key(pk)
44             obj = cache.get(key)
45
46             if obj is None:
47                 obj = super(CachedManager, self).get(*args, **kwargs)
48                 self.cache_obj(obj)
49             else:
50                 d = obj.__dict__
51
52             return obj
53         
54         return super(CachedManager, self).get(*args, **kwargs)
55
56     def get_or_create(self, *args, **kwargs):
57         try:
58             return self.get(*args, **kwargs)
59         except:
60             return super(CachedManager, self).get_or_create(*args, **kwargs)
61
62 denorm_update = django.dispatch.Signal(providing_args=["instance", "field", "old", "new"])
63
64 class DenormalizedField(models.IntegerField):
65     __metaclass__ = models.SubfieldBase
66
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 = []
71
72         cls._denormalizad_fields.append(name)
73
74 class BaseModel(models.Model):
75     objects = CachedManager()
76
77     class Meta:
78         abstract = True
79         app_label = 'forum'
80
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])
84
85     @classmethod
86     def cache_key(cls, pk):
87         return '%s.%s:%s' % (settings.APP_URL, cls.__name__, pk)
88
89     def get_dirty_fields(self):
90         missing = object()
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])
93
94     def save(self, *args, **kwargs):
95         put_back = None
96
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]
100
101             if put_back:
102                 for n in put_back:
103                     self.__dict__[n] = models.F(n) + (self.__dict__[n] - dirty[n])
104
105         super(BaseModel, self).save(*args, **kwargs)
106
107         if put_back:
108             try:
109                 self.__dict__.update(
110                     self.__class__.objects.filter(id=self.id).values(*put_back)[0]
111                 )
112                 for f in put_back:
113                     denorm_update.send(sender=self.__class__, instance=self, field=f,
114                                        old=self._original_state[f], new=self.__dict__[f])
115             except:
116                 #todo: log this properly
117                 pass
118
119         self._original_state = dict(self.__dict__)
120         self.__class__.objects.cache_obj(self)
121
122     def delete(self):
123         cache.delete(self.cache_key(self.pk))
124         super(BaseModel, self).delete()
125
126
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)
131
132 class UndeletedObjectManager(models.Manager):
133     def get_query_set(self):
134         return super(UndeletedObjectManager, self).get_query_set().filter(deleted=False)
135
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')
140
141     class Meta:
142         abstract = True
143         app_label = 'forum'
144
145 class MetaContent(BaseModel):
146     node = models.ForeignKey('Node', null=True, related_name='%(class)ss')
147
148     def __init__(self, *args, **kwargs):
149         if 'content_object' in kwargs:
150             kwargs['node'] = kwargs['content_object']
151             del kwargs['content_object']
152
153         super (MetaContent, self).__init__(*args, **kwargs)
154     
155     @property
156     def content_object(self):
157         return self.node.leaf
158
159     class Meta:
160         abstract = True
161         app_label = 'forum'
162
163 from user import User
164
165 class UserContent(models.Model):
166     user = models.ForeignKey(User, related_name='%(class)ss')
167
168     class Meta:
169         abstract = True
170         app_label = 'forum'
171
172
173 marked_deleted = django.dispatch.Signal(providing_args=["instance", "deleted_by"])
174
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')
179
180     active = UndeletedObjectManager()
181
182     class Meta:
183         abstract = True
184         app_label = 'forum'
185
186     def mark_deleted(self, user):
187         if not self.deleted:
188             self.deleted = True
189             self.deleted_at = datetime.datetime.now()
190             self.deleted_by = user
191             self.save()
192             marked_deleted.send(sender=self.__class__, instance=self, deleted_by=user)
193             return True
194         else:
195             return False
196
197     def unmark_deleted(self):
198         if self.deleted:
199             self.deleted = False
200             self.save()
201             return True
202         else:
203             return False
204
205 mark_canceled = django.dispatch.Signal(providing_args=['instance'])
206
207 class CancelableContent(models.Model):
208     canceled = models.BooleanField(default=False)
209
210     def cancel(self):
211         if not self.canceled:
212             self.canceled = True
213             self.save()
214             mark_canceled.send(sender=self.__class__, instance=self)
215             return True
216             
217         return False
218
219     class Meta:
220         abstract = True
221         app_label = 'forum'
222
223
224 from node import Node, NodeRevision
225
226 class QandA(Node):
227     wiki                 = models.BooleanField(default=False)
228     wikified_at          = models.DateTimeField(null=True, blank=True)
229
230     class Meta:
231         abstract = True
232         app_label = 'forum'
233
234     def wikify(self):
235         if not self.wiki:
236             self.wiki = True
237             self.wikified_at = datetime.datetime.now()
238             self.save()
239
240
241
242