|
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()) |