web/hdabo/fields.py
author ymh <ymh.work@gmail.com>
Tue, 12 Jul 2011 18:24:56 +0200
changeset 97 0d0b23401d14
parent 21 20d3375b6d28
permissions -rw-r--r--
version 00.05
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
11
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
# -*- coding: utf-8 -*-
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
from django.db import models, router
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
from django.db.models import signals
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
     4
from django.db.models.fields.related import (create_many_related_manager, 
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
     5
    ManyToManyField, ReverseManyRelatedObjectsDescriptor)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
     6
from hdabo.forms import SortedMultipleChoiceField
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
from hdabo.utils import OrderedSet
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
SORT_VALUE_FIELD_NAME = 'sort_value'
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
def create_sorted_many_related_manager(superclass, rel):
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
    RelatedManager = create_many_related_manager(superclass, rel)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
    class SortedRelatedManager(RelatedManager):
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    17
        def get_query_set(self):
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    18
            # We use ``extra`` method here because we have no other access to
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    19
            # the extra sorting field of the intermediary model. The fields
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    20
            # are hidden for joins because we set ``auto_created`` on the
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    21
            # intermediary's meta options.
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
            return super(SortedRelatedManager, self).\
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
                get_query_set().\
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
                order_by('%s.%s' % (
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
                    rel.through._meta.db_table,
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
                    rel.through._sort_field_name,))
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
        def _add_items(self, source_field_name, target_field_name, *objs):
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
            # join_table: name of the m2m link table
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
            # source_field_name: the PK fieldname in join_table for the source object
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
            # target_field_name: the PK fieldname in join_table for the target object
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
            # *objs - objects to add. Either object instances, or primary keys of object instances.
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
            # If there aren't any objects, there is nothing to do.
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    35
            from django.db.models import Model
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    36
            if objs:
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
                count = self.through._default_manager.count
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
                new_ids = OrderedSet()
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    39
                for obj in objs:
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
                    if isinstance(obj, self.model):
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
                        if not router.allow_relation(obj, self.instance):
21
20d3375b6d28 reformat code
ymh <ymh.work@gmail.com>
parents: 11
diff changeset
    42
                            raise ValueError('Cannot add "%r": instance is on database "%s", value is on database "%s"' % 
11
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    43
                                               (obj, self.instance._state.db, obj._state.db))
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    44
                        new_ids.add(obj.pk)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    45
                    elif isinstance(obj, Model):
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
                        raise TypeError("'%s' instance expected" % self.model._meta.object_name)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    47
                    else:
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    48
                        new_ids.add(obj)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
                db = router.db_for_write(self.through, instance=self.instance)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
                vals = self.through._default_manager.using(db).values_list(target_field_name, flat=True)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    51
                vals = vals.filter(**{
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    52
                    source_field_name: self._pk_val,
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    53
                    '%s__in' % target_field_name: new_ids,
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    54
                })
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    55
                new_ids = new_ids - OrderedSet(vals)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    56
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    57
                if self.reverse or source_field_name == self.source_field_name:
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    58
                    # Don't send the signal when we are inserting the
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    59
                    # duplicate data row for symmetrical reverse entries.
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    60
                    signals.m2m_changed.send(sender=rel.through, action='pre_add',
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    61
                        instance=self.instance, reverse=self.reverse,
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    62
                        model=self.model, pk_set=new_ids, using=db)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
                # Add the ones that aren't there already
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
                count = self.through._default_manager.count
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
                for obj_id in new_ids:
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
                    self.through._default_manager.using(db).create(**{
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
                        '%s_id' % source_field_name: self._pk_val,
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
                        '%s_id' % target_field_name: obj_id,
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
                        self.through._sort_field_name: count(),
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
                    })
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    71
                if self.reverse or source_field_name == self.source_field_name:
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
                    # Don't send the signal when we are inserting the
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    73
                    # duplicate data row for symmetrical reverse entries.
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    74
                    signals.m2m_changed.send(sender=rel.through, action='post_add',
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    75
                        instance=self.instance, reverse=self.reverse,
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    76
                        model=self.model, pk_set=new_ids, using=db)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    77
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    78
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    79
    return SortedRelatedManager
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    80
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    81
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    82
class ReverseSortedManyRelatedObjectsDescriptor(ReverseManyRelatedObjectsDescriptor):
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    83
    def __get__(self, instance, instance_type=None):
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    84
        if instance is None:
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
            return self
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
        # Dynamically create a class that subclasses the related
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
        # model's default manager.
21
20d3375b6d28 reformat code
ymh <ymh.work@gmail.com>
parents: 11
diff changeset
    89
        rel_model = self.field.rel.to
11
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
        superclass = rel_model._default_manager.__class__
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
        RelatedManager = create_sorted_many_related_manager(superclass, self.field.rel)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    92
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    93
        manager = RelatedManager(
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    94
            model=rel_model,
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    95
            core_filters={'%s__pk' % self.field.related_query_name(): instance._get_pk_val()},
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    96
            instance=instance,
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    97
            symmetrical=(self.field.rel.symmetrical and isinstance(instance, rel_model)),
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    98
            source_field_name=self.field.m2m_field_name(),
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
    99
            target_field_name=self.field.m2m_reverse_field_name(),
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   100
            reverse=False
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   101
        )
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   102
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   103
        return manager
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   104
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   105
    def __set__(self, instance, value):
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   106
        if instance is None:
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   107
            raise AttributeError, "Manager must be accessed via instance"
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   108
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   109
        manager = self.__get__(instance)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   110
        manager.clear()
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   111
        manager.add(*value)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   112
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   113
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   114
class SortedManyToManyField(ManyToManyField):
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   115
    '''
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   116
    Providing a many to many relation that remembers the order of related
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   117
    objects.
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   118
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   119
    Accept a boolean ``sorted`` attribute which specifies if relation is
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   120
    ordered or not. Default is set to ``True``. If ``sorted`` is set to
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   121
    ``False`` the field will behave exactly like django's ``ManyToManyField``.
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   122
    '''
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   123
    def __init__(self, to, sorted=True, **kwargs):
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   124
        self.sorted = sorted
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   125
        if self.sorted:
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   126
            # This is very hacky and should be removed if a better solution is
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   127
            # found.
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   128
            kwargs.setdefault('through', True)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   129
        super(SortedManyToManyField, self).__init__(to, **kwargs)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   130
        self.help_text = kwargs.get('help_text', None)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   131
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   132
    def create_intermediary_model(self, cls, field_name):
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   133
        '''
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   134
        Create intermediary model that stores the relation's data.
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   135
        '''
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   136
        module = ''
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   137
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   138
        # make sure rel.to is a model class and not a string
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   139
        if isinstance(self.rel.to, basestring):
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   140
            bits = self.rel.to.split('.')
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   141
            if len(bits) == 1:
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   142
                bits = cls._meta.app_label.lower(), bits[0]
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   143
            self.rel.to = models.get_model(*bits)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   144
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   145
        model_name = '%s_%s_%s' % (
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   146
            cls._meta.app_label,
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   147
            cls._meta.object_name,
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   148
            field_name)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   149
        from_ = '%s.%s' % (
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   150
            cls._meta.app_label,
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   151
            cls._meta.object_name)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   152
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   153
        def default_sort_value():
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   154
            model = models.get_model(cls._meta.app_label, model_name)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   155
            return model._default_manager.count()
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   156
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   157
        # Using from and to model's name as field names for relations. This is
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   158
        # also django default behaviour for m2m intermediary tables.
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   159
        fields = {
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   160
            cls._meta.object_name.lower():
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   161
                models.ForeignKey(from_),
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   162
            # using to model's name as field name for the other relation
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   163
            self.rel.to._meta.object_name.lower():
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   164
                models.ForeignKey(self.rel.to),
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   165
            SORT_VALUE_FIELD_NAME:
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   166
                models.IntegerField(default=default_sort_value),
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   167
        }
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   168
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   169
        class Meta:
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   170
            db_table = '%s_%s_%s' % (
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   171
                cls._meta.app_label.lower(),
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   172
                cls._meta.object_name.lower(),
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   173
                field_name.lower())
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   174
            app_label = cls._meta.app_label
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   175
            ordering = (SORT_VALUE_FIELD_NAME,)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   176
            auto_created = cls
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   177
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   178
        attrs = {
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   179
            '__module__': module,
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   180
            'Meta': Meta,
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   181
            '_sort_field_name': SORT_VALUE_FIELD_NAME,
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   182
            '__unicode__': lambda s: 'pk=%d' % s.pk,
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   183
        }
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   184
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   185
        # Add in any fields that were provided
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   186
        if fields:
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   187
            attrs.update(fields)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   188
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   189
        # Create the class, which automatically triggers ModelBase processing
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   190
        model = type(model_name, (models.Model,), attrs)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   191
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   192
        return model
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   193
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   194
    def contribute_to_class(self, cls, name):
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   195
        if self.sorted:
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   196
            self.rel.through = self.create_intermediary_model(cls, name)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   197
            super(SortedManyToManyField, self).contribute_to_class(cls, name)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   198
            # overwrite default descriptor with reverse and sorted one
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   199
            setattr(cls, self.name, ReverseSortedManyRelatedObjectsDescriptor(self))
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   200
        else:
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   201
            super(SortedManyToManyField, self).contribute_to_class(cls, name)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   202
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   203
    def formfield(self, **kwargs):
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   204
        defaults = {}
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   205
        if self.sorted:
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   206
            defaults['form_class'] = SortedMultipleChoiceField
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   207
        defaults.update(kwargs)
143ab88d17f8 add ordered manytomany fields and indexing
ymh <ymh.work@gmail.com>
parents:
diff changeset
   208
        return super(SortedManyToManyField, self).formfield(**defaults)