[cig-commits] r14576 - in cs/portal/trunk/northridge/SeismoWebPortal: . templates/SeismoWebPortal

leif at geodynamics.org leif at geodynamics.org
Thu Apr 2 17:24:49 PDT 2009


Author: leif
Date: 2009-04-02 17:24:46 -0700 (Thu, 02 Apr 2009)
New Revision: 14576

Modified:
   cs/portal/trunk/northridge/SeismoWebPortal/mezzanine.py
   cs/portal/trunk/northridge/SeismoWebPortal/models.py
   cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/event_confirm_delete.html
   cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/mineosmodecatalog_confirm_delete.html
   cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/mineosmodel_confirm_delete.html
   cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/mineosparameters_confirm_delete.html
   cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/run_confirm_delete.html
   cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobemesh_confirm_delete.html
   cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobemodel_confirm_delete.html
   cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobeparameters_confirm_delete.html
   cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/stationlist_confirm_delete.html
Log:
Fixed a bug associated with object deletion.  When an object is
destroyed, the Django framework follows ForeignKeys to find all
"related" objects to destroy (objects which reference the original
object).  But it does not follow GenericForeignKeys, so related Runs
and Ownerships were not being destroyed.  This lead to broken links
and generally erratic behavior in the portal.

This finally solves the mystery of the occasional portal crash:
"AssertionError: get() returned more than one Ownership -- it returned
2!".

In addition, when a user attempts to delete an object referenced by
other objects, the portal now displays a list of all the other objects
that will be destroyed on the delete confirmation page.  (Before,
users weren't even aware they were playing Jenga.)


Modified: cs/portal/trunk/northridge/SeismoWebPortal/mezzanine.py
===================================================================
--- cs/portal/trunk/northridge/SeismoWebPortal/mezzanine.py	2009-04-02 21:38:30 UTC (rev 14575)
+++ cs/portal/trunk/northridge/SeismoWebPortal/mezzanine.py	2009-04-03 00:24:46 UTC (rev 14576)
@@ -29,6 +29,10 @@
     views = []
     defaultView = 'detail'
 
+    def __str__(self): return str(self.obj)
+
+    name = property(lambda self: str(self))
+
     @classmethod
     def create(cls, request, template_name=None,
                template_loader=loader, extra_context=None, post_save_redirect=None,
@@ -196,17 +200,23 @@
 
         if request.method == 'POST':
             message = 'Deleted "%s".' % object
-            ownership = models.Ownership.objects.get(objType = self.contentType(), objId = object.id)
-            ownership.delete()
-            if not object.isArchival():
-                object.delete()
+            object.delete()
             if request.user.is_authenticated():
                 request.user.message_set.create(message = message)
             if post_delete_redirect:
                 return HttpResponseRedirect(post_delete_redirect)
             else:
                 return HttpResponseRedirect(self.postDeleteRedirect(request.root))
+
         else:
+            related_objects = []
+            for related in self.relatedObjects(request.user):
+                related_objects.append(dict(
+                    url = related.url(request.root),
+                    icon = related.icon(request.root),
+                    name = related.name,
+                    ))
+            
             if not template_name:
                 template_name = "%s/%s_confirm_delete.html" % (model._meta.app_label, model._meta.object_name.lower())
             t = template_loader.get_template(template_name)
@@ -214,6 +224,7 @@
                 template_object_name: object,
                 'action': request.path,
                 'root': request.root,
+                'related_objects': related_objects,
             })
             for key, value in extra_context.items():
                 if callable(value):
@@ -431,13 +442,27 @@
     @classmethod
     def userObjectList(cls, user):
         return cls.Model.userObjectList(user)
-        
+
+    def belongsToUser(self, user):
+        userObjList, builtInCount = self.userObjectList(user)
+        userObjList = userObjList[builtInCount:]
+        for uo in userObjList:
+            if uo.id == self.id:
+                return True
+        return False
+
     @classmethod
     def urlForObject(cls, obj, root, action=None):
         if action:
             return "%s/?class=%s&object=%d&action=%s" % (root, cls.__name__, obj.id, action)
         return "%s/?class=%s&object=%d" % (root, cls.__name__, obj.id)
 
+    def url(self, root, action=None):
+        return self.urlForObject(self, root, action)
+
+    def icon(self, root):
+        return root + "/images/" + self.iconFilename
+
     def postDeleteRedirect(self, root):
         return "%s/?class=%s" % (root, self.__class__.__name__)
 
@@ -489,7 +514,32 @@
         
         return
 
+    def relatedObjects(self, user):
+        l = []
+        g = globals()
+        opts = self.Model._meta
+        for related in opts.get_all_related_objects():
+            for obj in getattr(self.obj, related.get_accessor_name()).all():
+                Class = g.get(obj.__class__.__name__)
+                if Class is None:
+                    continue
+                obj = Class(obj)
+                if obj.belongsToUser(user):
+                    l.append(obj)
+                    l.extend(obj.relatedObjects(user))
+        l.extend(self.auxRelatedObjects())
+        return l
 
+    def auxRelatedObjects(self): return []
+
+    def relatedRuns(self):
+        l = []
+        ctype = self.contentType()
+        for obj in models.Run.objects.filter(parametersId = self.obj.id, parametersType = ctype):
+            l.append(Run(obj))
+        return l
+
+
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 # Events
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -506,6 +556,8 @@
 
     views = [('detail', "Mechanism"), ('map', "Map")]
 
+    iconFilename = "events.gif"
+
     @classmethod
     def create(cls, request):
         return super(Event, cls).create(
@@ -712,6 +764,8 @@
     views = [('map', "Map"), ('detail', "List")]
     defaultView = 'map'
 
+    iconFilename = "stationlist.gif"
+
     def downloadAsText(self, request):
         response = HttpResponse(mimetype='text/plain')
         response['Content-Disposition'] = 'attachment; filename=%s' % self.obj.name
@@ -744,6 +798,8 @@
 
     duplicatable = property(lambda self: self.obj.nchunks != 6) # see the comment in create()
 
+    iconFilename = "mesh.gif"
+
     @classmethod
     def create(cls, request):
         nchunks = request.REQUEST.get('nchunks')
@@ -813,6 +869,8 @@
     duplicatable = False
     downloadableAsTarGz = True
 
+    iconFilename = "s20rts.gif"
+
     def changeManipulator(self, user):
         object = self.obj
         return self.Model.ChangeManipulator(getattr(object, object._meta.pk.attname),
@@ -836,6 +894,8 @@
 
 class Specfem3DGlobeParameters(Object):
     Model = models.Specfem3DGlobeParameters
+    iconFilename = "parameters.gif"
+    auxRelatedObjects = Object.relatedRuns
 
     @classmethod
     def create(cls, request):
@@ -856,6 +916,7 @@
             'model_choices': Specfem3DGlobeModel.userObjectList(request.user)[0]
             }
 
+
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 # Mineos
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -863,6 +924,7 @@
 
 class MineosModeCatalog(Object):
     Model = models.MineosModeCatalog
+    iconFilename = "modecat.gif"
 
 
 class MineosModel(Object):
@@ -874,6 +936,8 @@
     downloadableAsText = True
     duplicatable = False
 
+    iconFilename = "prem.gif"
+
     def changeManipulator(self, user):
         object = self.obj
         return self.Model.ChangeManipulator(getattr(object, object._meta.pk.attname), follow = dict(data = False))
@@ -887,9 +951,10 @@
 
 class MineosParameters(Object):
     Model = models.MineosParameters
+    iconFilename = "parameters.gif"
+    auxRelatedObjects = Object.relatedRuns
 
 
-
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 # Runs
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -897,6 +962,7 @@
 
 class Run(Object):
     Model = models.Run
+    iconFilename = "rocket.gif"
 
     def editable(self, builtIn):
         return super(Run, self).editable(builtIn) and self.obj.isDraft

Modified: cs/portal/trunk/northridge/SeismoWebPortal/models.py
===================================================================
--- cs/portal/trunk/northridge/SeismoWebPortal/models.py	2009-04-02 21:38:30 UTC (rev 14575)
+++ cs/portal/trunk/northridge/SeismoWebPortal/models.py	2009-04-03 00:24:46 UTC (rev 14576)
@@ -31,7 +31,16 @@
         objList.extend([o.obj for o in Ownership.objects.filter(owner = user, objType = cls.contentType())])
         return objList, builtInCount
 
+    def _collect_sub_objects(self, seen_objs):
+        super(Model, self)._collect_sub_objects(seen_objs)
+        if hasattr(self, 'isArchival') and self.isArchival():
+            # Don't delete archived objects.
+            del seen_objs[self.__class__][self._get_pk_val()]
+        for obj in Ownership.objects.filter(objType = self.contentType(), objId = self.id):
+            obj._collect_sub_objects(seen_objs)
+        return
 
+
 class MaybeArchival(object):
 
     def isArchival(self):
@@ -545,6 +554,12 @@
                     estCost = c
         return estCost
 
+    def _collect_sub_objects(self, seen_objs):
+        super(Specfem3DGlobeParameters, self)._collect_sub_objects(seen_objs)
+        for obj in Run.objects.filter(parametersId = self.id, parametersType = self.contentType()):
+            obj._collect_sub_objects(seen_objs)
+        return
+
     code = 1
 
 
@@ -639,7 +654,12 @@
 
     def estimatedCost(self, run):
         return None # NYI
-        
+    
+    def _collect_sub_objects(self, seen_objs):
+        super(MineosParameters, self)._collect_sub_objects(seen_objs)
+        for obj in Run.objects.filter(parametersId = self.id, parametersType = self.contentType()):
+            obj._collect_sub_objects(seen_objs)
+        return
 
     code = 2
 

Modified: cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/event_confirm_delete.html
===================================================================
--- cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/event_confirm_delete.html	2009-04-02 21:38:30 UTC (rev 14575)
+++ cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/event_confirm_delete.html	2009-04-03 00:24:46 UTC (rev 14576)
@@ -6,5 +6,13 @@
     <input type="hidden" name="object" value="{{object.id}}">
     <input type="hidden" name="action" value="delete">
     <p>Are you sure you want to delete the event "{{ object }}"?
+    {% if related_objects %}
+    <p>All of the following related items will also be deleted:</p>
+    <ul>
+        {% for object in related_objects %}
+        <li><img src="{{object.icon}}"> <a href="{{object.url}}">{{ object.name }}</a></li>
+        {% endfor %}
+    </ul>
+    {% endif %}
     <p><input type="submit" value="Delete" />
 </form>

Modified: cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/mineosmodecatalog_confirm_delete.html
===================================================================
--- cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/mineosmodecatalog_confirm_delete.html	2009-04-02 21:38:30 UTC (rev 14575)
+++ cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/mineosmodecatalog_confirm_delete.html	2009-04-03 00:24:46 UTC (rev 14576)
@@ -6,5 +6,13 @@
     <input type="hidden" name="object" value="{{object.id}}">
     <input type="hidden" name="action" value="delete">
     <p>Are you sure you want to delete the parameters "{{ object }}"?
+    {% if related_objects %}
+    <p>All of the following related items will also be deleted:</p>
+    <ul>
+        {% for object in related_objects %}
+        <li><img src="{{object.icon}}"> <a href="{{object.url}}">{{ object.name }}</a></li>
+        {% endfor %}
+    </ul>
+    {% endif %}
     <p><input type="submit" value="Delete" />
 </form>

Modified: cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/mineosmodel_confirm_delete.html
===================================================================
--- cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/mineosmodel_confirm_delete.html	2009-04-02 21:38:30 UTC (rev 14575)
+++ cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/mineosmodel_confirm_delete.html	2009-04-03 00:24:46 UTC (rev 14576)
@@ -6,5 +6,13 @@
     <input type="hidden" name="object" value="{{object.id}}">
     <input type="hidden" name="action" value="delete">
     <p>Are you sure you want to delete the model "{{ object }}"?
+    {% if related_objects %}
+    <p>All of the following related items will also be deleted:</p>
+    <ul>
+        {% for object in related_objects %}
+        <li><img src="{{object.icon}}"> <a href="{{object.url}}">{{ object.name }}</a></li>
+        {% endfor %}
+    </ul>
+    {% endif %}
     <p><input type="submit" value="Delete" />
 </form>

Modified: cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/mineosparameters_confirm_delete.html
===================================================================
--- cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/mineosparameters_confirm_delete.html	2009-04-02 21:38:30 UTC (rev 14575)
+++ cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/mineosparameters_confirm_delete.html	2009-04-03 00:24:46 UTC (rev 14576)
@@ -6,5 +6,13 @@
     <input type="hidden" name="object" value="{{object.id}}">
     <input type="hidden" name="action" value="delete">
     <p>Are you sure you want to delete the parameters "{{ object }}"?
+    {% if related_objects %}
+    <p>All of the following related items will also be deleted:</p>
+    <ul>
+        {% for object in related_objects %}
+        <li><img src="{{object.icon}}"> <a href="{{object.url}}">{{ object.name }}</a></li>
+        {% endfor %}
+    </ul>
+    {% endif %}
     <p><input type="submit" value="Delete" />
 </form>

Modified: cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/run_confirm_delete.html
===================================================================
--- cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/run_confirm_delete.html	2009-04-02 21:38:30 UTC (rev 14575)
+++ cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/run_confirm_delete.html	2009-04-03 00:24:46 UTC (rev 14576)
@@ -6,5 +6,13 @@
     <input type="hidden" name="object" value="{{object.id}}">
     <input type="hidden" name="action" value="delete">
     <p>Are you sure you want to delete {{ object }}?
+    {% if related_objects %}
+    <p>All of the following related items will also be deleted:</p>
+    <ul>
+        {% for object in related_objects %}
+        <li><img src="{{object.icon}}"> <a href="{{object.url}}">{{ object.name }}</a></li>
+        {% endfor %}
+    </ul>
+    {% endif %}
     <p><input type="submit" value="Delete" />
 </form>

Modified: cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobemesh_confirm_delete.html
===================================================================
--- cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobemesh_confirm_delete.html	2009-04-02 21:38:30 UTC (rev 14575)
+++ cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobemesh_confirm_delete.html	2009-04-03 00:24:46 UTC (rev 14576)
@@ -6,5 +6,13 @@
     <input type="hidden" name="object" value="{{object.id}}">
     <input type="hidden" name="action" value="delete">
     <p>Are you sure you want to delete the mesh "{{ object }}"?
+    {% if related_objects %}
+    <p>All of the following related items will also be deleted:</p>
+    <ul>
+        {% for object in related_objects %}
+        <li><img src="{{object.icon}}"> <a href="{{object.url}}">{{ object.name }}</a></li>
+        {% endfor %}
+    </ul>
+    {% endif %}
     <p><input type="submit" value="Delete" />
 </form>

Modified: cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobemodel_confirm_delete.html
===================================================================
--- cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobemodel_confirm_delete.html	2009-04-02 21:38:30 UTC (rev 14575)
+++ cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobemodel_confirm_delete.html	2009-04-03 00:24:46 UTC (rev 14576)
@@ -6,5 +6,13 @@
     <input type="hidden" name="object" value="{{object.id}}">
     <input type="hidden" name="action" value="delete">
     <p>Are you sure you want to delete the model "{{ object }}"?
+    {% if related_objects %}
+    <p>All of the following related items will also be deleted:</p>
+    <ul>
+        {% for object in related_objects %}
+        <li><img src="{{object.icon}}"> <a href="{{object.url}}">{{ object.name }}</a></li>
+        {% endfor %}
+    </ul>
+    {% endif %}
     <p><input type="submit" value="Delete" />
 </form>

Modified: cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobeparameters_confirm_delete.html
===================================================================
--- cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobeparameters_confirm_delete.html	2009-04-02 21:38:30 UTC (rev 14575)
+++ cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobeparameters_confirm_delete.html	2009-04-03 00:24:46 UTC (rev 14576)
@@ -6,5 +6,13 @@
     <input type="hidden" name="object" value="{{object.id}}">
     <input type="hidden" name="action" value="delete">
     <p>Are you sure you want to delete the parameters "{{ object }}"?
+    {% if related_objects %}
+    <p>All of the following related items will also be deleted:</p>
+    <ul>
+        {% for object in related_objects %}
+        <li><img src="{{object.icon}}"> <a href="{{object.url}}">{{ object.name }}</a></li>
+        {% endfor %}
+    </ul>
+    {% endif %}
     <p><input type="submit" value="Delete" />
 </form>

Modified: cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/stationlist_confirm_delete.html
===================================================================
--- cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/stationlist_confirm_delete.html	2009-04-02 21:38:30 UTC (rev 14575)
+++ cs/portal/trunk/northridge/SeismoWebPortal/templates/SeismoWebPortal/stationlist_confirm_delete.html	2009-04-03 00:24:46 UTC (rev 14576)
@@ -6,5 +6,13 @@
     <input type="hidden" name="object" value="{{object.id}}">
     <input type="hidden" name="action" value="delete">
     <p>Are you sure you want to delete the station list "{{ object }}"?
+    {% if related_objects %}
+    <p>All of the following related items will also be deleted:</p>
+    <ul>
+        {% for object in related_objects %}
+        <li><img src="{{object.icon}}"> <a href="{{object.url}}">{{ object.name }}</a></li>
+        {% endfor %}
+    </ul>
+    {% endif %}
     <p><input type="submit" value="Delete" />
 </form>



More information about the CIG-COMMITS mailing list