Django

Code

root/django/branches/unicode/django/db/models/related.py

Revision 4698, 5.9 kB (checked in by mtredinnick, 2 years ago)

An improved version of the change attempted in [4693]: retain backwards
compatibility with hand-crafted oldforms without breaking edit_inline.

  • Property svn:eol-style set to native
Line 
1 class BoundRelatedObject(object):
2     def __init__(self, related_object, field_mapping, original):
3         self.relation = related_object
4         self.field_mappings = field_mapping[related_object.name]
5
6     def template_name(self):
7         raise NotImplementedError
8
9     def __repr__(self):
10         return repr(self.__dict__)
11
12 class RelatedObject(object):
13     def __init__(self, parent_model, model, field):
14         self.parent_model = parent_model
15         self.model = model
16         self.opts = model._meta
17         self.field = field
18         self.edit_inline = field.rel.edit_inline
19         self.name = '%s:%s' % (self.opts.app_label, self.opts.module_name)
20         self.var_name = self.opts.object_name.lower()
21
22     def flatten_data(self, follow, obj=None):
23         new_data = {}
24         rel_instances = self.get_list(obj)
25         for i, rel_instance in enumerate(rel_instances):
26             instance_data = {}
27             for f in self.opts.fields + self.opts.many_to_many:
28                 # TODO: Fix for recursive manipulators.
29                 fol = follow.get(f.name, None)
30                 if fol:
31                     field_data = f.flatten_data(fol, rel_instance)
32                     for name, value in field_data.items():
33                         instance_data['%s.%d.%s' % (self.var_name, i, name)] = value
34             new_data.update(instance_data)
35         return new_data
36
37     def extract_data(self, data):
38         """
39         Pull out the data meant for inline objects of this class,
40         i.e. anything starting with our module name.
41         """
42         return data # TODO
43
44     def get_list(self, parent_instance=None):
45         "Get the list of this type of object from an instance of the parent class."
46         if parent_instance is not None:
47             attr = getattr(parent_instance, self.get_accessor_name())
48             if self.field.rel.multiple:
49                 # For many-to-many relationships, return a list of objects
50                 # corresponding to the xxx_num_in_admin options of the field
51                 objects = list(attr.all())
52
53                 count = len(objects) + self.field.rel.num_extra_on_change
54                 if self.field.rel.min_num_in_admin:
55                     count = max(count, self.field.rel.min_num_in_admin)
56                 if self.field.rel.max_num_in_admin:
57                     count = min(count, self.field.rel.max_num_in_admin)
58
59                 change = count - len(objects)
60                 if change > 0:
61                     return objects + [None] * change
62                 if change < 0:
63                     return objects[:change]
64                 else: # Just right
65                     return objects
66             else:
67                 # A one-to-one relationship, so just return the single related
68                 # object
69                 return [attr]
70         else:
71             if self.field.rel.min_num_in_admin:
72                 return [None] * max(self.field.rel.num_in_admin, self.field.rel.min_num_in_admin)
73             else:
74                 return [None] * self.field.rel.num_in_admin
75
76     def get_db_prep_lookup(self, lookup_type, value):
77         # Defer to the actual field definition for db prep
78         return self.field.get_db_prep_lookup(lookup_type, value)
79        
80     def editable_fields(self):
81         "Get the fields in this class that should be edited inline."
82         return [f for f in self.opts.fields + self.opts.many_to_many if f.editable and f != self.field]
83
84     def get_follow(self, override=None):
85         if isinstance(override, bool):
86             if override:
87                 over = {}
88             else:
89                 return None
90         else:
91             if override:
92                 over = override.copy()
93             elif self.edit_inline:
94                 over = {}
95             else:
96                 return None
97
98         over[self.field.name] = False
99         return self.opts.get_follow(over)
100
101     def get_manipulator_fields(self, opts, manipulator, change, follow):
102         if self.field.rel.multiple:
103             if change:
104                 attr = getattr(manipulator.original_object, self.get_accessor_name())
105                 count = attr.count()
106                 count += self.field.rel.num_extra_on_change
107             else:
108                 count = self.field.rel.num_in_admin
109             if self.field.rel.min_num_in_admin:
110                 count = max(count, self.field.rel.min_num_in_admin)
111             if self.field.rel.max_num_in_admin:
112                 count = min(count, self.field.rel.max_num_in_admin)
113         else:
114             count = 1
115
116         fields = []
117         for i in range(count):
118             for f in self.opts.fields + self.opts.many_to_many:
119                 if follow.get(f.name, False):
120                     prefix = '%s.%d.' % (self.var_name, i)
121                     fields.extend(f.get_manipulator_fields(self.opts, manipulator, change,
122                                                            name_prefix=prefix, rel=True))
123         return fields
124
125     def __repr__(self):
126         return "<RelatedObject: %s related to %s>" % (self.name, self.field.name)
127
128     def bind(self, field_mapping, original, bound_related_object_class=BoundRelatedObject):
129         return bound_related_object_class(self, field_mapping, original)
130
131     def get_accessor_name(self):
132         # This method encapsulates the logic that decides what name to give an
133         # accessor descriptor that retrieves related many-to-one or
134         # many-to-many objects. It uses the lower-cased object_name + "_set",
135         # but this can be overridden with the "related_name" option.
136         if self.field.rel.multiple:
137             # If this is a symmetrical m2m relation on self, there is no reverse accessor.
138             if getattr(self.field.rel, 'symmetrical', False) and self.model == self.parent_model:
139                 return None
140             return self.field.rel.related_name or (self.opts.object_name.lower() + '_set')
141         else:
142             return self.field.rel.related_name or (self.opts.object_name.lower())
Note: See TracBrowser for help on using the browser.