Commit 174641b9 authored by Russell Keith-Magee's avatar Russell Keith-Magee
Browse files

Fixed #6095 -- Added the ability to specify the model to use to manage a...

Fixed #6095 -- Added the ability to specify the model to use to manage a ManyToManyField. Thanks to Eric Florenzano for his excellent work on this patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@8136 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent f752f692
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -154,6 +154,7 @@ answer newbie questions, and generally made Django that much better:
    Maciej Fijalkowski
    Matthew Flanagan <http://wadofstuff.blogspot.com>
    Eric Floehr <eric@intellovations.com>
    Eric Florenzano <floguy@gmail.com>
    Vincent Foley <vfoleybourgon@yahoo.ca>
    Rudolph Froger <rfroger@estrate.nl>
    Jorge Gajon <gajon@gajon.org>
+4 −1
Original line number Diff line number Diff line
@@ -161,7 +161,10 @@ class BaseModelAdmin(object):
                kwargs['empty_label'] = db_field.blank and _('None') or None
            else:
                if isinstance(db_field, models.ManyToManyField):
                    if db_field.name in self.raw_id_fields:
                    # If it uses an intermediary model, don't show field in admin. 
                    if db_field.rel.through is not None:
                        return None
                    elif db_field.name in self.raw_id_fields:
                        kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel)
                        kwargs['help_text'] = ''
                    elif db_field.name in (list(self.filter_vertical) + list(self.filter_horizontal)):
+3 −0
Original line number Diff line number Diff line
@@ -104,6 +104,9 @@ class GenericRelation(RelatedField, Field):
                            limit_choices_to=kwargs.pop('limit_choices_to', None),
                            symmetrical=kwargs.pop('symmetrical', True))

        # By its very nature, a GenericRelation doesn't create a table.
        self.creates_table = False

        # Override content-type/object-id field names on the related class
        self.object_id_field_name = kwargs.pop("object_id_field", "object_id")
        self.content_type_field_name = kwargs.pop("content_type_field", "content_type")
+1 −1
Original line number Diff line number Diff line
@@ -353,7 +353,7 @@ def many_to_many_sql_for_model(model, style):
    qn = connection.ops.quote_name
    inline_references = connection.features.inline_fk_references
    for f in opts.local_many_to_many:
        if not isinstance(f.rel, generic.GenericRel):
        if f.creates_table:
            tablespace = f.db_tablespace or opts.db_tablespace
            if tablespace and connection.features.supports_tablespaces: 
                tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace, inline=True)
+44 −1
Original line number Diff line number Diff line
@@ -102,6 +102,7 @@ def get_validation_errors(outfile, app=None):
                        if r.get_accessor_name() == rel_query_name:
                            e.add(opts, "Reverse query name for field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))

        seen_intermediary_signatures = [] 
        for i, f in enumerate(opts.local_many_to_many):
            # Check to see if the related m2m field will clash with any
            # existing fields, m2m fields, m2m related objects or related
@@ -112,6 +113,48 @@ def get_validation_errors(outfile, app=None):
                # so skip the next section
                if isinstance(f.rel.to, (str, unicode)):
                    continue
            if getattr(f.rel, 'through', None) is not None:
                if hasattr(f.rel, 'through_model'):
                    from_model, to_model = cls, f.rel.to
                    if from_model == to_model and f.rel.symmetrical:
                        e.add(opts, "Many-to-many fields with intermediate tables cannot be symmetrical.")
                    seen_from, seen_to, seen_self = False, False, 0
                    for inter_field in f.rel.through_model._meta.fields:
                        rel_to = getattr(inter_field.rel, 'to', None)
                        if from_model == to_model: # relation to self
                            if rel_to == from_model:
                                seen_self += 1
                            if seen_self > 2:
                                e.add(opts, "Intermediary model %s has more than two foreign keys to %s, which is ambiguous and is not permitted." % (f.rel.through_model._meta.object_name, from_model._meta.object_name))
                        else:
                            if rel_to == from_model:
                                if seen_from:
                                    e.add(opts, "Intermediary model %s has more than one foreign key to %s, which is ambiguous and is not permitted." % (f.rel.through_model._meta.object_name, rel_from._meta.object_name))
                                else:
                                    seen_from = True
                            elif rel_to == to_model:
                                if seen_to:
                                    e.add(opts, "Intermediary model %s has more than one foreign key to %s, which is ambiguous and is not permitted." % (f.rel.through_model._meta.object_name, rel_to._meta.object_name))
                                else:
                                    seen_to = True
                    if f.rel.through_model not in models.get_models():
                        e.add(opts, "'%s' specifies an m2m relation through model %s, which has not been installed." % (f.name, f.rel.through))
                    signature = (f.rel.to, cls, f.rel.through_model)
                    if signature in seen_intermediary_signatures:
                        e.add(opts, "The model %s has two manually-defined m2m relations through the model %s, which is not permitted. Please consider using an extra field on your intermediary model instead." % (cls._meta.object_name, f.rel.through_model._meta.object_name))
                    else:
                        seen_intermediary_signatures.append(signature)
                    seen_related_fk, seen_this_fk = False, False
                    for field in f.rel.through_model._meta.fields:
                        if field.rel:
                            if not seen_related_fk and field.rel.to == f.rel.to:
                                seen_related_fk = True
                            elif field.rel.to == cls:
                                seen_this_fk = True
                    if not seen_related_fk or not seen_this_fk:
                        e.add(opts, "'%s' has a manually-defined m2m relation through model %s, which does not have foreign keys to %s and %s" % (f.name, f.rel.through, f.rel.to._meta.object_name, cls._meta.object_name))
                else:
                    e.add(opts, "'%s' specifies an m2m relation through model %s, which has not been installed" % (f.name, f.rel.through))
            
            rel_opts = f.rel.to._meta
            rel_name = RelatedObject(f.rel.to, cls, f).get_accessor_name()
Loading