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)