diff -r 57b4279d8c4e -r 03e267d67478 app/django/db/models/related.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/django/db/models/related.py Fri Jul 18 18:22:23 2008 +0000 @@ -0,0 +1,142 @@ +class BoundRelatedObject(object): + def __init__(self, related_object, field_mapping, original): + self.relation = related_object + self.field_mappings = field_mapping[related_object.name] + + def template_name(self): + raise NotImplementedError + + def __repr__(self): + return repr(self.__dict__) + +class RelatedObject(object): + def __init__(self, parent_model, model, field): + self.parent_model = parent_model + self.model = model + self.opts = model._meta + self.field = field + self.edit_inline = field.rel.edit_inline + self.name = '%s:%s' % (self.opts.app_label, self.opts.module_name) + self.var_name = self.opts.object_name.lower() + + def flatten_data(self, follow, obj=None): + new_data = {} + rel_instances = self.get_list(obj) + for i, rel_instance in enumerate(rel_instances): + instance_data = {} + for f in self.opts.fields + self.opts.many_to_many: + # TODO: Fix for recursive manipulators. + fol = follow.get(f.name, None) + if fol: + field_data = f.flatten_data(fol, rel_instance) + for name, value in field_data.items(): + instance_data['%s.%d.%s' % (self.var_name, i, name)] = value + new_data.update(instance_data) + return new_data + + def extract_data(self, data): + """ + Pull out the data meant for inline objects of this class, + i.e. anything starting with our module name. + """ + return data # TODO + + def get_list(self, parent_instance=None): + "Get the list of this type of object from an instance of the parent class." + if parent_instance is not None: + attr = getattr(parent_instance, self.get_accessor_name()) + if self.field.rel.multiple: + # For many-to-many relationships, return a list of objects + # corresponding to the xxx_num_in_admin options of the field + objects = list(attr.all()) + + count = len(objects) + self.field.rel.num_extra_on_change + if self.field.rel.min_num_in_admin: + count = max(count, self.field.rel.min_num_in_admin) + if self.field.rel.max_num_in_admin: + count = min(count, self.field.rel.max_num_in_admin) + + change = count - len(objects) + if change > 0: + return objects + [None] * change + if change < 0: + return objects[:change] + else: # Just right + return objects + else: + # A one-to-one relationship, so just return the single related + # object + return [attr] + else: + if self.field.rel.min_num_in_admin: + return [None] * max(self.field.rel.num_in_admin, self.field.rel.min_num_in_admin) + else: + return [None] * self.field.rel.num_in_admin + + def get_db_prep_lookup(self, lookup_type, value): + # Defer to the actual field definition for db prep + return self.field.get_db_prep_lookup(lookup_type, value) + + def editable_fields(self): + "Get the fields in this class that should be edited inline." + return [f for f in self.opts.fields + self.opts.many_to_many if f.editable and f != self.field] + + def get_follow(self, override=None): + if isinstance(override, bool): + if override: + over = {} + else: + return None + else: + if override: + over = override.copy() + elif self.edit_inline: + over = {} + else: + return None + + over[self.field.name] = False + return self.opts.get_follow(over) + + def get_manipulator_fields(self, opts, manipulator, change, follow): + if self.field.rel.multiple: + if change: + attr = getattr(manipulator.original_object, self.get_accessor_name()) + count = attr.count() + count += self.field.rel.num_extra_on_change + else: + count = self.field.rel.num_in_admin + if self.field.rel.min_num_in_admin: + count = max(count, self.field.rel.min_num_in_admin) + if self.field.rel.max_num_in_admin: + count = min(count, self.field.rel.max_num_in_admin) + else: + count = 1 + + fields = [] + for i in range(count): + for f in self.opts.fields + self.opts.many_to_many: + if follow.get(f.name, False): + prefix = '%s.%d.' % (self.var_name, i) + fields.extend(f.get_manipulator_fields(self.opts, manipulator, change, + name_prefix=prefix, rel=True)) + return fields + + def __repr__(self): + return "" % (self.name, self.field.name) + + def bind(self, field_mapping, original, bound_related_object_class=BoundRelatedObject): + return bound_related_object_class(self, field_mapping, original) + + def get_accessor_name(self): + # This method encapsulates the logic that decides what name to give an + # accessor descriptor that retrieves related many-to-one or + # many-to-many objects. It uses the lower-cased object_name + "_set", + # but this can be overridden with the "related_name" option. + if self.field.rel.multiple: + # If this is a symmetrical m2m relation on self, there is no reverse accessor. + if getattr(self.field.rel, 'symmetrical', False) and self.model == self.parent_model: + return None + return self.field.rel.related_name or (self.opts.object_name.lower() + '_set') + else: + return self.field.rel.related_name or (self.opts.object_name.lower())