2

Still learning Django, so not sure if there's a nice way to do this.

I have a few models with specific attributes (all use Item as base class), and a metadata table (id, language, type, value) used to store any extra attributes that could be potentially associated with instances of any of those models (code below). These models are used with a form / template, simple web-based CRUD.

Right now, I call .save_metadata(...) and .load_metadata(...) explicitly, and use .form_initia(...) to populate the form with metadata that isn't explicitly in the model.

I'm looking for a way to handle this automatically -- basically implementing a model with a variable number of fields, key ones are columns in the model's table, the other ones are rows in the metadata table, and are instance-specific. Is there a way of hooking a method after objects.get(...) or objects.filter(...) etc? I've messed with custom managers and looked into signals, but nothing seems to lead towards an acceptable solution.

class Item(models.Model):
  mdata = ['title'] # metadata associated with item

  user = models.ForeignKey(User)
  created = models.DateTimeField(auto_now_add = True)
  status = models.IntegerField(default=0, choices = ([(0,'Staged'), (1,'Published'),(2,'Archived'), ]))

  def set_status(self, s):
    self.status = s
    self.save()

  # stores metadata attributes associated with current item
  def save_metadata(self, lang, form):
    for mt in self.mdata:
      try:
        md = Metadata.objects.get(item=self, lang=lang, t=mt)
      except Metadata.DoesNotExist:
        md = Metadata.objects.create(item=self, lang=lang, t=mt)
      md.v=form.cleaned_data[mt]
      md.save()

  # retrieves metadata attributes associated with current item
  def load_metadata(self, lang):
    for mt in self.mdata:
      self.__dict__[mt] = None
      try:
        self.__dict__[mt] = Metadata.objects.get(item=self, t=mt, lang=lang).v
      except Metadata.DoesNotExist:
        md = Metadata.objects.filter(item=self, t=mt)
        if len(md) > 0:
          self.__dict__[mt] = md[0].v

  # provides metadata attributes associated with current item needed to populate a form
  def form_initial(self, seed=None):
    meta = {}
    for mt in self.mdata:
      meta[mt] = self.__dict__[mt]
      #meta[mt] = 'test'
    if seed:
      meta = dict(meta.items() + seed.items())
    return meta

# used to store various metadata associated with models derived from Item
class Metadata(models.Model):
  item = models.ForeignKey(Item)
  lang = models.CharField(max_length = 8)
  t = models.CharField(max_length = 250)
  v = models.CharField(max_length = 2500)
animuson
  • 53,861
  • 28
  • 137
  • 147
btk
  • 3,158
  • 2
  • 29
  • 30
  • I'm not sure, what you try to accomplish, but wouldn't a Many2Many Relation with Metadata do the job? – philgiese Nov 11 '11 at 19:46
  • I'm trying to store matching sets of attributes in multiple languages for each model -- it seemed like using a key/value model was the preferred approach. Would Many2Many give me access to the custom attributes on model instances? – btk Nov 11 '11 at 19:55
  • Check out django-eav, implements a common design pattern for solving your problem. – J.Spiral Aug 21 '12 at 08:03

0 Answers0