| author | ymh <ymh.work@gmail.com> |
| Tue, 12 Jul 2011 18:24:56 +0200 | |
| changeset 97 | 0d0b23401d14 |
| parent 21 | 20d3375b6d28 |
| permissions | -rw-r--r-- |
|
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 | 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 | 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) |