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