Usage

Ordered Field

The OrderedField class keeps all the records in the table in order from 0 to count - 1

from django_ordered_field import OrderedField

class Table(models.Model):
    name = models.CharField(max_length=100)
    order = OrderedField()

Ordered Collection Field

The OrderedCollectionField class is used to keep records in order related to another field. This can be used to make structures like ordered lists and so on.

from django_ordered_field import OrderedCollectionField

class Item(models.Model):
    name = models.OrderedCollectionField(max_length=100)
    item_type = models.IntegerField()
    order = OrderedCollectionField(collection='item_type')

A collection can consist of another signle field or it can be a combination of multiple fields

OrderedCollectionField(collection='item_type')
# Or
OrderedCollectionField(collection=['list', 'sub_list'])

Update table data

Inserting, updating and deletion of instances has to use methods that uses the model.save() and model.delete() methods. queryset.update(…), queryset.delete() and similar functions that omits model.save() and model.delete() will destroy the ordering of the instances.

Item.objects.create(name="A")
# [('A', 0)]
item = Item(name="B")
item.save()
# [('A', 0), ('B', 1)]
Item.objects.create(name="C", order=0)
# [('C', 0), ('A', 1), ('B', 2)]
item = Item.objects.filer(name='A').first()
item.order = -1
item.save()
# [('C', 0), ('B', 1), ('A', 2)]
item = Item.objects.filer(name='A').first()
item.order = 0
item.save()
# [('A', 0), ('C', 1), ('B', 2)]
item = Item.objects.filer(name='A').first()
item.delete()
# [('C', 0), ('B', 1)]
item = Item.objects.filer(name='B').first()
item.delete()
# [('C', 0)]

Other fields updated when order is changed

It is possible to specify other fields than the order field to be automatically updated when a field has its position changed by another field that was inserted/changed/deleted.

The update_auto_now setting will make sure that all date/datetime related fields that are taged to be automatically updated on change will be updated when the order is changed. This setting is default on, so remember to turn ot off if it is not wanted.

OrderedField(update_auto_now=True)

The extra_field_updates is a dictionary and it is used to specify other field to be updated when the order field is changed by anothers position change.

def get_loged_in_user():
    return "KGA"

OrderedField(extra_field_updates={
                               'order_changed_count': models.F("order_changed_count") + 1,
                               'updated_by': get_loged_in_user
                           })

The self_updates_on_collection_change parameter is used to specify fields to be updated when an instance changes collection. Unlike the extra_field_updates which is triggered when a records osition is changed when another field has its position changed the self_updates_on_collection_change works on the active instance and only when it changes collection.

def get_loged_in_user():
    return "KGA"

OrderedField(self_updates_on_collection_change={
                               'order_changed_count': models.F("order_changed_count") + 1,
                               'updated_by': get_loged_in_user
                           })

If self_updates_on_collection_change is the same as extra_field_updates like above then it is also possible to set the self_updates_on_collection_change_like_regular to True to avoid duplicating the settings.

def get_loged_in_user():
    return "KGA"

OrderedField(self_updates_on_collection_change_like_regular=True)

Model inheritance

NB: Remember to manually register the signals by using the add_signals method when using inheritance.

There are two ways to do regular inheritance. The first one is just to add inheritance without doing anything else. By doing this each model that inherit from it has its order related to its own table.

from django_ordered_field import (OrderedField, add_signals_for_inheritance)

class Unit(models.Model):
    name = models.CharField(max_length=100)
    position = OrderedField()


class Video(Unit):
    pass


add_signals_for_inheritance(Unit, Video, "position")


class Audio(Unit):
    pass


add_signals_for_inheritance(Unit, Audio, "position")


Video.objects.create(name="Video")
Quiz.objects.create(name="Audio")
print(list(Unit.objects.all().order_by("position").
                  values_list( "name", "position")))
# [("Video", 0), ("Audio", 0)]

The other method is to use the parent_link_name parameter. This will make the order field use the parrent model for its ordering.

from django_ordered_field import (OrderedField, add_signals_for_inheritance)

class Unit(models.Model):
    name = models.CharField(max_length=100)
    position = OrderedField(parent_link_name='unittwo_ptr')


class Video(Unit):
    pass


add_signals_for_inheritance(Unit, Video, "position")


class Audio(Unit):
    pass


add_signals_for_inheritance(Unit, Audio, "position")


Video.objects.create(name="Video")
Quiz.objects.create(name="Audio")
print(list(Unit.objects.all().order_by("position").
                  values_list( "name", "position")))
# [("Video", 0), ("Audio", 1)]

Abstract model

from django_ordered_field import OrderedField

class CommonInfo(models.Model):
    name = models.CharField(max_length=100)
    position = OrderedField()

    class Meta:
        abstract = True


class Person(CommonInfoTwo):
    description = models.CharField(max_length=100)

Proxy model

NB: Remember to manually register the signals by using the add_signals_for_proxy method when using inheritance.

from django_ordered_field import (OrderedField, add_signals_for_proxy)

class Person(models.Model):
    name = models.CharField(max_length=100)
    position = OrderedField()


class PersonProxy(Person):

    class Meta:
        proxy = True


add_signals_for_proxy(Person, PersonProxy, "position")

Add signals

Current version has a limitation in a few circumstances than one has to mannually register some of the signals. If you use Proxy models or inherit from a model containing a order field then you have to manually register the signals.

Feel free to add a git pull request if you find a way to automatically register thise signals.