Login or Sign up

Django OrderedManyToManyField

Posted by: skyl on Sept. 14, 2010

def OrderedManyToManyField(model, through, order_by, name=None,
                         back_reference_name=None):
  """
  Similar to django ManyToManyField but order can be on attribute
  in intermediary model. (This will work with as a setter as well
  if the name of the model is in the same namespace as the current
  model

  A workaround for this bug: http://code.djangoproject.com/ticket/11850

  @param model: The target model
  @param through: The intermediary model
  @param order_by: String specifier of the column to order by
                   (intermediatemodel__colname)
  @param name: Optional custom name for the property
  @param back_reference_name: The name of the field on the through
      table refererncing this model (default is the class name in
      lowercase)
  """
  frame = sys._getframe(1)
  f_locals = frame.f_locals
  f_globals = frame.f_globals

  through_name = through
  if isinstance(through, django.db.models.Model):
      through_name = through.__class__.__name__

  if name is None:
      if isinstance(model, django.db.models.Model):
          name = model.__class__.__name__.lower()
      else:
          name = model.lower()
      name = name + 's'
  unordered_name = '_unordered_%s' % name
  f_locals[unordered_name] = ManyToManyField(model, through=through)

  def _get(self):
      return getattr(self, unordered_name).order_by(order_by)

  def _set(self, objects):
      through_model = f_globals[through_name]
      refname = back_reference_name
      if not refname:
          refname = self.__class__.__name__.lower()
      kw = { refname : self }
      through_model.objects.filter(**kw).delete()
      for (index, obj) in enumerate(objects):
          seqname = order_by[len(through_name)+2:]
          kw = { seqname: index + 1,
                 refname: self,
                 name[:-1]: obj}
          through_model.objects.create(**kw).save()

  return property(_get, _set)

Comments on This Post:

Please Login (or Sign Up) to leave a comment