diff --git a/django/core/management.py b/django/core/management.py
index 87db88611a..61213d0965 100644
--- a/django/core/management.py
+++ b/django/core/management.py
@@ -211,35 +211,38 @@ def _get_sql_for_pending_references(klass, pending_references):
 
 def _get_many_to_many_sql_for_model(klass):
     from django.db import backend, get_creation_module
+    from django.db.models import GenericRel
+    
     data_types = get_creation_module().DATA_TYPES
 
     opts = klass._meta
     final_output = []
     for f in opts.many_to_many:
-        table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
-            style.SQL_TABLE(backend.quote_name(f.m2m_db_table())) + ' (']
-        table_output.append('    %s %s %s,' % \
-            (style.SQL_FIELD(backend.quote_name('id')),
-            style.SQL_COLTYPE(data_types['AutoField']),
-            style.SQL_KEYWORD('NOT NULL PRIMARY KEY')))
-        table_output.append('    %s %s %s %s (%s),' % \
-            (style.SQL_FIELD(backend.quote_name(f.m2m_column_name())),
-            style.SQL_COLTYPE(data_types[get_rel_data_type(opts.pk)] % opts.pk.__dict__),
-            style.SQL_KEYWORD('NOT NULL REFERENCES'),
-            style.SQL_TABLE(backend.quote_name(opts.db_table)),
-            style.SQL_FIELD(backend.quote_name(opts.pk.column))))
-        table_output.append('    %s %s %s %s (%s),' % \
-            (style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())),
-            style.SQL_COLTYPE(data_types[get_rel_data_type(f.rel.to._meta.pk)] % f.rel.to._meta.pk.__dict__),
-            style.SQL_KEYWORD('NOT NULL REFERENCES'),
-            style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)),
-            style.SQL_FIELD(backend.quote_name(f.rel.to._meta.pk.column))))
-        table_output.append('    %s (%s, %s)' % \
-            (style.SQL_KEYWORD('UNIQUE'),
-            style.SQL_FIELD(backend.quote_name(f.m2m_column_name())),
-            style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name()))))
-        table_output.append(');')
-        final_output.append('\n'.join(table_output))
+        if not isinstance(f.rel, GenericRel):
+            table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
+                style.SQL_TABLE(backend.quote_name(f.m2m_db_table())) + ' (']
+            table_output.append('    %s %s %s,' % \
+                (style.SQL_FIELD(backend.quote_name('id')),
+                style.SQL_COLTYPE(data_types['AutoField']),
+                style.SQL_KEYWORD('NOT NULL PRIMARY KEY')))
+            table_output.append('    %s %s %s %s (%s),' % \
+                (style.SQL_FIELD(backend.quote_name(f.m2m_column_name())),
+                style.SQL_COLTYPE(data_types[get_rel_data_type(opts.pk)] % opts.pk.__dict__),
+                style.SQL_KEYWORD('NOT NULL REFERENCES'),
+                style.SQL_TABLE(backend.quote_name(opts.db_table)),
+                style.SQL_FIELD(backend.quote_name(opts.pk.column))))
+            table_output.append('    %s %s %s %s (%s),' % \
+                (style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())),
+                style.SQL_COLTYPE(data_types[get_rel_data_type(f.rel.to._meta.pk)] % f.rel.to._meta.pk.__dict__),
+                style.SQL_KEYWORD('NOT NULL REFERENCES'),
+                style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)),
+                style.SQL_FIELD(backend.quote_name(f.rel.to._meta.pk.column))))
+            table_output.append('    %s (%s, %s)' % \
+                (style.SQL_KEYWORD('UNIQUE'),
+                style.SQL_FIELD(backend.quote_name(f.m2m_column_name())),
+                style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name()))))
+            table_output.append(');')
+            final_output.append('\n'.join(table_output))
     return final_output
 
 def get_sql_delete(app):
diff --git a/django/db/models/__init__.py b/django/db/models/__init__.py
index 6d6197c162..82b1238723 100644
--- a/django/db/models/__init__.py
+++ b/django/db/models/__init__.py
@@ -8,6 +8,7 @@ from django.db.models.manager import Manager
 from django.db.models.base import Model, AdminOptions
 from django.db.models.fields import *
 from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel, TABULAR, STACKED
+from django.db.models.fields.generic import GenericRelation, GenericRel, GenericForeignKey
 from django.db.models import signals
 from django.utils.functional import curry
 from django.utils.text import capfirst
diff --git a/django/db/models/fields/generic.py b/django/db/models/fields/generic.py
new file mode 100644
index 0000000000..5f4de40e69
--- /dev/null
+++ b/django/db/models/fields/generic.py
@@ -0,0 +1,259 @@
+"""
+Classes allowing "generic" relations through ContentType and object-id fields.
+"""
+
+from django import forms
+from django.core.exceptions import ObjectDoesNotExist
+from django.db import backend
+from django.db.models import signals
+from django.db.models.fields.related import RelatedField, Field, ManyToManyRel
+from django.db.models.loading import get_model
+from django.dispatch import dispatcher
+from django.utils.functional import curry
+
+class GenericForeignKey(object):
+    """
+    Provides a generic relation to any object through content-type/object-id
+    fields.
+    """
+    
+    def __init__(self, ct_field="content_type", fk_field="object_id"):
+        self.ct_field = ct_field
+        self.fk_field = fk_field
+        
+    def contribute_to_class(self, cls, name):
+        # Make sure the fields exist (these raise FieldDoesNotExist, 
+        # which is a fine error to raise here)
+        self.name = name
+        self.model = cls
+        self.cache_attr = "_%s_cache" % name
+        
+        # For some reason I don't totally understand, using weakrefs here doesn't work.
+        dispatcher.connect(self.instance_pre_init, signal=signals.pre_init, sender=cls, weak=False)
+
+        # Connect myself as the descriptor for this field
+        setattr(cls, name, self)
+
+    def instance_pre_init(self, signal, sender, args, kwargs):
+        # Handle initalizing an object with the generic FK instaed of 
+        # content-type/object-id fields.        
+        if kwargs.has_key(self.name):
+            value = kwargs.pop(self.name)
+            kwargs[self.ct_field] = self.get_content_type(value)
+            kwargs[self.fk_field] = value._get_pk_val()
+            
+    def get_content_type(self, obj):
+        # Convenience function using get_model avoids a circular import when using this model
+        ContentType = get_model("contenttypes", "contenttype")
+        return ContentType.objects.get_for_model(obj)
+        
+    def __get__(self, instance, instance_type=None):
+        if instance is None:
+            raise AttributeError, "%s must be accessed via instance" % self.name
+
+        try:
+            return getattr(instance, self.cache_attr)
+        except AttributeError:
+            rel_obj = None
+            ct = getattr(instance, self.ct_field)
+            if ct:
+                try:
+                    rel_obj = ct.get_object_for_this_type(pk=getattr(instance, self.fk_field))
+                except ObjectDoesNotExist:
+                    pass
+            setattr(instance, self.cache_attr, rel_obj)
+            return rel_obj
+
+    def __set__(self, instance, value):
+        if instance is None:
+            raise AttributeError, "%s must be accessed via instance" % self.related.opts.object_name
+
+        ct = None
+        fk = None
+        if value is not None:
+            ct = self.get_content_type(value)
+            fk = value._get_pk_val()
+
+        setattr(instance, self.ct_field, ct)
+        setattr(instance, self.fk_field, fk)
+        setattr(instance, self.cache_attr, value)
+    
+class GenericRelation(RelatedField, Field):
+    """Provides an accessor to generic related objects (i.e. comments)"""
+
+    def __init__(self, to, **kwargs):
+        kwargs['verbose_name'] = kwargs.get('verbose_name', None)
+        kwargs['rel'] = GenericRel(to, 
+                            related_name=kwargs.pop('related_name', None),
+                            limit_choices_to=kwargs.pop('limit_choices_to', None),
+                            symmetrical=kwargs.pop('symmetrical', True))
+                            
+        # 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")                
+        
+        kwargs['blank'] = True
+        kwargs['editable'] = False
+        Field.__init__(self, **kwargs)
+
+    def get_manipulator_field_objs(self):
+        choices = self.get_choices_default()
+        return [curry(forms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
+
+    def get_choices_default(self):
+        return Field.get_choices(self, include_blank=False)
+
+    def flatten_data(self, follow, obj = None):
+        new_data = {}
+        if obj:
+            instance_ids = [instance._get_pk_val() for instance in getattr(obj, self.name).all()]
+            new_data[self.name] = instance_ids
+        return new_data
+
+    def m2m_db_table(self):
+        return self.rel.to._meta.db_table
+
+    def m2m_column_name(self):
+        return self.object_id_field_name
+        
+    def m2m_reverse_name(self):
+        return self.model._meta.pk.attname
+
+    def contribute_to_class(self, cls, name):
+        super(GenericRelation, self).contribute_to_class(cls, name)
+
+        # Save a reference to which model this class is on for future use
+        self.model = cls
+
+        # Add the descriptor for the m2m relation
+        setattr(cls, self.name, ReverseGenericRelatedObjectsDescriptor(self))
+
+    def contribute_to_related_class(self, cls, related):
+        pass
+        
+    def set_attributes_from_rel(self):
+        pass
+
+    def get_internal_type(self):
+        return "ManyToManyField"
+        
+class ReverseGenericRelatedObjectsDescriptor(object):
+    """
+    This class provides the functionality that makes the related-object
+    managers available as attributes on a model class, for fields that have
+    multiple "remote" values and have a GenericRelation defined in their model
+    (rather than having another model pointed *at* them). In the example
+    "article.publications", the publications attribute is a
+    ReverseGenericRelatedObjectsDescriptor instance.
+    """
+    def __init__(self, field):
+        self.field = field
+
+    def __get__(self, instance, instance_type=None):
+        if instance is None:
+            raise AttributeError, "Manager must be accessed via instance"
+
+        # This import is done here to avoid circular import importing this module
+        from django.contrib.contenttypes.models import ContentType
+
+        # Dynamically create a class that subclasses the related model's
+        # default manager.
+        rel_model = self.field.rel.to
+        superclass = rel_model._default_manager.__class__
+        RelatedManager = create_generic_related_manager(superclass)
+
+        manager = RelatedManager(
+            model = rel_model,
+            instance = instance,
+            symmetrical = (self.field.rel.symmetrical and instance.__class__ == rel_model),
+            join_table = backend.quote_name(self.field.m2m_db_table()),
+            source_col_name = backend.quote_name(self.field.m2m_column_name()),
+            target_col_name = backend.quote_name(self.field.m2m_reverse_name()),
+            content_type = ContentType.objects.get_for_model(self.field.model),
+            content_type_field_name = self.field.content_type_field_name,
+            object_id_field_name = self.field.object_id_field_name
+        )
+
+        return manager
+
+    def __set__(self, instance, value):
+        if instance is None:
+            raise AttributeError, "Manager must be accessed via instance"
+
+        manager = self.__get__(instance)
+        manager.clear()
+        for obj in value:
+            manager.add(obj)
+
+def create_generic_related_manager(superclass):
+    """
+    Factory function for a manager that subclasses 'superclass' (which is a
+    Manager) and adds behavior for generic related objects.
+    """
+    
+    class GenericRelatedObjectManager(superclass):
+        def __init__(self, model=None, core_filters=None, instance=None, symmetrical=None,
+                     join_table=None, source_col_name=None, target_col_name=None, content_type=None,
+                     content_type_field_name=None, object_id_field_name=None):
+            
+            super(GenericRelatedObjectManager, self).__init__()
+            self.core_filters = core_filters or {}
+            self.model = model
+            self.content_type = content_type
+            self.symmetrical = symmetrical
+            self.instance = instance
+            self.join_table = join_table
+            self.join_table = model._meta.db_table
+            self.source_col_name = source_col_name
+            self.target_col_name = target_col_name
+            self.content_type_field_name = content_type_field_name
+            self.object_id_field_name = object_id_field_name
+            self.pk_val = self.instance._get_pk_val()
+                        
+        def get_query_set(self):
+            query = {
+                '%s__pk' % self.content_type_field_name : self.content_type.id, 
+                '%s__exact' % self.object_id_field_name : self.pk_val,
+            }
+            return superclass.get_query_set(self).filter(**query)
+
+        def add(self, *objs):
+            for obj in objs:
+                setattr(obj, self.content_type_field_name, self.content_type)
+                setattr(obj, self.object_id_field_name, self.pk_val)
+                obj.save()
+        add.alters_data = True
+
+        def remove(self, *objs):
+            for obj in objs:
+                obj.delete()
+        remove.alters_data = True
+
+        def clear(self):
+            for obj in self.all():
+                obj.delete()
+        clear.alters_data = True
+
+        def create(self, **kwargs):
+            kwargs[self.content_type_field_name] = self.content_type
+            kwargs[self.object_id_field_name] = self.pk_val
+            obj = self.model(**kwargs)
+            obj.save()
+            return obj
+        create.alters_data = True
+
+    return GenericRelatedObjectManager
+
+class GenericRel(ManyToManyRel):
+    def __init__(self, to, related_name=None, limit_choices_to=None, symmetrical=True):
+        self.to = to
+        self.num_in_admin = 0
+        self.related_name = related_name
+        self.filter_interface = None
+        self.limit_choices_to = limit_choices_to or {}
+        self.edit_inline = False
+        self.raw_id_admin = False
+        self.symmetrical = symmetrical
+        self.multiple = True
+        assert not (self.raw_id_admin and self.filter_interface), \
+            "Generic relations may not use both raw_id_admin and filter_interface"
diff --git a/tests/modeltests/generic_relations/__init__.py b/tests/modeltests/generic_relations/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/modeltests/generic_relations/models.py b/tests/modeltests/generic_relations/models.py
new file mode 100644
index 0000000000..a9a775ad6e
--- /dev/null
+++ b/tests/modeltests/generic_relations/models.py
@@ -0,0 +1,108 @@
+"""
+33. Generic relations
+
+Generic relations let an object have a foreign key to any object through a
+content-type/object-id field. A generic foreign key can point to any object,
+be it animal, vegetable, or mineral.
+
+The cannonical example is tags (although this example implementation is *far*
+from complete).
+"""
+
+from django.db import models
+from django.contrib.contenttypes.models import ContentType
+
+class TaggedItem(models.Model):
+    """A tag on an item."""
+    tag = models.SlugField()
+    content_type = models.ForeignKey(ContentType)
+    object_id = models.PositiveIntegerField()
+    
+    content_object = models.GenericForeignKey()
+    
+    class Meta:
+        ordering = ["tag"]
+    
+    def __str__(self):
+        return self.tag
+
+class Animal(models.Model):
+    common_name = models.CharField(maxlength=150)
+    latin_name = models.CharField(maxlength=150)
+    
+    tags = models.GenericRelation(TaggedItem)
+
+    def __str__(self):
+        return self.common_name
+        
+class Vegetable(models.Model):
+    name = models.CharField(maxlength=150)
+    is_yucky = models.BooleanField(default=True)
+    
+    tags = models.GenericRelation(TaggedItem)
+    
+    def __str__(self):
+        return self.name
+    
+class Mineral(models.Model):
+    name = models.CharField(maxlength=150)
+    hardness = models.PositiveSmallIntegerField()
+    
+    # note the lack of an explicit GenericRelation here...
+    
+    def __str__(self):
+        return self.name
+        
+API_TESTS = """
+# Create the world in 7 lines of code...
+>>> lion = Animal(common_name="Lion", latin_name="Panthera leo")
+>>> platypus = Animal(common_name="Platypus", latin_name="Ornithorhynchus anatinus")
+>>> eggplant = Vegetable(name="Eggplant", is_yucky=True)
+>>> bacon = Vegetable(name="Bacon", is_yucky=False)
+>>> quartz = Mineral(name="Quartz", hardness=7)
+>>> for o in (lion, platypus, eggplant, bacon, quartz):
+...     o.save()
+
+# Objects with declared GenericRelations can be tagged directly -- the API
+# mimics the many-to-many API
+>>> lion.tags.create(tag="yellow")
+<TaggedItem: yellow>
+>>> lion.tags.create(tag="hairy")
+<TaggedItem: hairy>
+>>> bacon.tags.create(tag="fatty")
+<TaggedItem: fatty>
+>>> bacon.tags.create(tag="salty")
+<TaggedItem: salty>
+
+>>> lion.tags.all()
+[<TaggedItem: hairy>, <TaggedItem: yellow>]
+>>> bacon.tags.all()
+[<TaggedItem: fatty>, <TaggedItem: salty>]
+
+# You can easily access the content object like a foreign key
+>>> t = TaggedItem.objects.get(tag="salty")
+>>> t.content_object
+<Vegetable: Bacon>
+
+# Recall that the Mineral class doesn't have an explicit GenericRelation
+# defined. That's OK since you can create TaggedItems explicitally.
+>>> tag1 = TaggedItem(content_object=quartz, tag="shiny")
+>>> tag2 = TaggedItem(content_object=quartz, tag="clearish")
+>>> tag1.save()
+>>> tag2.save()
+
+# However, not having the convience takes a small toll when it comes
+# to do lookups
+>>> from django.contrib.contenttypes.models import ContentType
+>>> ctype = ContentType.objects.get_for_model(quartz)
+>>> TaggedItem.objects.filter(content_type__pk=ctype.id, object_id=quartz.id)
+[<TaggedItem: clearish>, <TaggedItem: shiny>]
+
+# You can set a generic foreign key in the way you'd expect
+>>> tag1.content_object = platypus
+>>> tag1.save()
+>>> platypus.tags.all()
+[<TaggedItem: shiny>]
+>>> TaggedItem.objects.filter(content_type__pk=ctype.id, object_id=quartz.id)
+[<TaggedItem: clearish>]
+"""
\ No newline at end of file