1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103 | from django.db import models
class Fieldset(object):
"""
Fieldset objects can be declared within model declarations to allow
fields to be automatically grouped into fieldsets.
django.db.models.Field.creation_counter is used to preserve the
order of fields. When Python 3 is used, ordered attr dictionaries
passed to metaclass __new__ methods would eliminate this requirement.
Simply declare a new Fieldset object between field declaration in
your model, and all following fields are automatically included in it.
Any fields preceeding Fieldset declarations are grouped into a
plain fieldset.
Fieldset objects take an optional name argument, and a number of
keyword arguments. 'classes' and 'description' correspond to the
dictionary values in a django fieldset. 'grouped' is a boolean shortcut
argument for enclosing all fields in the fieldset within a tuple.
Subsets of fields can be grouped by calling the Fieldset.new_group(n)
static method, which causes the following n fields to be grouped
using a tuple (appear in one row of a django form).
"""
fieldsets = {}
groups = {}
def __init__(self, name=None, **kwargs):
self.name = name
self.classes = kwargs.get('classes', None)
self.description = kwargs.get('description', None)
self.fields = []
self.last = None
self.grouped = kwargs.get('grouped', False)
if kwargs.get('dummy'):
self.first = None
else:
self.first = models.Field.creation_counter;
Fieldset.fieldsets[None] = Fieldset.fieldsets.get(None, [])+[self]
def __contains(self,field): # will raise if self.first == self.last == None
if self.last == None:
return (field.creation_counter >= self.first)
if self.first == None:
return (field.creation_counter <= self.last)
return (field.creation_counter in range(self.first, self.last+1))
def get_fieldset(self):
d = {'fields' : self.fields}
if self.grouped: d['fields'] = (tuple(d['fields']),)
if self.classes: d['classes'] = self.classes
if self.description: d['description'] = self.description
return (self.name, d)
@staticmethod
def new_group(n):
Fieldset.groups[models.Field.creation_counter] = n
@staticmethod
def which_group(field):
for g in Fieldset.groups:
if field.creation_counter in range(g,g+Fieldset.groups[g]):
return g
return None
@staticmethod
def claim_fieldsets(model):
unclaimed = [Fieldset(dummy=True)] + Fieldset.fieldsets.pop(None, [])
for (i,f) in enumerate(unclaimed[:-1]):
f.last = unclaimed[i+1].first - 1
Fieldset.fieldsets[model] = unclaimed
@staticmethod
def get_fieldsets(model):
fieldsets = Fieldset.fieldsets.get(model.__name__, [])
for f in fieldsets:
group = []
group_id = None
for field in model._meta.fields:
if field.editable and not field.auto_created:
if f.__contains(field):
g = Fieldset.which_group(field)
if g: # field is part of a group
if g!=group_id and group!=[]: # new adjacent group
# flush old one first:
f.fields.append(tuple(group))
group = []
group.append(field.name)
group_id = g
else:
if group != []: # must have just finished a group
f.fields.append(tuple(group)) # append it first
group = []
f.fields.append(field.name) # then the next field
if group != []:
f.fields.append(tuple(group)) # append any trailing group
return [f.get_fieldset() for f in fieldsets if f.fields]
class MyBase(models.base.ModelBase):
def __new__(cls, name, bases, attrs):
Fieldset.claim_fieldsets(name)
return super(MyBase, cls).__new__(cls, name, bases, attrs)
class ModelWithFieldsets(models.Model):
__metaclass__ = MyBase
class Meta:
abstract = True
|
Comments