views:

35

answers:

1

Howdy,

I am trying to figure out how to use proxy classes in Django. I want to receive a queryset where each object belongs to a proxy class of a common super class so that I can run custom sub-classed methods with the same name and my controller logic doesn't need to know or care about which kind of Proxy model it is working with. One thing I don't want to do is to store the information in multiple tables because I want to have unified identifiers for easier reference/management.

I am pretty new to django/python so I would be happy to hear alternative ways to accomplish what I am trying to do.

Here is what I have:

TYPES = (
    ('aol','AOL'),
    ('yhoo','Yahoo'),
)

class SuperConnect(models.Model):
  name = models.CharField(max_length=90)
  type = models.CharField(max_length=45, choices = TYPES)
  connection_string = models.TextField(null=True)

class ConnectAOL(SuperConnect):
  class Meta:
    proxy = True

  def connect(self):
     conn_options = self.deconstruct_constring()
     # do special stuff to connect to AOL

  def deconstruct_constring(self):
     return pickle.loads(self.connection_string)

class ConnectYahoo(SuperConnect):
  class Meta:
    proxy = True

  def connect(self):
     conn_options = self.deconstruct_constring()
     # do special stuff to connect to Yahoo

  def deconstruct_constring(self):
     return pickle.loads(self.connection_string)

Now what I want to do is this:

connections = SuperConnect.objects.all()

for connection in connections:
  connection.connect()
  connection.dostuff

I've looked around and found some hacks but they look questionable and may require me to go to the database for each item in order to retrieve data I probably already have...

Somebody please rescue me :) or I am going to go with this hack:

class MixedQuerySet(QuerySet):
    def __getitem__(self, k):
        item = super(MixedQuerySet, self).__getitem__(k)
        if item.atype == 'aol':
            yield(ConnectAOL.objects.get(id=item.id))
        elif item.atype == 'yhoo':
            yield(ConnectYahoo.objects.get(id=item.id))
        else:
            raise NotImplementedError

    def __iter__(self):
        for item in super(MixedQuerySet, self).__iter__():
            if item.atype == 'aol':
                yield(ConnectAOL.objects.get(id=item.id))
            elif item.atype == 'yhoo':
                yield(ConnectYahoo.objects.get(id=item.id))
            else:
                raise NotImplementedError

class MixManager(models.Manager):
    def get_query_set(self):
        return MixedQuerySet(self.model)

TYPES = (
    ('aol','AOL'),
    ('yhoo','Yahoo'),
)

class SuperConnect(models.Model):
  name = models.CharField(max_length=90)
  atype = models.CharField(max_length=45, choices = TYPES)
  connection_string = models.TextField(null=True)
  objects = MixManager()

class ConnectAOL(SuperConnect):
  class Meta:
    proxy = True

  def connect(self):
     conn_options = self.deconstruct_constring()
     # do special stuff to connect to AOL

  def deconstruct_constring(self):
     return pickle.loads(self.connection_string)

class ConnectYahoo(SuperConnect):
  class Meta:
    proxy = True

  def connect(self):
     conn_options = self.deconstruct_constring()
     # do special stuff to connect to Yahoo

  def deconstruct_constring(self):
     return pickle.loads(self.connection_string)
A: 

How about putting all the logic in one class. Something like this:

def connect(self):
    return getattr(self, "connect_%s" % self.type)()

def connect_aol(self):
    pass # AOL stuff

def connect_yahoo(self):
    pass # Yahoo! stuff

In the end you have your type field and you should be able to do most (if not all) things that you can do with seperate proxy classes.

If this approach doesn't solve your specific use cases, please clarify.

muhuk
muhuk, thanks! this would work, I just wonder if there might be a more elegant OOP solution... I have a bunch of things that are done differently for each of the proxy classes and I hope to be able to easily add new ones without hacking away on the same class.
Bubba Raskin
You can easily move `connect_*` methods to your proxy classes and it will work. The main thing here is that Django ORM is not really object oriented. It supports subclassing but not polymorphism. I would give up `obj.__class__.__name__ == "Connect" + obj.type` constraint.
muhuk
ok.. i guess i am going to go with my hack then. :) it works. i just had to override the get method of the QuerySet as well... cheers.
Bubba Raskin