Merge branch 'master' of ssh://git.planet-lab.org/git/plstackapi
[plstackapi.git] / planetstack / core / models / plcorebase.py
1 import os
2 import sys
3 from django.db import models
4 from django.forms.models import model_to_dict
5 from django.core.urlresolvers import reverse
6 from django.forms.models import model_to_dict
7 from django.utils import timezone
8 import model_policy
9
10 try:
11     # This is a no-op if observer_disabled is set to 1 in the config file
12     from observer import *
13 except:
14     print >> sys.stderr, "import of observer failed! printing traceback and disabling observer:"
15     import traceback
16     traceback.print_exc()
17
18     # guard against something failing
19     def notify_observer(*args, **kwargs):
20         pass
21
22 # This manager will be inherited by all subclasses because
23 # the core model is abstract.
24 class PlCoreBaseDeletionManager(models.Manager):
25     def get_queryset(self):
26         parent=super(PlCoreBaseDeletionManager, self)
27         if hasattr(parent, "get_queryset"):
28             return parent.get_queryset().filter(deleted=True)
29         else:
30             return parent.get_query_set().filter(deleted=True)
31
32     # deprecated in django 1.7 in favor of get_queryset().
33     def get_query_set(self):
34         return self.get_queryset()
35
36 # This manager will be inherited by all subclasses because
37 # the core model is abstract.
38 class PlCoreBaseManager(models.Manager):
39     def get_queryset(self):
40         parent=super(PlCoreBaseManager, self)
41         if hasattr(parent, "get_queryset"):
42             return parent.get_queryset().filter(deleted=False)
43         else:
44             return parent.get_query_set().filter(deleted=False)
45
46     # deprecated in django 1.7 in favor of get_queryset().
47     def get_query_set(self):
48         return self.get_queryset()
49
50 class PlCoreBase(models.Model):
51     objects = PlCoreBaseManager()
52     deleted_objects = PlCoreBaseDeletionManager()
53
54     # default values for created and updated are only there to keep evolution
55     # from failing.
56     created = models.DateTimeField(auto_now_add=True, default=timezone.now)
57     updated = models.DateTimeField(auto_now=True, default=timezone.now)
58     enacted = models.DateTimeField(null=True, default=None)
59     backend_status = models.CharField(max_length=140,
60                                       default="Provisioning in progress")
61     deleted = models.BooleanField(default=False)
62
63     class Meta:
64         # Changing abstract to False would require the managers of subclasses of
65         # PlCoreBase to be customized individually.
66         abstract = True
67         app_label = "core"
68
69     def __init__(self, *args, **kwargs):
70         super(PlCoreBase, self).__init__(*args, **kwargs)
71         self.__initial = self._dict
72         self.silent = False
73
74     @property
75     def diff(self):
76         d1 = self.__initial
77         d2 = self._dict
78         diffs = [(k, (v, d2[k])) for k, v in d1.items() if v != d2[k]]
79         return dict(diffs)
80
81     @property
82     def has_changed(self):
83         return bool(self.diff)
84
85     @property
86     def changed_fields(self):
87         return self.diff.keys()
88
89     def get_field_diff(self, field_name):
90         return self.diff.get(field_name, None)
91
92     def can_update(self, user):
93         if user.is_readonly:
94             return False
95         if user.is_admin:
96             return True
97         return False
98
99     def delete(self, *args, **kwds):
100         # so we have something to give the observer
101         purge = kwds.get('purge',False)
102         silent = kwds.get('silent',False)
103         try:
104             purge = purge or observer_disabled
105         except NameError:
106             pass
107
108         if (purge):
109             del kwds['purge']
110             super(PlCoreBase, self).delete(*args, **kwds)
111         else:
112             self.deleted = True
113             self.enacted=None
114             self.save(update_fields=['enacted','deleted'], silent=silent)
115
116     def save(self, *args, **kwargs):
117         # let the user specify silence as either a kwarg or an instance varible
118         silent = self.silent
119         if "silent" in kwargs:
120             silent=silent or kwargs.pop("silent")
121
122         super(PlCoreBase, self).save(*args, **kwargs)
123
124         # This is a no-op if observer_disabled is set
125         if not silent:
126             notify_observer()
127
128         self.__initial = self._dict
129
130     def save_by_user(self, user, *args, **kwds):
131         if self.can_update(user):
132             self.save(*args, **kwds)
133
134     def delete_by_user(self, user, *args, **kwds):
135         if self.can_update(user):
136             self.delete(*args, **kwds)
137
138     @property
139     def _dict(self):
140         return model_to_dict(self, fields=[field.name for field in
141                              self._meta.fields])
142
143
144