Entries in the Category “openerp”

Using distant OpenERP objects pythonically

written by nicoe, on May 19, 2010 10:03:00 PM.

At work we have a project to realize a pygtk POS interface for OpenERP. It is getting closer and closer everyday and after much hagglings to create the right UI, I had to connect it to the underlying server. Using the so called netrpc protocol out of the box is not really the most pythonic you can get.

So I came up with those classes that made me fells just like home when I needed to work with the OpenERP objects.

class MetaOEObject(type):

  def __init__(cls, name, bases, dict):
      super(MetaOEObject, cls).__init__(name, bases, dict)
      cls.proxy = Config.client.create_proxy(Config.database,
                                             dict['dotted_name'])
      cls.fields = cls.proxy.fields_get()
      OEObject.proxies[dict['dotted_name']] = cls


class OEObject(object):

  proxies = {}

  def __init__(self, id=None):
      self.id = id
      if id is not None:
          self.value = self.proxy.read(id)

  @classmethod
  def select(cls, condition):
      ids = cls.proxy.search(condition)
      return [cls(id) for id in ids]

  def __getattr__(self, name):
      if name not in self.fields:
          raise AttributeError
      elif self.fields[name]['type'] == 'many2one':
          oeobj = OEObject.proxies[self.fields[name]['relation']]
          if self.value[name]:
              return oeobj(self.value[name][0])
          else:
              return None
      elif self.fields[name]['type'] == 'one2many':
          oeobj = OEObject.proxies[self.fields[name]['relation']]
          return [oeobj(id) for ids in self.value[name]]
      elif self.fields[name]['type'] == 'binary':
          if not self.value[name]:
              return None
          filename = os.tempnam()
          fd = open(filename, 'w')
          fd.write(base64.b64decode(self.value[name]))
          fd.flush()
          return filename
      return self.value[name]

  def __str__(self):
      print self.dotted_name, self.id
      return '<%s (%d)>' % (self.dotted_name, self.id)


class Category(OEObject):
  __metaclass__ = MetaOEObject
  dotted_name = 'product.category'


class Product(OEObject):
  __metaclass__ = MetaOEObject
  dotted_name = 'product.product'

Those kind of classes makes use of metaclasses so that proxy and fields are class-attributes which seems nicer to me. It allows me write pieces of code like this

for category in Category.select([('parent_id', '=', None)]):
    page = self.create_category_page(category, notebook)
    for product in Product.select([('categ_id', '=', category.id)]):
        page.add_product(product)

Which populates a GtkIconView into a notebook used to display products sorted by categories.