Author: floguy
Date: Sun Sep 21 19:54:40 2008
New Revision: 32

Modified:
    trunk/things/__init__.py
    trunk/things/fields.py
    trunk/things/options.py
    trunk/thingsproject/dev.db
    trunk/thingsproject/twitter/admin.py
    trunk/thingsproject/twitter/models.py
    trunk/thingsproject/twitter/thing.py
    trunk/thingsproject/urls.py

Log:
Refactored the aggregate support to be a bit more DRY (using multiple  
inhericance O_o).  Also added support for ordering based on sums either  
from GenericForeignKeys or direct Foreign Keys.

Modified: trunk/things/__init__.py
==============================================================================
--- trunk/things/__init__.py    (original)
+++ trunk/things/__init__.py    Sun Sep 21 19:54:40 2008
@@ -1,6 +1,7 @@
  from things.options import ModelThing, BaseThing
  from things.sites import ThingSite, site
  from things.fields import OrderField, OrderCountField,  
OrderGenericCountField
+from things.fields import OrderSumField, OrderGenericSumField

  def autodiscover():
      """

Modified: trunk/things/fields.py
==============================================================================
--- trunk/things/fields.py      (original)
+++ trunk/things/fields.py      Sun Sep 21 19:54:40 2008
@@ -7,6 +7,8 @@

  __all__ = ('OrderField', )

+qn = connection.ops.quote_name
+
  def create_setter(prop):
      def inner(self, value):
          setattr(self, prop, value)
@@ -15,8 +17,6 @@
  class BaseField(object):
      parent = None
      field_name = None
-    modifies_query_set = False
-    is_aggregate = False

  class OrderField(BaseField):
      def __init__(self, verbose_name_asc=None, verbose_name_desc=None,
@@ -80,16 +80,23 @@
                  'url_prefix': self.parent.url_prefix,
          })

-class OrderCountField(OrderField):
-    modifies_query_set = True
-    is_aggregate=True
+class AggregateBase(object):
+    def get_aggregate_name(self):
+        raise NotImplementedError

-    def _get_aggregate_name(self):
-        return '%s_count' % self.field_name
-    aggregate_name = property(_get_aggregate_name)
+    def get_aggregate_sql(self):
+        raise NotImplementedError
+
+    def get_sql_args(self):
+        raise NotImplementedError

      def modify_query_set(self, qs):
-        qn = connection.ops.quote_name
+        sql = self.get_aggregate_sql()
+        args = self.get_sql_args()
+        return qs.extra(select={self.get_aggregate_name(): sql % args})
+
+class ForeignKeyAggregate(AggregateBase):
+    def get_sql_args(self):
          try:
              parent_field = self.parent.opts.get_field(self.field_name)
              m2m = True
@@ -115,36 +122,89 @@
                  raise ValueError("Could not determine relationship on  
related name %s" % self.field_name)
              related_table = qn(related_table)
              related_column_name = qn(related_column_name)
-
-        SQL = """
+        return {
+            'related_table': related_table,
+            'related_column_name': related_column_name,
+            'self_table': self_table,
+        }
+
+class GenericForeignKeyAggregate(AggregateBase):
+    def get_sql_args(self):
+        related_table = qn(self.generic_model._meta.db_table)
+        self_content_type = int(ContentType.objects.get_for_model(
+            self.parent.model).id)
+        self_table = qn(self.parent.model._meta.db_table)
+        return {
+            'related_table': related_table,
+            'self_content_type': self_content_type,
+            'self_table': self_table,
+        }
+
+class OrderSumField(OrderField, ForeignKeyAggregate):
+    def __init__(self, sum_field, *args, **kwargs):
+        self.sum_field = sum_field
+        super(OrderSumField, self).__init__(*args, **kwargs)
+
+    def get_aggregate_sql(self):
+        return """
+            SELECT COALESCE(SUM(%(sum_field)s), 0)
+            FROM %(related_table)s
+            WHERE %(related_table)s.%(related_column_name)s  
= %(self_table)s.id
+        """
+
+    def get_aggregate_name(self):
+        return '%s_sum' % self.field_name
+
+    def get_sql_args(self):
+        args = super(OrderSumField, self).get_sql_args().copy()
+        args.update({'sum_field': self.sum_field})
+        return args
+
+class OrderCountField(OrderField, ForeignKeyAggregate):
+    def get_aggregate_name(self):
+        return '%s_count' % self.field_name
+
+    def get_aggregate_sql(self):
+        return """
          SELECT COUNT(*)
          FROM %(related_table)s
          WHERE %(related_table)s.%(related_column_name)s = %(self_table)s.id
-        """ % locals()
-        return qs.extra(select={self.aggregate_name: SQL})
-
-class OrderGenericCountField(OrderField):
-    modifies_query_set = True
-    is_aggregate = True
+        """

+class OrderGenericCountField(OrderField, GenericForeignKeyAggregate):
      def __init__(self, generic_model, *args, **kwargs):
          self.generic_model = generic_model
          super(OrderGenericCountField, self).__init__(*args, **kwargs)

-    def _get_aggregate_name(self):
+    def get_aggregate_name(self):
          return '%s_count' % self.field_name
-    aggregate_name = property(_get_aggregate_name)

-    def modify_query_set(self, qs):
-        qn = connection.ops.quote_name
-        related_table = qn(self.generic_model._meta.db_table)
-        self_content_type = int(ContentType.objects.get_for_model(
-            self.parent.model).id)
-        self_table = qn(self.parent.model._meta.db_table)
-        SQL = u"""
+    def get_aggregate_sql(self):
+        return u"""
          SELECT COUNT(*)
          FROM %(related_table)s
          WHERE %(related_table)s.content_type_id=%(self_content_type)i
          AND %(related_table)s.object_id=%(self_table)s.id
-        """ % locals()
-        return qs.extra(select={self.aggregate_name: SQL})
\ No newline at end of file
+        """
+
+class OrderGenericSumField(OrderField, GenericForeignKeyAggregate):
+    def __init__(self, generic_model, sum_field, *args, **kwargs):
+        self.generic_model = generic_model
+        self.sum_field = sum_field
+        super(OrderGenericSumField, self).__init__(*args, **kwargs)
+
+    def get_aggregate_name(self):
+        return '%s_count' % self.field_name
+
+    def get_sql_args(self):
+        args = super(OrderGenericSumField, self).get_sql_args().copy()
+        args.update({'sum_field': self.sum_field})
+        return args
+
+    def get_aggregate_sql(self):
+        return u"""
+        SELECT COALESCE(SUM(%(sum_field)s), 0)
+        FROM %(related_table)s
+        WHERE %(related_table)s.content_type_id=%(self_content_type)i
+        AND %(related_table)s.object_id=%(self_table)s.id
+        """
\ No newline at end of file

Modified: trunk/things/options.py
==============================================================================
--- trunk/things/options.py     (original)
+++ trunk/things/options.py     Sun Sep 21 19:54:40 2008
@@ -7,7 +7,7 @@
  from django.template.loader import select_template
  from django.core.urlresolvers import reverse
  from django.db.models import Q
-from things.fields import BaseField, OrderCountField
+from things.fields import BaseField, OrderCountField, AggregateBase

  DETAIL_RE = re.compile('^(\d+)/$')

@@ -59,8 +59,8 @@
              pre = ''
              if descending == True:
                  pre = '-'
-            if field.is_aggregate:
-                items = items.order_by('%s%s' % (pre,  
field.aggregate_name))
+            if isinstance(field, AggregateBase):
+                items = items.order_by('%s%s' % (pre,  
field.get_aggregate_name()))
              else:
                  items = items.order_by('%s%s' % (pre, field.field_name))
          templates = ['%s/%s' % (self.template_dir, template_name,),
@@ -134,7 +134,7 @@
      def get_query_set(self):
          query_set = self.model._default_manager.all()
          for field_name, field in self.fields.iteritems():
-            if field.modifies_query_set:
+            if isinstance(field, AggregateBase):
                  query_set = field.modify_query_set(query_set)
          return query_set


Modified: trunk/thingsproject/dev.db
==============================================================================
Binary files. No diff available.

Modified: trunk/thingsproject/twitter/admin.py
==============================================================================
--- trunk/thingsproject/twitter/admin.py        (original)
+++ trunk/thingsproject/twitter/admin.py        Sun Sep 21 19:54:40 2008
@@ -1,5 +1,7 @@
  from django.contrib import admin

  from models import Tweet
+from models import Vote

-admin.site.register(Tweet)
\ No newline at end of file
+admin.site.register(Tweet)
+admin.site.register(Vote)
\ No newline at end of file

Modified: trunk/thingsproject/twitter/models.py
==============================================================================
--- trunk/thingsproject/twitter/models.py       (original)
+++ trunk/thingsproject/twitter/models.py       Sun Sep 21 19:54:40 2008
@@ -11,3 +11,7 @@

      class Admin:
          pass
+
+class Vote(models.Model):
+    value = models.IntegerField()
+    tweet = models.ForeignKey(Tweet)
\ No newline at end of file

Modified: trunk/thingsproject/twitter/thing.py
==============================================================================
--- trunk/thingsproject/twitter/thing.py        (original)
+++ trunk/thingsproject/twitter/thing.py        Sun Sep 21 19:54:40 2008
@@ -5,6 +5,7 @@
      date_posted = things.OrderField(verbose_name_asc='Newest',
          verbose_name_desc='Oldest', url_asc='newest', url_desc='oldest',
          field_url='date')
+    vote_set = things.OrderSumField('value')
      message = things.OrderField()
      search = ('message', 'date_posted')


Modified: trunk/thingsproject/urls.py
==============================================================================
--- trunk/thingsproject/urls.py (original)
+++ trunk/thingsproject/urls.py Sun Sep 21 19:54:40 2008
@@ -12,4 +12,4 @@

  urlpatterns = patterns('',
       (r'^admin/(.*)$', admin.site.root),
-) + tt.urls(url_prefix='tweets')
\ No newline at end of file
+) + tt.urls(url_prefix='tweets/')
\ No newline at end of file

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"pinax-updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/pinax-updates?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to