Selaa lähdekoodia

Fixed #19774 -- Deprecated the contenttypes.generic module.

It contained models, forms and admin objects causing undesirable
import side effects. Refs #16368.

Thanks to Ramiro, Carl and Loïc for the review.
Simon Charette 11 vuotta sitten
vanhempi
commit
10e3faf191

+ 2 - 2
django/contrib/comments/models.py

@@ -1,6 +1,6 @@
 from django.conf import settings
 from django.contrib.comments.managers import CommentManager
-from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.fields import GenericForeignKey
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.sites.models import Site
 from django.core import urlresolvers
@@ -23,7 +23,7 @@ class BaseCommentAbstractModel(models.Model):
             verbose_name=_('content type'),
             related_name="content_type_set_for_%(class)s")
     object_pk = models.TextField(_('object ID'))
-    content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_pk")
+    content_object = GenericForeignKey(ct_field="content_type", fk_field="object_pk")
 
     # Metadata about the comment
     site = models.ForeignKey(Site)

+ 60 - 0
django/contrib/contenttypes/admin.py

@@ -0,0 +1,60 @@
+from __future__ import unicode_literals
+
+from functools import partial
+
+from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets
+from django.contrib.contenttypes.forms import (
+    BaseGenericInlineFormSet, generic_inlineformset_factory
+)
+from django.forms import ALL_FIELDS
+from django.forms.models import modelform_defines_fields
+
+
+class GenericInlineModelAdmin(InlineModelAdmin):
+    ct_field = "content_type"
+    ct_fk_field = "object_id"
+    formset = BaseGenericInlineFormSet
+
+    def get_formset(self, request, obj=None, **kwargs):
+        if 'fields' in kwargs:
+            fields = kwargs.pop('fields')
+        else:
+            fields = flatten_fieldsets(self.get_fieldsets(request, obj))
+        if self.exclude is None:
+            exclude = []
+        else:
+            exclude = list(self.exclude)
+        exclude.extend(self.get_readonly_fields(request, obj))
+        if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude:
+            # Take the custom ModelForm's Meta.exclude into account only if the
+            # GenericInlineModelAdmin doesn't define its own.
+            exclude.extend(self.form._meta.exclude)
+        exclude = exclude or None
+        can_delete = self.can_delete and self.has_delete_permission(request, obj)
+        defaults = {
+            "ct_field": self.ct_field,
+            "fk_field": self.ct_fk_field,
+            "form": self.form,
+            "formfield_callback": partial(self.formfield_for_dbfield, request=request),
+            "formset": self.formset,
+            "extra": self.extra,
+            "can_delete": can_delete,
+            "can_order": False,
+            "fields": fields,
+            "max_num": self.max_num,
+            "exclude": exclude
+        }
+        defaults.update(kwargs)
+
+        if defaults['fields'] is None and not modelform_defines_fields(defaults['form']):
+            defaults['fields'] = ALL_FIELDS
+
+        return generic_inlineformset_factory(self.model, **defaults)
+
+
+class GenericStackedInline(GenericInlineModelAdmin):
+    template = 'admin/edit_inline/stacked.html'
+
+
+class GenericTabularInline(GenericInlineModelAdmin):
+    template = 'admin/edit_inline/tabular.html'

+ 1 - 1
django/contrib/contenttypes/checks.py

@@ -6,7 +6,7 @@ from django.apps import apps
 
 
 def check_generic_foreign_keys(**kwargs):
-    from .generic import GenericForeignKey
+    from .fields import GenericForeignKey
 
     errors = []
     fields = (obj

+ 576 - 0
django/contrib/contenttypes/fields.py

@@ -0,0 +1,576 @@
+from __future__ import unicode_literals
+
+from collections import defaultdict
+
+from django.core import checks
+from django.core.exceptions import ObjectDoesNotExist
+from django.db import connection
+from django.db import models, router, transaction, DEFAULT_DB_ALIAS
+from django.db.models import signals, FieldDoesNotExist
+from django.db.models.base import ModelBase
+from django.db.models.fields.related import ForeignObject, ForeignObjectRel
+from django.db.models.related import PathInfo
+from django.db.models.sql.datastructures import Col
+from django.contrib.contenttypes.models import ContentType
+from django.utils import six
+from django.utils.deprecation import RenameMethodsBase
+from django.utils.encoding import smart_text, python_2_unicode_compatible
+
+
+class RenameGenericForeignKeyMethods(RenameMethodsBase):
+    renamed_methods = (
+        ('get_prefetch_query_set', 'get_prefetch_queryset', DeprecationWarning),
+    )
+
+
+@python_2_unicode_compatible
+class GenericForeignKey(six.with_metaclass(RenameGenericForeignKeyMethods)):
+    """
+    Provides a generic relation to any object through content-type/object-id
+    fields.
+    """
+
+    def __init__(self, ct_field="content_type", fk_field="object_id", for_concrete_model=True):
+        self.ct_field = ct_field
+        self.fk_field = fk_field
+        self.for_concrete_model = for_concrete_model
+        self.editable = False
+
+    def contribute_to_class(self, cls, name):
+        self.name = name
+        self.model = cls
+        self.cache_attr = "_%s_cache" % name
+        cls._meta.add_virtual_field(self)
+
+        # Only run pre-initialization field assignment on non-abstract models
+        if not cls._meta.abstract:
+            signals.pre_init.connect(self.instance_pre_init, sender=cls)
+
+        setattr(cls, name, self)
+
+    def __str__(self):
+        model = self.model
+        app = model._meta.app_label
+        return '%s.%s.%s' % (app, model._meta.object_name, self.name)
+
+    def check(self, **kwargs):
+        errors = []
+        errors.extend(self._check_content_type_field())
+        errors.extend(self._check_object_id_field())
+        errors.extend(self._check_field_name())
+        return errors
+
+    def _check_content_type_field(self):
+        return _check_content_type_field(
+            model=self.model,
+            field_name=self.ct_field,
+            checked_object=self)
+
+    def _check_object_id_field(self):
+        try:
+            self.model._meta.get_field(self.fk_field)
+        except FieldDoesNotExist:
+            return [
+                checks.Error(
+                    'The field refers to "%s" field which is missing.' % self.fk_field,
+                    hint=None,
+                    obj=self,
+                    id='contenttypes.E001',
+                )
+            ]
+        else:
+            return []
+
+    def _check_field_name(self):
+        if self.name.endswith("_"):
+            return [
+                checks.Error(
+                    'Field names must not end with underscores.',
+                    hint=None,
+                    obj=self,
+                    id='contenttypes.E002',
+                )
+            ]
+        else:
+            return []
+
+    def instance_pre_init(self, signal, sender, args, kwargs, **_kwargs):
+        """
+        Handles initializing an object with the generic FK instead of
+        content-type/object-id fields.
+        """
+        if self.name in kwargs:
+            value = kwargs.pop(self.name)
+            if value is not None:
+                kwargs[self.ct_field] = self.get_content_type(obj=value)
+                kwargs[self.fk_field] = value._get_pk_val()
+            else:
+                kwargs[self.ct_field] = None
+                kwargs[self.fk_field] = None
+
+    def get_content_type(self, obj=None, id=None, using=None):
+        if obj is not None:
+            return ContentType.objects.db_manager(obj._state.db).get_for_model(
+                obj, for_concrete_model=self.for_concrete_model)
+        elif id is not None:
+            return ContentType.objects.db_manager(using).get_for_id(id)
+        else:
+            # This should never happen. I love comments like this, don't you?
+            raise Exception("Impossible arguments to GFK.get_content_type!")
+
+    def get_prefetch_queryset(self, instances, queryset=None):
+        if queryset is not None:
+            raise ValueError("Custom queryset can't be used for this lookup.")
+
+        # For efficiency, group the instances by content type and then do one
+        # query per model
+        fk_dict = defaultdict(set)
+        # We need one instance for each group in order to get the right db:
+        instance_dict = {}
+        ct_attname = self.model._meta.get_field(self.ct_field).get_attname()
+        for instance in instances:
+            # We avoid looking for values if either ct_id or fkey value is None
+            ct_id = getattr(instance, ct_attname)
+            if ct_id is not None:
+                fk_val = getattr(instance, self.fk_field)
+                if fk_val is not None:
+                    fk_dict[ct_id].add(fk_val)
+                    instance_dict[ct_id] = instance
+
+        ret_val = []
+        for ct_id, fkeys in fk_dict.items():
+            instance = instance_dict[ct_id]
+            ct = self.get_content_type(id=ct_id, using=instance._state.db)
+            ret_val.extend(ct.get_all_objects_for_this_type(pk__in=fkeys))
+
+        # For doing the join in Python, we have to match both the FK val and the
+        # content type, so we use a callable that returns a (fk, class) pair.
+        def gfk_key(obj):
+            ct_id = getattr(obj, ct_attname)
+            if ct_id is None:
+                return None
+            else:
+                model = self.get_content_type(id=ct_id,
+                                              using=obj._state.db).model_class()
+                return (model._meta.pk.get_prep_value(getattr(obj, self.fk_field)),
+                        model)
+
+        return (ret_val,
+                lambda obj: (obj._get_pk_val(), obj.__class__),
+                gfk_key,
+                True,
+                self.cache_attr)
+
+    def is_cached(self, instance):
+        return hasattr(instance, self.cache_attr)
+
+    def __get__(self, instance, instance_type=None):
+        if instance is None:
+            return self
+
+        try:
+            return getattr(instance, self.cache_attr)
+        except AttributeError:
+            rel_obj = None
+
+            # Make sure to use ContentType.objects.get_for_id() to ensure that
+            # lookups are cached (see ticket #5570). This takes more code than
+            # the naive ``getattr(instance, self.ct_field)``, but has better
+            # performance when dealing with GFKs in loops and such.
+            f = self.model._meta.get_field(self.ct_field)
+            ct_id = getattr(instance, f.get_attname(), None)
+            if ct_id is not None:
+                ct = self.get_content_type(id=ct_id, using=instance._state.db)
+                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):
+        ct = None
+        fk = None
+        if value is not None:
+            ct = self.get_content_type(obj=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(ForeignObject):
+    """Provides an accessor to generic related objects (e.g. comments)"""
+
+    def __init__(self, to, **kwargs):
+        kwargs['verbose_name'] = kwargs.get('verbose_name', None)
+        kwargs['rel'] = GenericRel(
+            self, to, related_name=kwargs.pop('related_name', None),
+            limit_choices_to=kwargs.pop('limit_choices_to', None),)
+        # 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")
+
+        self.for_concrete_model = kwargs.pop("for_concrete_model", True)
+
+        kwargs['blank'] = True
+        kwargs['editable'] = False
+        kwargs['serialize'] = False
+        # This construct is somewhat of an abuse of ForeignObject. This field
+        # represents a relation from pk to object_id field. But, this relation
+        # isn't direct, the join is generated reverse along foreign key. So,
+        # the from_field is object_id field, to_field is pk because of the
+        # reverse join.
+        super(GenericRelation, self).__init__(
+            to, to_fields=[],
+            from_fields=[self.object_id_field_name], **kwargs)
+
+    def check(self, **kwargs):
+        errors = super(GenericRelation, self).check(**kwargs)
+        errors.extend(self._check_content_type_field())
+        errors.extend(self._check_object_id_field())
+        errors.extend(self._check_generic_foreign_key_existence())
+        return errors
+
+    def _check_content_type_field(self):
+        target = self.rel.to
+        if isinstance(target, ModelBase):
+            return _check_content_type_field(
+                model=target,
+                field_name=self.content_type_field_name,
+                checked_object=self)
+        else:
+            return []
+
+    def _check_object_id_field(self):
+        target = self.rel.to
+        if isinstance(target, ModelBase):
+            opts = target._meta
+            try:
+                opts.get_field(self.object_id_field_name)
+            except FieldDoesNotExist:
+                return [
+                    checks.Error(
+                        'The field refers to %s.%s field which is missing.' % (
+                            opts.object_name, self.object_id_field_name
+                        ),
+                        hint=None,
+                        obj=self,
+                        id='contenttypes.E003',
+                    )
+                ]
+            else:
+                return []
+        else:
+            return []
+
+    def _check_generic_foreign_key_existence(self):
+        target = self.rel.to
+        if isinstance(target, ModelBase):
+            # Using `vars` is very ugly approach, but there is no better one,
+            # because GenericForeignKeys are not considered as fields and,
+            # therefore, are not included in `target._meta.local_fields`.
+            fields = target._meta.virtual_fields
+            if any(isinstance(field, GenericForeignKey) and
+                    field.ct_field == self.content_type_field_name and
+                    field.fk_field == self.object_id_field_name
+                    for field in fields):
+                return []
+            else:
+                return [
+                    checks.Warning(
+                        ('The field defines a generic relation with the model '
+                         '%s.%s, but the model lacks GenericForeignKey.') % (
+                            target._meta.app_label, target._meta.object_name
+                        ),
+                        hint=None,
+                        obj=self,
+                        id='contenttypes.E004',
+                    )
+                ]
+        else:
+            return []
+
+    def resolve_related_fields(self):
+        self.to_fields = [self.model._meta.pk.name]
+        return [(self.rel.to._meta.get_field_by_name(self.object_id_field_name)[0],
+                 self.model._meta.pk)]
+
+    def get_reverse_path_info(self):
+        opts = self.rel.to._meta
+        target = opts.get_field_by_name(self.object_id_field_name)[0]
+        return [PathInfo(self.model._meta, opts, (target,), self.rel, True, False)]
+
+    def get_choices_default(self):
+        return super(GenericRelation, self).get_choices(include_blank=False)
+
+    def value_to_string(self, obj):
+        qs = getattr(obj, self.name).all()
+        return smart_text([instance._get_pk_val() for instance in qs])
+
+    def get_joining_columns(self, reverse_join=False):
+        if not reverse_join:
+            # This error message is meant for the user, and from user
+            # perspective this is a reverse join along the GenericRelation.
+            raise ValueError('Joining in reverse direction not allowed.')
+        return super(GenericRelation, self).get_joining_columns(reverse_join)
+
+    def contribute_to_class(self, cls, name):
+        super(GenericRelation, self).contribute_to_class(cls, name, virtual_only=True)
+        # Save a reference to which model this class is on for future use
+        self.model = cls
+        # Add the descriptor for the relation
+        setattr(cls, self.name, ReverseGenericRelatedObjectsDescriptor(self, self.for_concrete_model))
+
+    def contribute_to_related_class(self, cls, related):
+        pass
+
+    def set_attributes_from_rel(self):
+        pass
+
+    def get_internal_type(self):
+        return "ManyToManyField"
+
+    def get_content_type(self):
+        """
+        Returns the content type associated with this field's model.
+        """
+        return ContentType.objects.get_for_model(self.model,
+                                                 for_concrete_model=self.for_concrete_model)
+
+    def get_extra_restriction(self, where_class, alias, remote_alias):
+        field = self.rel.to._meta.get_field_by_name(self.content_type_field_name)[0]
+        contenttype_pk = self.get_content_type().pk
+        cond = where_class()
+        lookup = field.get_lookup('exact')(Col(remote_alias, field, field), contenttype_pk)
+        cond.add(lookup, 'AND')
+        return cond
+
+    def bulk_related_objects(self, objs, using=DEFAULT_DB_ALIAS):
+        """
+        Return all objects related to ``objs`` via this ``GenericRelation``.
+
+        """
+        return self.rel.to._base_manager.db_manager(using).filter(**{
+            "%s__pk" % self.content_type_field_name: ContentType.objects.db_manager(using).get_for_model(
+                self.model, for_concrete_model=self.for_concrete_model).pk,
+            "%s__in" % self.object_id_field_name: [obj.pk for obj in objs]
+        })
+
+
+def _check_content_type_field(model, field_name, checked_object):
+    """ Check if field named `field_name` in model `model` exists and is
+    valid content_type field (is a ForeignKey to ContentType). """
+
+    try:
+        field = model._meta.get_field(field_name)
+    except FieldDoesNotExist:
+        return [
+            checks.Error(
+                'The field refers to %s.%s field which is missing.' % (
+                    model._meta.object_name, field_name
+                ),
+                hint=None,
+                obj=checked_object,
+                id='contenttypes.E005',
+            )
+        ]
+    else:
+        if not isinstance(field, models.ForeignKey):
+            return [
+                checks.Error(
+                    ('"%s" field is used by a %s '
+                     'as content type field and therefore it must be '
+                     'a ForeignKey.') % (
+                        field_name, checked_object.__class__.__name__
+                    ),
+                    hint=None,
+                    obj=checked_object,
+                    id='contenttypes.E006',
+                )
+            ]
+        elif field.rel.to != ContentType:
+            return [
+                checks.Error(
+                    ('"%s" field is used by a %s '
+                     'as content type field and therefore it must be '
+                     'a ForeignKey to ContentType.') % (
+                        field_name, checked_object.__class__.__name__
+                    ),
+                    hint=None,
+                    obj=checked_object,
+                    id='contenttypes.E007',
+                )
+            ]
+        else:
+            return []
+
+
+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, for_concrete_model=True):
+        self.field = field
+        self.for_concrete_model = for_concrete_model
+
+    def __get__(self, instance, instance_type=None):
+        if instance is None:
+            return self
+
+        # 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)
+
+        qn = connection.ops.quote_name
+        content_type = ContentType.objects.db_manager(instance._state.db).get_for_model(
+            instance, for_concrete_model=self.for_concrete_model)
+
+        join_cols = self.field.get_joining_columns(reverse_join=True)[0]
+        manager = RelatedManager(
+            model=rel_model,
+            instance=instance,
+            source_col_name=qn(join_cols[0]),
+            target_col_name=qn(join_cols[1]),
+            content_type=content_type,
+            content_type_field_name=self.field.content_type_field_name,
+            object_id_field_name=self.field.object_id_field_name,
+            prefetch_cache_name=self.field.attname,
+        )
+
+        return manager
+
+    def __set__(self, instance, value):
+        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, instance=None, symmetrical=None,
+                     source_col_name=None, target_col_name=None, content_type=None,
+                     content_type_field_name=None, object_id_field_name=None,
+                     prefetch_cache_name=None):
+
+            super(GenericRelatedObjectManager, self).__init__()
+            self.model = model
+            self.content_type = content_type
+            self.symmetrical = symmetrical
+            self.instance = instance
+            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.prefetch_cache_name = prefetch_cache_name
+            self.pk_val = self.instance._get_pk_val()
+            self.core_filters = {
+                '%s__pk' % content_type_field_name: content_type.id,
+                '%s' % object_id_field_name: instance._get_pk_val(),
+            }
+
+        def __call__(self, **kwargs):
+            # We use **kwargs rather than a kwarg argument to enforce the
+            # `manager='manager_name'` syntax.
+            manager = getattr(self.model, kwargs.pop('manager'))
+            manager_class = create_generic_related_manager(manager.__class__)
+            return manager_class(
+                model=self.model,
+                instance=self.instance,
+                symmetrical=self.symmetrical,
+                source_col_name=self.source_col_name,
+                target_col_name=self.target_col_name,
+                content_type=self.content_type,
+                content_type_field_name=self.content_type_field_name,
+                object_id_field_name=self.object_id_field_name,
+                prefetch_cache_name=self.prefetch_cache_name,
+            )
+        do_not_call_in_templates = True
+
+        def get_queryset(self):
+            try:
+                return self.instance._prefetched_objects_cache[self.prefetch_cache_name]
+            except (AttributeError, KeyError):
+                db = self._db or router.db_for_read(self.model, instance=self.instance)
+                return super(GenericRelatedObjectManager, self).get_queryset().using(db).filter(**self.core_filters)
+
+        def get_prefetch_queryset(self, instances, queryset=None):
+            if queryset is None:
+                queryset = super(GenericRelatedObjectManager, self).get_queryset()
+
+            queryset._add_hints(instance=instances[0])
+            queryset = queryset.using(queryset._db or self._db)
+
+            query = {
+                '%s__pk' % self.content_type_field_name: self.content_type.id,
+                '%s__in' % self.object_id_field_name: set(obj._get_pk_val() for obj in instances)
+            }
+
+            # We (possibly) need to convert object IDs to the type of the
+            # instances' PK in order to match up instances:
+            object_id_converter = instances[0]._meta.pk.to_python
+            return (queryset.filter(**query),
+                    lambda relobj: object_id_converter(getattr(relobj, self.object_id_field_name)),
+                    lambda obj: obj._get_pk_val(),
+                    False,
+                    self.prefetch_cache_name)
+
+        def add(self, *objs):
+            for obj in objs:
+                if not isinstance(obj, self.model):
+                    raise TypeError("'%s' instance expected" % self.model._meta.object_name)
+                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, **kwargs):
+            if not objs:
+                return
+            bulk = kwargs.pop('bulk', True)
+            self._clear(self.filter(pk__in=[o.pk for o in objs]), bulk)
+        remove.alters_data = True
+
+        def clear(self, **kwargs):
+            bulk = kwargs.pop('bulk', True)
+            self._clear(self, bulk)
+        clear.alters_data = True
+
+        def _clear(self, queryset, bulk):
+            db = router.db_for_write(self.model, instance=self.instance)
+            queryset = queryset.using(db)
+            if bulk:
+                queryset.delete()
+            else:
+                with transaction.commit_on_success_unless_managed(using=db, savepoint=False):
+                    for obj in queryset:
+                        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
+            db = router.db_for_write(self.model, instance=self.instance)
+            return super(GenericRelatedObjectManager, self).using(db).create(**kwargs)
+        create.alters_data = True
+
+    return GenericRelatedObjectManager
+
+
+class GenericRel(ForeignObjectRel):
+    def __init__(self, field, to, related_name=None, limit_choices_to=None):
+        super(GenericRel, self).__init__(field, to, related_name, limit_choices_to)

+ 88 - 0
django/contrib/contenttypes/forms.py

@@ -0,0 +1,88 @@
+from __future__ import unicode_literals
+
+from django.db import models
+from django.forms import ModelForm, modelformset_factory
+from django.forms.models import BaseModelFormSet
+from django.contrib.contenttypes.models import ContentType
+
+
+class BaseGenericInlineFormSet(BaseModelFormSet):
+    """
+    A formset for generic inline objects to a parent.
+    """
+
+    def __init__(self, data=None, files=None, instance=None, save_as_new=None,
+                 prefix=None, queryset=None, **kwargs):
+        opts = self.model._meta
+        self.instance = instance
+        self.rel_name = '-'.join((
+            opts.app_label, opts.model_name,
+            self.ct_field.name, self.ct_fk_field.name,
+        ))
+        if self.instance is None or self.instance.pk is None:
+            qs = self.model._default_manager.none()
+        else:
+            if queryset is None:
+                queryset = self.model._default_manager
+            qs = queryset.filter(**{
+                self.ct_field.name: ContentType.objects.get_for_model(
+                    self.instance, for_concrete_model=self.for_concrete_model),
+                self.ct_fk_field.name: self.instance.pk,
+            })
+        super(BaseGenericInlineFormSet, self).__init__(
+            queryset=qs, data=data, files=files,
+            prefix=prefix,
+            **kwargs
+        )
+
+    @classmethod
+    def get_default_prefix(cls):
+        opts = cls.model._meta
+        return '-'.join(
+            (opts.app_label, opts.model_name,
+            cls.ct_field.name, cls.ct_fk_field.name)
+        )
+
+    def save_new(self, form, commit=True):
+        setattr(form.instance, self.ct_field.get_attname(),
+            ContentType.objects.get_for_model(self.instance).pk)
+        setattr(form.instance, self.ct_fk_field.get_attname(),
+            self.instance.pk)
+        return form.save(commit=commit)
+
+
+def generic_inlineformset_factory(model, form=ModelForm,
+                                  formset=BaseGenericInlineFormSet,
+                                  ct_field="content_type", fk_field="object_id",
+                                  fields=None, exclude=None,
+                                  extra=3, can_order=False, can_delete=True,
+                                  max_num=None,
+                                  formfield_callback=None, validate_max=False,
+                                  for_concrete_model=True):
+    """
+    Returns a ``GenericInlineFormSet`` for the given kwargs.
+
+    You must provide ``ct_field`` and ``fk_field`` if they are different from
+    the defaults ``content_type`` and ``object_id`` respectively.
+    """
+    opts = model._meta
+    # if there is no field called `ct_field` let the exception propagate
+    ct_field = opts.get_field(ct_field)
+    if not isinstance(ct_field, models.ForeignKey) or ct_field.rel.to != ContentType:
+        raise Exception("fk_name '%s' is not a ForeignKey to ContentType" % ct_field)
+    fk_field = opts.get_field(fk_field)  # let the exception propagate
+    if exclude is not None:
+        exclude = list(exclude)
+        exclude.extend([ct_field.name, fk_field.name])
+    else:
+        exclude = [ct_field.name, fk_field.name]
+    FormSet = modelformset_factory(model, form=form,
+                                   formfield_callback=formfield_callback,
+                                   formset=formset,
+                                   extra=extra, can_delete=can_delete, can_order=can_order,
+                                   fields=fields, exclude=exclude, max_num=max_num,
+                                   validate_max=validate_max)
+    FormSet.ct_field = ct_field
+    FormSet.ct_fk_field = fk_field
+    FormSet.for_concrete_model = for_concrete_model
+    return FormSet

+ 15 - 711
django/contrib/contenttypes/generic.py

@@ -1,716 +1,20 @@
-"""
-Classes allowing "generic" relations through ContentType and object-id fields.
-"""
 from __future__ import unicode_literals
 
-from collections import defaultdict
-from functools import partial
+import warnings
 
-from django.core import checks
-from django.core.exceptions import ObjectDoesNotExist
-from django.db import connection
-from django.db import models, router, transaction, DEFAULT_DB_ALIAS
-from django.db.models import signals, FieldDoesNotExist
-from django.db.models.base import ModelBase
-from django.db.models.fields.related import ForeignObject, ForeignObjectRel
-from django.db.models.related import PathInfo
-from django.db.models.sql.datastructures import Col
-from django.forms import ModelForm, ALL_FIELDS
-from django.forms.models import (BaseModelFormSet, modelformset_factory,
-    modelform_defines_fields)
-from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets
-from django.contrib.contenttypes.models import ContentType
-from django.utils import six
-from django.utils.deprecation import RenameMethodsBase
-from django.utils.encoding import smart_text, python_2_unicode_compatible
 
+warnings.warn(
+    ('django.contrib.contenttypes.generic is deprecated and will be removed in '
+     'Django 1.9. Its contents have been moved to the fields, forms, and admin '
+     'submodules of django.contrib.contenttypes.'), PendingDeprecationWarning, stacklevel=2
+)
 
-class RenameGenericForeignKeyMethods(RenameMethodsBase):
-    renamed_methods = (
-        ('get_prefetch_query_set', 'get_prefetch_queryset', DeprecationWarning),
-    )
-
-
-@python_2_unicode_compatible
-class GenericForeignKey(six.with_metaclass(RenameGenericForeignKeyMethods)):
-    """
-    Provides a generic relation to any object through content-type/object-id
-    fields.
-    """
-
-    def __init__(self, ct_field="content_type", fk_field="object_id", for_concrete_model=True):
-        self.ct_field = ct_field
-        self.fk_field = fk_field
-        self.for_concrete_model = for_concrete_model
-        self.editable = False
-
-    def contribute_to_class(self, cls, name):
-        self.name = name
-        self.model = cls
-        self.cache_attr = "_%s_cache" % name
-        cls._meta.add_virtual_field(self)
-
-        # Only run pre-initialization field assignment on non-abstract models
-        if not cls._meta.abstract:
-            signals.pre_init.connect(self.instance_pre_init, sender=cls)
-
-        setattr(cls, name, self)
-
-    def __str__(self):
-        model = self.model
-        app = model._meta.app_label
-        return '%s.%s.%s' % (app, model._meta.object_name, self.name)
-
-    def check(self, **kwargs):
-        errors = []
-        errors.extend(self._check_content_type_field())
-        errors.extend(self._check_object_id_field())
-        errors.extend(self._check_field_name())
-        return errors
-
-    def _check_content_type_field(self):
-        return _check_content_type_field(
-            model=self.model,
-            field_name=self.ct_field,
-            checked_object=self)
-
-    def _check_object_id_field(self):
-        try:
-            self.model._meta.get_field(self.fk_field)
-        except FieldDoesNotExist:
-            return [
-                checks.Error(
-                    'The field refers to "%s" field which is missing.' % self.fk_field,
-                    hint=None,
-                    obj=self,
-                    id='contenttypes.E001',
-                )
-            ]
-        else:
-            return []
-
-    def _check_field_name(self):
-        if self.name.endswith("_"):
-            return [
-                checks.Error(
-                    'Field names must not end with underscores.',
-                    hint=None,
-                    obj=self,
-                    id='contenttypes.E002',
-                )
-            ]
-        else:
-            return []
-
-    def instance_pre_init(self, signal, sender, args, kwargs, **_kwargs):
-        """
-        Handles initializing an object with the generic FK instead of
-        content-type/object-id fields.
-        """
-        if self.name in kwargs:
-            value = kwargs.pop(self.name)
-            if value is not None:
-                kwargs[self.ct_field] = self.get_content_type(obj=value)
-                kwargs[self.fk_field] = value._get_pk_val()
-            else:
-                kwargs[self.ct_field] = None
-                kwargs[self.fk_field] = None
-
-    def get_content_type(self, obj=None, id=None, using=None):
-        if obj is not None:
-            return ContentType.objects.db_manager(obj._state.db).get_for_model(
-                obj, for_concrete_model=self.for_concrete_model)
-        elif id is not None:
-            return ContentType.objects.db_manager(using).get_for_id(id)
-        else:
-            # This should never happen. I love comments like this, don't you?
-            raise Exception("Impossible arguments to GFK.get_content_type!")
-
-    def get_prefetch_queryset(self, instances, queryset=None):
-        if queryset is not None:
-            raise ValueError("Custom queryset can't be used for this lookup.")
-
-        # For efficiency, group the instances by content type and then do one
-        # query per model
-        fk_dict = defaultdict(set)
-        # We need one instance for each group in order to get the right db:
-        instance_dict = {}
-        ct_attname = self.model._meta.get_field(self.ct_field).get_attname()
-        for instance in instances:
-            # We avoid looking for values if either ct_id or fkey value is None
-            ct_id = getattr(instance, ct_attname)
-            if ct_id is not None:
-                fk_val = getattr(instance, self.fk_field)
-                if fk_val is not None:
-                    fk_dict[ct_id].add(fk_val)
-                    instance_dict[ct_id] = instance
-
-        ret_val = []
-        for ct_id, fkeys in fk_dict.items():
-            instance = instance_dict[ct_id]
-            ct = self.get_content_type(id=ct_id, using=instance._state.db)
-            ret_val.extend(ct.get_all_objects_for_this_type(pk__in=fkeys))
-
-        # For doing the join in Python, we have to match both the FK val and the
-        # content type, so we use a callable that returns a (fk, class) pair.
-        def gfk_key(obj):
-            ct_id = getattr(obj, ct_attname)
-            if ct_id is None:
-                return None
-            else:
-                model = self.get_content_type(id=ct_id,
-                                              using=obj._state.db).model_class()
-                return (model._meta.pk.get_prep_value(getattr(obj, self.fk_field)),
-                        model)
-
-        return (ret_val,
-                lambda obj: (obj._get_pk_val(), obj.__class__),
-                gfk_key,
-                True,
-                self.cache_attr)
-
-    def is_cached(self, instance):
-        return hasattr(instance, self.cache_attr)
-
-    def __get__(self, instance, instance_type=None):
-        if instance is None:
-            return self
-
-        try:
-            return getattr(instance, self.cache_attr)
-        except AttributeError:
-            rel_obj = None
-
-            # Make sure to use ContentType.objects.get_for_id() to ensure that
-            # lookups are cached (see ticket #5570). This takes more code than
-            # the naive ``getattr(instance, self.ct_field)``, but has better
-            # performance when dealing with GFKs in loops and such.
-            f = self.model._meta.get_field(self.ct_field)
-            ct_id = getattr(instance, f.get_attname(), None)
-            if ct_id is not None:
-                ct = self.get_content_type(id=ct_id, using=instance._state.db)
-                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):
-        ct = None
-        fk = None
-        if value is not None:
-            ct = self.get_content_type(obj=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(ForeignObject):
-    """Provides an accessor to generic related objects (e.g. comments)"""
-
-    def __init__(self, to, **kwargs):
-        kwargs['verbose_name'] = kwargs.get('verbose_name', None)
-        kwargs['rel'] = GenericRel(
-            self, to, related_name=kwargs.pop('related_name', None),
-            limit_choices_to=kwargs.pop('limit_choices_to', None),)
-        # 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")
-
-        self.for_concrete_model = kwargs.pop("for_concrete_model", True)
-
-        kwargs['blank'] = True
-        kwargs['editable'] = False
-        kwargs['serialize'] = False
-        # This construct is somewhat of an abuse of ForeignObject. This field
-        # represents a relation from pk to object_id field. But, this relation
-        # isn't direct, the join is generated reverse along foreign key. So,
-        # the from_field is object_id field, to_field is pk because of the
-        # reverse join.
-        super(GenericRelation, self).__init__(
-            to, to_fields=[],
-            from_fields=[self.object_id_field_name], **kwargs)
-
-    def check(self, **kwargs):
-        errors = super(GenericRelation, self).check(**kwargs)
-        errors.extend(self._check_content_type_field())
-        errors.extend(self._check_object_id_field())
-        errors.extend(self._check_generic_foreign_key_existence())
-        return errors
-
-    def _check_content_type_field(self):
-        target = self.rel.to
-        if isinstance(target, ModelBase):
-            return _check_content_type_field(
-                model=target,
-                field_name=self.content_type_field_name,
-                checked_object=self)
-        else:
-            return []
-
-    def _check_object_id_field(self):
-        target = self.rel.to
-        if isinstance(target, ModelBase):
-            opts = target._meta
-            try:
-                opts.get_field(self.object_id_field_name)
-            except FieldDoesNotExist:
-                return [
-                    checks.Error(
-                        'The field refers to %s.%s field which is missing.' % (
-                            opts.object_name, self.object_id_field_name
-                        ),
-                        hint=None,
-                        obj=self,
-                        id='contenttypes.E003',
-                    )
-                ]
-            else:
-                return []
-        else:
-            return []
-
-    def _check_generic_foreign_key_existence(self):
-        target = self.rel.to
-        if isinstance(target, ModelBase):
-            # Using `vars` is very ugly approach, but there is no better one,
-            # because GenericForeignKeys are not considered as fields and,
-            # therefore, are not included in `target._meta.local_fields`.
-            fields = target._meta.virtual_fields
-            if any(isinstance(field, GenericForeignKey) and
-                    field.ct_field == self.content_type_field_name and
-                    field.fk_field == self.object_id_field_name
-                    for field in fields):
-                return []
-            else:
-                return [
-                    checks.Warning(
-                        ('The field defines a generic relation with the model '
-                         '%s.%s, but the model lacks GenericForeignKey.') % (
-                            target._meta.app_label, target._meta.object_name
-                        ),
-                        hint=None,
-                        obj=self,
-                        id='contenttypes.E004',
-                    )
-                ]
-        else:
-            return []
-
-    def resolve_related_fields(self):
-        self.to_fields = [self.model._meta.pk.name]
-        return [(self.rel.to._meta.get_field_by_name(self.object_id_field_name)[0],
-                 self.model._meta.pk)]
-
-    def get_reverse_path_info(self):
-        opts = self.rel.to._meta
-        target = opts.get_field_by_name(self.object_id_field_name)[0]
-        return [PathInfo(self.model._meta, opts, (target,), self.rel, True, False)]
-
-    def get_choices_default(self):
-        return super(GenericRelation, self).get_choices(include_blank=False)
-
-    def value_to_string(self, obj):
-        qs = getattr(obj, self.name).all()
-        return smart_text([instance._get_pk_val() for instance in qs])
-
-    def get_joining_columns(self, reverse_join=False):
-        if not reverse_join:
-            # This error message is meant for the user, and from user
-            # perspective this is a reverse join along the GenericRelation.
-            raise ValueError('Joining in reverse direction not allowed.')
-        return super(GenericRelation, self).get_joining_columns(reverse_join)
-
-    def contribute_to_class(self, cls, name):
-        super(GenericRelation, self).contribute_to_class(cls, name, virtual_only=True)
-        # Save a reference to which model this class is on for future use
-        self.model = cls
-        # Add the descriptor for the relation
-        setattr(cls, self.name, ReverseGenericRelatedObjectsDescriptor(self, self.for_concrete_model))
-
-    def contribute_to_related_class(self, cls, related):
-        pass
-
-    def set_attributes_from_rel(self):
-        pass
-
-    def get_internal_type(self):
-        return "ManyToManyField"
-
-    def get_content_type(self):
-        """
-        Returns the content type associated with this field's model.
-        """
-        return ContentType.objects.get_for_model(self.model,
-                                                 for_concrete_model=self.for_concrete_model)
-
-    def get_extra_restriction(self, where_class, alias, remote_alias):
-        field = self.rel.to._meta.get_field_by_name(self.content_type_field_name)[0]
-        contenttype_pk = self.get_content_type().pk
-        cond = where_class()
-        lookup = field.get_lookup('exact')(Col(remote_alias, field, field), contenttype_pk)
-        cond.add(lookup, 'AND')
-        return cond
-
-    def bulk_related_objects(self, objs, using=DEFAULT_DB_ALIAS):
-        """
-        Return all objects related to ``objs`` via this ``GenericRelation``.
-
-        """
-        return self.rel.to._base_manager.db_manager(using).filter(**{
-            "%s__pk" % self.content_type_field_name: ContentType.objects.db_manager(using).get_for_model(
-                self.model, for_concrete_model=self.for_concrete_model).pk,
-            "%s__in" % self.object_id_field_name: [obj.pk for obj in objs]
-        })
-
-
-def _check_content_type_field(model, field_name, checked_object):
-    """ Check if field named `field_name` in model `model` exists and is
-    valid content_type field (is a ForeignKey to ContentType). """
-
-    try:
-        field = model._meta.get_field(field_name)
-    except FieldDoesNotExist:
-        return [
-            checks.Error(
-                'The field refers to %s.%s field which is missing.' % (
-                    model._meta.object_name, field_name
-                ),
-                hint=None,
-                obj=checked_object,
-                id='contenttypes.E005',
-            )
-        ]
-    else:
-        if not isinstance(field, models.ForeignKey):
-            return [
-                checks.Error(
-                    ('"%s" field is used by a %s '
-                     'as content type field and therefore it must be '
-                     'a ForeignKey.') % (
-                        field_name, checked_object.__class__.__name__
-                    ),
-                    hint=None,
-                    obj=checked_object,
-                    id='contenttypes.E006',
-                )
-            ]
-        elif field.rel.to != ContentType:
-            return [
-                checks.Error(
-                    ('"%s" field is used by a %s '
-                     'as content type field and therefore it must be '
-                     'a ForeignKey to ContentType.') % (
-                        field_name, checked_object.__class__.__name__
-                    ),
-                    hint=None,
-                    obj=checked_object,
-                    id='contenttypes.E007',
-                )
-            ]
-        else:
-            return []
-
-
-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, for_concrete_model=True):
-        self.field = field
-        self.for_concrete_model = for_concrete_model
-
-    def __get__(self, instance, instance_type=None):
-        if instance is None:
-            return self
-
-        # 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)
-
-        qn = connection.ops.quote_name
-        content_type = ContentType.objects.db_manager(instance._state.db).get_for_model(
-            instance, for_concrete_model=self.for_concrete_model)
-
-        join_cols = self.field.get_joining_columns(reverse_join=True)[0]
-        manager = RelatedManager(
-            model=rel_model,
-            instance=instance,
-            source_col_name=qn(join_cols[0]),
-            target_col_name=qn(join_cols[1]),
-            content_type=content_type,
-            content_type_field_name=self.field.content_type_field_name,
-            object_id_field_name=self.field.object_id_field_name,
-            prefetch_cache_name=self.field.attname,
-        )
-
-        return manager
-
-    def __set__(self, instance, value):
-        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, instance=None, symmetrical=None,
-                     source_col_name=None, target_col_name=None, content_type=None,
-                     content_type_field_name=None, object_id_field_name=None,
-                     prefetch_cache_name=None):
-
-            super(GenericRelatedObjectManager, self).__init__()
-            self.model = model
-            self.content_type = content_type
-            self.symmetrical = symmetrical
-            self.instance = instance
-            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.prefetch_cache_name = prefetch_cache_name
-            self.pk_val = self.instance._get_pk_val()
-            self.core_filters = {
-                '%s__pk' % content_type_field_name: content_type.id,
-                '%s' % object_id_field_name: instance._get_pk_val(),
-            }
-
-        def __call__(self, **kwargs):
-            # We use **kwargs rather than a kwarg argument to enforce the
-            # `manager='manager_name'` syntax.
-            manager = getattr(self.model, kwargs.pop('manager'))
-            manager_class = create_generic_related_manager(manager.__class__)
-            return manager_class(
-                model=self.model,
-                instance=self.instance,
-                symmetrical=self.symmetrical,
-                source_col_name=self.source_col_name,
-                target_col_name=self.target_col_name,
-                content_type=self.content_type,
-                content_type_field_name=self.content_type_field_name,
-                object_id_field_name=self.object_id_field_name,
-                prefetch_cache_name=self.prefetch_cache_name,
-            )
-        do_not_call_in_templates = True
-
-        def get_queryset(self):
-            try:
-                return self.instance._prefetched_objects_cache[self.prefetch_cache_name]
-            except (AttributeError, KeyError):
-                db = self._db or router.db_for_read(self.model, instance=self.instance)
-                return super(GenericRelatedObjectManager, self).get_queryset().using(db).filter(**self.core_filters)
-
-        def get_prefetch_queryset(self, instances, queryset=None):
-            if queryset is None:
-                queryset = super(GenericRelatedObjectManager, self).get_queryset()
-
-            queryset._add_hints(instance=instances[0])
-            queryset = queryset.using(queryset._db or self._db)
-
-            query = {
-                '%s__pk' % self.content_type_field_name: self.content_type.id,
-                '%s__in' % self.object_id_field_name: set(obj._get_pk_val() for obj in instances)
-            }
-
-            # We (possibly) need to convert object IDs to the type of the
-            # instances' PK in order to match up instances:
-            object_id_converter = instances[0]._meta.pk.to_python
-            return (queryset.filter(**query),
-                    lambda relobj: object_id_converter(getattr(relobj, self.object_id_field_name)),
-                    lambda obj: obj._get_pk_val(),
-                    False,
-                    self.prefetch_cache_name)
-
-        def add(self, *objs):
-            for obj in objs:
-                if not isinstance(obj, self.model):
-                    raise TypeError("'%s' instance expected" % self.model._meta.object_name)
-                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, **kwargs):
-            if not objs:
-                return
-            bulk = kwargs.pop('bulk', True)
-            self._clear(self.filter(pk__in=[o.pk for o in objs]), bulk)
-        remove.alters_data = True
-
-        def clear(self, **kwargs):
-            bulk = kwargs.pop('bulk', True)
-            self._clear(self, bulk)
-        clear.alters_data = True
-
-        def _clear(self, queryset, bulk):
-            db = router.db_for_write(self.model, instance=self.instance)
-            queryset = queryset.using(db)
-            if bulk:
-                queryset.delete()
-            else:
-                with transaction.commit_on_success_unless_managed(using=db, savepoint=False):
-                    for obj in queryset:
-                        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
-            db = router.db_for_write(self.model, instance=self.instance)
-            return super(GenericRelatedObjectManager, self).using(db).create(**kwargs)
-        create.alters_data = True
-
-    return GenericRelatedObjectManager
-
-
-class GenericRel(ForeignObjectRel):
-    def __init__(self, field, to, related_name=None, limit_choices_to=None):
-        super(GenericRel, self).__init__(field, to, related_name, limit_choices_to)
-
-
-class BaseGenericInlineFormSet(BaseModelFormSet):
-    """
-    A formset for generic inline objects to a parent.
-    """
-
-    def __init__(self, data=None, files=None, instance=None, save_as_new=None,
-                 prefix=None, queryset=None, **kwargs):
-        opts = self.model._meta
-        self.instance = instance
-        self.rel_name = '-'.join((
-            opts.app_label, opts.model_name,
-            self.ct_field.name, self.ct_fk_field.name,
-        ))
-        if self.instance is None or self.instance.pk is None:
-            qs = self.model._default_manager.none()
-        else:
-            if queryset is None:
-                queryset = self.model._default_manager
-            qs = queryset.filter(**{
-                self.ct_field.name: ContentType.objects.get_for_model(
-                    self.instance, for_concrete_model=self.for_concrete_model),
-                self.ct_fk_field.name: self.instance.pk,
-            })
-        super(BaseGenericInlineFormSet, self).__init__(
-            queryset=qs, data=data, files=files,
-            prefix=prefix,
-            **kwargs
-        )
-
-    @classmethod
-    def get_default_prefix(cls):
-        opts = cls.model._meta
-        return '-'.join(
-            (opts.app_label, opts.model_name,
-            cls.ct_field.name, cls.ct_fk_field.name)
-        )
-
-    def save_new(self, form, commit=True):
-        setattr(form.instance, self.ct_field.get_attname(),
-            ContentType.objects.get_for_model(self.instance).pk)
-        setattr(form.instance, self.ct_fk_field.get_attname(),
-            self.instance.pk)
-        return form.save(commit=commit)
-
-
-def generic_inlineformset_factory(model, form=ModelForm,
-                                  formset=BaseGenericInlineFormSet,
-                                  ct_field="content_type", fk_field="object_id",
-                                  fields=None, exclude=None,
-                                  extra=3, can_order=False, can_delete=True,
-                                  max_num=None,
-                                  formfield_callback=None, validate_max=False,
-                                  for_concrete_model=True):
-    """
-    Returns a ``GenericInlineFormSet`` for the given kwargs.
-
-    You must provide ``ct_field`` and ``fk_field`` if they are different from
-    the defaults ``content_type`` and ``object_id`` respectively.
-    """
-    opts = model._meta
-    # if there is no field called `ct_field` let the exception propagate
-    ct_field = opts.get_field(ct_field)
-    if not isinstance(ct_field, models.ForeignKey) or ct_field.rel.to != ContentType:
-        raise Exception("fk_name '%s' is not a ForeignKey to ContentType" % ct_field)
-    fk_field = opts.get_field(fk_field)  # let the exception propagate
-    if exclude is not None:
-        exclude = list(exclude)
-        exclude.extend([ct_field.name, fk_field.name])
-    else:
-        exclude = [ct_field.name, fk_field.name]
-    FormSet = modelformset_factory(model, form=form,
-                                   formfield_callback=formfield_callback,
-                                   formset=formset,
-                                   extra=extra, can_delete=can_delete, can_order=can_order,
-                                   fields=fields, exclude=exclude, max_num=max_num,
-                                   validate_max=validate_max)
-    FormSet.ct_field = ct_field
-    FormSet.ct_fk_field = fk_field
-    FormSet.for_concrete_model = for_concrete_model
-    return FormSet
-
-
-class GenericInlineModelAdmin(InlineModelAdmin):
-    ct_field = "content_type"
-    ct_fk_field = "object_id"
-    formset = BaseGenericInlineFormSet
-
-    def get_formset(self, request, obj=None, **kwargs):
-        if 'fields' in kwargs:
-            fields = kwargs.pop('fields')
-        else:
-            fields = flatten_fieldsets(self.get_fieldsets(request, obj))
-        if self.exclude is None:
-            exclude = []
-        else:
-            exclude = list(self.exclude)
-        exclude.extend(self.get_readonly_fields(request, obj))
-        if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude:
-            # Take the custom ModelForm's Meta.exclude into account only if the
-            # GenericInlineModelAdmin doesn't define its own.
-            exclude.extend(self.form._meta.exclude)
-        exclude = exclude or None
-        can_delete = self.can_delete and self.has_delete_permission(request, obj)
-        defaults = {
-            "ct_field": self.ct_field,
-            "fk_field": self.ct_fk_field,
-            "form": self.form,
-            "formfield_callback": partial(self.formfield_for_dbfield, request=request),
-            "formset": self.formset,
-            "extra": self.extra,
-            "can_delete": can_delete,
-            "can_order": False,
-            "fields": fields,
-            "max_num": self.max_num,
-            "exclude": exclude
-        }
-        defaults.update(kwargs)
-
-        if defaults['fields'] is None and not modelform_defines_fields(defaults['form']):
-            defaults['fields'] = ALL_FIELDS
-
-        return generic_inlineformset_factory(self.model, **defaults)
-
-
-class GenericStackedInline(GenericInlineModelAdmin):
-    template = 'admin/edit_inline/stacked.html'
-
-
-class GenericTabularInline(GenericInlineModelAdmin):
-    template = 'admin/edit_inline/tabular.html'
+from django.contrib.contenttypes.admin import (  # NOQA
+    GenericInlineModelAdmin, GenericStackedInline, GenericTabularInline
+)
+from django.contrib.contenttypes.fields import (  # NOQA
+    GenericForeignKey, GenericRelation
+)
+from django.contrib.contenttypes.forms import (  # NOQA
+    BaseGenericInlineFormSet, generic_inlineformset_factory
+)

+ 8 - 6
docs/ref/contrib/admin/index.txt

@@ -2187,30 +2187,32 @@ It is possible to use an inline with generically related objects. Let's say
 you have the following models::
 
     from django.db import models
+    from django.contrib.contenttypes.fields import GenericForeignKey
 
     class Image(models.Model):
         image = models.ImageField(upload_to="images")
         content_type = models.ForeignKey(ContentType)
         object_id = models.PositiveIntegerField()
-        content_object = generic.GenericForeignKey("content_type", "object_id")
+        content_object = GenericForeignKey("content_type", "object_id")
 
     class Product(models.Model):
         name = models.CharField(max_length=100)
 
 If you want to allow editing and creating ``Image`` instance on the ``Product``
-add/change views you can use ``GenericTabularInline`` or
-``GenericStackedInline`` (both subclasses of ``GenericInlineModelAdmin``)
-provided by ``django.contrib.contenttypes.generic``, they implement tabular and
+add/change views you can use :class:`~django.contrib.contenttypes.admin.GenericTabularInline`
+or :class:`~django.contrib.contenttypes.admin.GenericStackedInline` (both
+subclasses of :class:`~django.contrib.contenttypes.admin.GenericInlineModelAdmin`)
+provided by :mod:`~django.contrib.contenttypes.admin`, they implement tabular and
 stacked visual layouts for the forms representing the inline objects
 respectively just like their non-generic counterparts and behave just like any
 other inline. In your ``admin.py`` for this example app::
 
     from django.contrib import admin
-    from django.contrib.contenttypes import generic
+    from django.contrib.contenttypes.admin import GenericTabularInline
 
     from myproject.myapp.models import Image, Product
 
-    class ImageInline(generic.GenericTabularInline):
+    class ImageInline(GenericTabularInline):
         model = Image
 
     class ProductAdmin(admin.ModelAdmin):

+ 2 - 2
docs/ref/contrib/comments/models.txt

@@ -23,12 +23,12 @@ The built-in comment models
 
     .. attribute:: content_object
 
-        A :class:`~django.contrib.contenttypes.generic.GenericForeignKey`
+        A :class:`~django.contrib.contenttypes.fields.GenericForeignKey`
         attribute pointing to the object the comment is attached to. You can use
         this to get at the related object (i.e. ``my_comment.content_object``).
 
         Since this field is a
-        :class:`~django.contrib.contenttypes.generic.GenericForeignKey`, it's
+        :class:`~django.contrib.contenttypes.fields.GenericForeignKey`, it's
         actually syntactic sugar on top of two underlying attributes, described
         below.
 

+ 86 - 50
docs/ref/contrib/contenttypes.txt

@@ -232,7 +232,7 @@ lookup::
     >>> user_type
     <ContentType: user>
 
-.. module:: django.contrib.contenttypes.generic
+.. module:: django.contrib.contenttypes.fields
 
 .. _generic-relations:
 
@@ -250,14 +250,14 @@ generic (sometimes called "polymorphic") relationships between models.
 A simple example is a tagging system, which might look like this::
 
     from django.db import models
+    from django.contrib.contenttypes.fields import GenericForeignKey
     from django.contrib.contenttypes.models import ContentType
-    from django.contrib.contenttypes import generic
 
     class TaggedItem(models.Model):
         tag = models.SlugField()
         content_type = models.ForeignKey(ContentType)
         object_id = models.PositiveIntegerField()
-        content_object = generic.GenericForeignKey('content_type', 'object_id')
+        content_object = GenericForeignKey('content_type', 'object_id')
 
         # On Python 3: def __str__(self):
         def __unicode__(self):
@@ -274,7 +274,7 @@ model:
 .. class:: GenericForeignKey
 
     There are three parts to setting up a
-    :class:`~django.contrib.contenttypes.generic.GenericForeignKey`:
+    :class:`~django.contrib.contenttypes.fields.GenericForeignKey`:
 
     1. Give your model a :class:`~django.db.models.ForeignKey`
        to :class:`~django.contrib.contenttypes.models.ContentType`. The usual
@@ -286,11 +286,11 @@ model:
        for this field is "object_id".
 
     3. Give your model a
-       :class:`~django.contrib.contenttypes.generic.GenericForeignKey`, and
+       :class:`~django.contrib.contenttypes.fields.GenericForeignKey`, and
        pass it the names of the two fields described above. If these fields
        are named "content_type" and "object_id", you can omit this -- those
        are the default field names
-       :class:`~django.contrib.contenttypes.generic.GenericForeignKey` will
+       :class:`~django.contrib.contenttypes.fields.GenericForeignKey` will
        look for.
 
     .. attribute:: GenericForeignKey.for_concrete_model
@@ -301,6 +301,10 @@ model:
        is ``True``. This mirrors the ``for_concrete_model`` argument to
        :meth:`~django.contrib.contenttypes.models.ContentTypeManager.get_for_model`.
 
+    .. versionchanged:: 1.7
+
+        This class used to be defined in ``django.contrib.contenttypes.generic``.
+
 
 .. admonition:: Primary key type compatibility
 
@@ -347,10 +351,10 @@ creating a ``TaggedItem``::
     >>> t.content_object
     <User: Guido>
 
-Due to the way :class:`~django.contrib.contenttypes.generic.GenericForeignKey`
+Due to the way :class:`~django.contrib.contenttypes.fields.GenericForeignKey`
 is implemented, you cannot use such fields directly with filters (``filter()``
 and ``exclude()``, for example) via the database API. Because a
-:class:`~django.contrib.contenttypes.generic.GenericForeignKey` isn't a
+:class:`~django.contrib.contenttypes.fields.GenericForeignKey` isn't a
 normal field object, these examples will *not* work::
 
     # This will fail
@@ -358,7 +362,7 @@ normal field object, these examples will *not* work::
     # This will also fail
     >>> TaggedItem.objects.get(content_object=guido)
 
-Likewise, :class:`~django.contrib.contenttypes.generic.GenericForeignKey`\s
+Likewise, :class:`~django.contrib.contenttypes.fields.GenericForeignKey`\s
 does not appear in :class:`~django.forms.ModelForm`\s.
 
 Reverse generic relations
@@ -366,12 +370,16 @@ Reverse generic relations
 
 .. class:: GenericRelation
 
+    .. versionchanged:: 1.7
+
+        This class used to be defined in ``django.contrib.contenttypes.generic``.
+
 If you know which models you'll be using most often, you can also add
 a "reverse" generic relationship to enable an additional API. For example::
 
     class Bookmark(models.Model):
         url = models.URLField()
-        tags = generic.GenericRelation(TaggedItem)
+        tags = GenericRelation(TaggedItem)
 
 ``Bookmark`` instances will each have a ``tags`` attribute, which can
 be used to retrieve their associated ``TaggedItems``::
@@ -385,10 +393,10 @@ be used to retrieve their associated ``TaggedItems``::
     >>> b.tags.all()
     [<TaggedItem: django>, <TaggedItem: python>]
 
-Just as :class:`~django.contrib.contenttypes.generic.GenericForeignKey`
+Just as :class:`~django.contrib.contenttypes.fields.GenericForeignKey`
 accepts the names of the content-type and object-ID fields as
 arguments, so too does
-:class:`~django.contrib.contenttypes.generic.GenericRelation`;
+:class:`~django.contrib.contenttypes.fields.GenericRelation`;
 if the model which has the generic foreign key is using non-default names
 for those fields, you must pass the names of the fields when setting up a
 :class:`.GenericRelation` to it. For example, if the ``TaggedItem`` model
@@ -396,9 +404,9 @@ referred to above used fields named ``content_type_fk`` and
 ``object_primary_key`` to create its generic foreign key, then a
 :class:`.GenericRelation` back to it would need to be defined like so::
 
-    tags = generic.GenericRelation(TaggedItem,
-                                   content_type_field='content_type_fk',
-                                   object_id_field='object_primary_key')
+    tags = GenericRelation(TaggedItem,
+                           content_type_field='content_type_fk',
+                           object_id_field='object_primary_key')
 
 Of course, if you don't add the reverse relationship, you can do the
 same types of lookups manually::
@@ -410,29 +418,29 @@ same types of lookups manually::
     [<TaggedItem: django>, <TaggedItem: python>]
 
 Note that if the model in a
-:class:`~django.contrib.contenttypes.generic.GenericRelation` uses a
+:class:`~django.contrib.contenttypes.fields.GenericRelation` uses a
 non-default value for ``ct_field`` or ``fk_field`` in its
-:class:`~django.contrib.contenttypes.generic.GenericForeignKey` (e.g. the
+:class:`~django.contrib.contenttypes.fields.GenericForeignKey` (e.g. the
 :mod:`django.contrib.comments` app uses ``ct_field="object_pk"``),
 you'll need to set ``content_type_field`` and/or ``object_id_field`` in
-the :class:`~django.contrib.contenttypes.generic.GenericRelation` to
+the :class:`~django.contrib.contenttypes.fields.GenericRelation` to
 match the ``ct_field`` and ``fk_field``, respectively, in the
-:class:`~django.contrib.contenttypes.generic.GenericForeignKey`::
+:class:`~django.contrib.contenttypes.fields.GenericForeignKey`::
 
-    comments = generic.GenericRelation(Comment, object_id_field="object_pk")
+    comments = fields.GenericRelation(Comment, object_id_field="object_pk")
 
 Note also, that if you delete an object that has a
-:class:`~django.contrib.contenttypes.generic.GenericRelation`, any objects
-which have a :class:`~django.contrib.contenttypes.generic.GenericForeignKey`
+:class:`~django.contrib.contenttypes.fields.GenericRelation`, any objects
+which have a :class:`~django.contrib.contenttypes.fields.GenericForeignKey`
 pointing at it will be deleted as well. In the example above, this means that
 if a ``Bookmark`` object were deleted, any ``TaggedItem`` objects pointing at
 it would be deleted at the same time.
 
 Unlike :class:`~django.db.models.ForeignKey`,
-:class:`~django.contrib.contenttypes.generic.GenericForeignKey` does not accept
+:class:`~django.contrib.contenttypes.fields.GenericForeignKey` does not accept
 an :attr:`~django.db.models.ForeignKey.on_delete` argument to customize this
 behavior; if desired, you can avoid the cascade-deletion simply by not using
-:class:`~django.contrib.contenttypes.generic.GenericRelation`, and alternate
+:class:`~django.contrib.contenttypes.fields.GenericRelation`, and alternate
 behavior can be provided via the :data:`~django.db.models.signals.pre_delete`
 signal.
 
@@ -441,7 +449,7 @@ Generic relations and aggregation
 
 :doc:`Django's database aggregation API </topics/db/aggregation>`
 doesn't work with a
-:class:`~django.contrib.contenttypes.generic.GenericRelation`. For example, you
+:class:`~django.contrib.contenttypes.fields.GenericRelation`. For example, you
 might be tempted to try something like::
 
     Bookmark.objects.aggregate(Count('tags'))
@@ -452,18 +460,55 @@ to the queryset to ensure the correct content type, but the
 into account. For now, if you need aggregates on generic relations, you'll
 need to calculate them without using the aggregation API.
 
-Generic relations in forms and admin
-------------------------------------
 
-The :mod:`django.contrib.contenttypes.generic` module provides:
+.. module:: django.contrib.contenttypes.forms
+
+Generic relation in forms
+-------------------------
 
-* ``BaseGenericInlineFormSet``
-* :class:`~django.contrib.contenttypes.generic.GenericTabularInline`
-  and :class:`~django.contrib.contenttypes.generic.GenericStackedInline`
-  (subclasses of
-  :class:`~django.contrib.contenttypes.generic.GenericInlineModelAdmin`)
+The :mod:`django.contrib.contenttypes.forms` module provides:
+
+* :class:`BaseGenericInlineFormSet`
 * A formset factory, :func:`generic_inlineformset_factory`, for use with
-  :class:`GenericForeignKey`
+  :class:`~django.contrib.contenttypes.fields.GenericForeignKey`.
+
+.. class:: BaseGenericInlineFormSet
+
+    .. versionchanged:: 1.7
+
+        This class used to be defined in ``django.contrib.contenttypes.generic``.
+
+.. function:: generic_inlineformset_factory(model, form=ModelForm, formset=BaseGenericInlineFormSet, ct_field="content_type", fk_field="object_id", fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, validate_max=False, for_concrete_model=True)
+
+    Returns a ``GenericInlineFormSet`` using
+    :func:`~django.forms.models.modelformset_factory`.
+
+    You must provide ``ct_field`` and ``fk_field`` if they are different from
+    the defaults, ``content_type`` and ``object_id`` respectively. Other
+    parameters are similar to those documented in
+    :func:`~django.forms.models.modelformset_factory` and
+    :func:`~django.forms.models.inlineformset_factory`.
+
+    .. versionadded:: 1.6
+
+        The ``for_concrete_model`` argument corresponds to the
+        :class:`~django.contrib.contenttypes.fields.GenericForeignKey.for_concrete_model`
+        argument on ``GenericForeignKey``.
+
+    .. versionchanged:: 1.7
+
+        This function used to be defined in ``django.contrib.contenttypes.generic``.
+
+
+.. module:: django.contrib.contenttypes.admin
+
+Generic relations in admin
+------------------------------------
+
+The :mod:`django.contrib.contenttypes.admin` module provides
+:class:`~django.contrib.contenttypes.admin.GenericTabularInline` and
+:class:`~django.contrib.contenttypes.admin.GenericStackedInline` (subclasses of
+:class:`~django.contrib.contenttypes.admin.GenericInlineModelAdmin`)
 
 These classes and functions enable the use of generic relations in forms
 and the admin. See the :doc:`model formset </topics/forms/modelforms>` and
@@ -472,7 +517,7 @@ information.
 
 .. class:: GenericInlineModelAdmin
 
-    The :class:`~django.contrib.contenttypes.generic.GenericInlineModelAdmin`
+    The :class:`~django.contrib.contenttypes.admin.GenericInlineModelAdmin`
     class inherits all properties from an
     :class:`~django.contrib.admin.InlineModelAdmin` class. However,
     it adds a couple of its own for working with the generic relation:
@@ -488,25 +533,16 @@ information.
         The name of the integer field that represents the ID of the related
         object. Defaults to ``object_id``.
 
+    .. versionchanged:: 1.7
+
+        This class used to be defined in ``django.contrib.contenttypes.generic``.
+
 .. class:: GenericTabularInline
 .. class:: GenericStackedInline
 
     Subclasses of :class:`GenericInlineModelAdmin` with stacked and tabular
     layouts, respectively.
 
-.. function:: generic_inlineformset_factory(model, form=ModelForm, formset=BaseGenericInlineFormSet, ct_field="content_type", fk_field="object_id", fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, validate_max=False, for_concrete_model=True)
+    .. versionchanged:: 1.7
 
-    Returns a ``GenericInlineFormSet`` using
-    :func:`~django.forms.models.modelformset_factory`.
-
-    You must provide ``ct_field`` and ``fk_field`` if they are different from
-    the defaults, ``content_type`` and ``object_id`` respectively. Other
-    parameters are similar to those documented in
-    :func:`~django.forms.models.modelformset_factory` and
-    :func:`~django.forms.models.inlineformset_factory`.
-
-    .. versionadded:: 1.6
-
-        The ``for_concrete_model`` argument corresponds to the
-        :class:`~django.contrib.contenttypes.generic.GenericForeignKey.for_concrete_model`
-        argument on ``GenericForeignKey``.
+        These classes used to be defined in ``django.contrib.contenttypes.generic``.

+ 2 - 2
docs/ref/models/querysets.txt

@@ -798,8 +798,8 @@ relationship, and does the 'joining' in Python. This allows it to prefetch
 many-to-many and many-to-one objects, which cannot be done using
 ``select_related``, in addition to the foreign key and one-to-one relationships
 that are supported by ``select_related``. It also supports prefetching of
-:class:`~django.contrib.contenttypes.generic.GenericRelation` and
-:class:`~django.contrib.contenttypes.generic.GenericForeignKey`.
+:class:`~django.contrib.contenttypes.fields.GenericRelation` and
+:class:`~django.contrib.contenttypes.fields.GenericForeignKey`.
 
 For example, suppose you have these models::
 

+ 1 - 2
docs/releases/1.4-alpha-1.txt

@@ -92,8 +92,7 @@ different strategy and broader scope,
 ``QuerySet`` that will prefetch each of the specified related lookups in a
 single batch as soon as the query begins to be evaluated. Unlike
 ``select_related``, it does the joins in Python, not in the database, and
-supports many-to-many relationships,
-:class:`~django.contrib.contenttypes.generic.GenericForeignKey` and more. This
+supports many-to-many relationships, ``GenericForeignKey`` and more. This
 allows you to fix a very common performance problem in which your code ends up
 doing O(n) database queries (or worse) if objects on your primary ``QuerySet``
 each have many related objects that you also need.

+ 1 - 2
docs/releases/1.4-beta-1.txt

@@ -108,8 +108,7 @@ different strategy and broader scope,
 ``QuerySet`` that will prefetch each of the specified related lookups in a
 single batch as soon as the query begins to be evaluated. Unlike
 ``select_related``, it does the joins in Python, not in the database, and
-supports many-to-many relationships,
-:class:`~django.contrib.contenttypes.generic.GenericForeignKey` and more. This
+supports many-to-many relationships, ``GenericForeignKey`` and more. This
 allows you to fix a very common performance problem in which your code ends up
 doing O(n) database queries (or worse) if objects on your primary ``QuerySet``
 each have many related objects that you also need.

+ 1 - 2
docs/releases/1.4.txt

@@ -257,8 +257,7 @@ different strategy and broader scope,
 ``QuerySet`` that will prefetch each of the specified related lookups in a
 single batch as soon as the query begins to be evaluated. Unlike
 ``select_related``, it does the joins in Python, not in the database, and
-supports many-to-many relationships,
-:class:`~django.contrib.contenttypes.generic.GenericForeignKey` and more. This
+supports many-to-many relationships, ``GenericForeignKey`` and more. This
 allows you to fix a very common performance problem in which your code ends up
 doing O(n) database queries (or worse) if objects on your primary ``QuerySet``
 each have many related objects that you also need to fetch.

+ 3 - 5
docs/releases/1.6.txt

@@ -302,11 +302,9 @@ Minor features
   :class:`~django.views.generic.base.RedirectView` now support HTTP ``PATCH``
   method.
 
-* :class:`GenericForeignKey <django.contrib.contenttypes.generic.GenericForeignKey>`
-  now takes an optional
-  :attr:`~django.contrib.contenttypes.generic.GenericForeignKey.for_concrete_model`
-  argument, which when set to ``False`` allows the field to reference proxy
-  models. The default is ``True`` to retain the old behavior.
+* ``GenericForeignKey`` now takes an optional ``for_concrete_model`` argument,
+  which when set to ``False`` allows the field to reference proxy models. The
+  default is ``True`` to retain the old behavior.
 
 * The :class:`~django.middleware.locale.LocaleMiddleware` now stores the active
   language in session if it is not present there. This prevents loss of

+ 19 - 0
docs/releases/1.7.txt

@@ -1030,6 +1030,25 @@ API, it will go through a regular deprecation path. This attribute was mostly
 used by methods that bypassed ``ModelAdmin.get_fieldsets()`` but this was
 considered a bug and has been addressed.
 
+Reorganization of ``django.contrib.contenttypes``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Since ``django.contrib.contenttypes.generic`` defined both admin and model
+related objects an import of this module could trigger unexpected side effects.
+As a consequence, its contents were split into :mod:`~django.contrib.contenttypes`
+submodules and the ``django.contrib.contenttypes.generic`` module is deprecated:
+
+* :class:`~django.contrib.contenttypes.fields.GenericForeignKey` and
+  :class:`~django.contrib.contenttypes.fields.GenericRelation` now live in
+  :mod:`~django.contrib.contenttypes.fields`.
+* :class:`~django.contrib.contenttypes.forms.BaseGenericInlineFormSet` and
+  :func:`~django.contrib.contenttypes.forms.generic_inlineformset_factory` now
+  live in :mod:`~django.contrib.contenttypes.forms`.
+* :class:`~django.contrib.contenttypes.admin.GenericInlineModelAdmin`,
+  :class:`~django.contrib.contenttypes.admin.GenericStackedInline` and
+  :class:`~django.contrib.contenttypes.admin.GenericTabularInline` now live in
+  :mod:`~django.contrib.contenttypes.admin`.
+
 ``syncdb``
 ~~~~~~~~~~
 

+ 2 - 2
tests/admin_inlines/models.py

@@ -6,8 +6,8 @@ from __future__ import unicode_literals
 import random
 
 from django.db import models
+from django.contrib.contenttypes.fields import GenericForeignKey
 from django.contrib.contenttypes.models import ContentType
-from django.contrib.contenttypes import generic
 from django.utils.encoding import python_2_unicode_compatible
 
 
@@ -34,7 +34,7 @@ class Child(models.Model):
 
     content_type = models.ForeignKey(ContentType)
     object_id = models.PositiveIntegerField()
-    parent = generic.GenericForeignKey()
+    parent = GenericForeignKey()
 
     def __str__(self):
         return 'I am %s, a child of %s' % (self.name, self.parent)

+ 5 - 3
tests/admin_views/models.py

@@ -6,7 +6,9 @@ import tempfile
 import os
 
 from django.contrib.auth.models import User
-from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.fields import (
+    GenericForeignKey, GenericRelation
+)
 from django.contrib.contenttypes.models import ContentType
 from django.core.files.storage import FileSystemStorage
 from django.db import models
@@ -430,7 +432,7 @@ class FunkyTag(models.Model):
     name = models.CharField(max_length=25)
     content_type = models.ForeignKey(ContentType)
     object_id = models.PositiveIntegerField()
-    content_object = generic.GenericForeignKey('content_type', 'object_id')
+    content_object = GenericForeignKey('content_type', 'object_id')
 
     def __str__(self):
         return self.name
@@ -441,7 +443,7 @@ class Plot(models.Model):
     name = models.CharField(max_length=100)
     team_leader = models.ForeignKey(Villain, related_name='lead_plots')
     contact = models.ForeignKey(Villain, related_name='contact_plots')
-    tags = generic.GenericRelation(FunkyTag)
+    tags = GenericRelation(FunkyTag)
 
     def __str__(self):
         return self.name

+ 5 - 3
tests/aggregation_regress/models.py

@@ -1,5 +1,7 @@
 # coding: utf-8
-from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.fields import (
+    GenericForeignKey, GenericRelation
+)
 from django.contrib.contenttypes.models import ContentType
 from django.db import models
 from django.utils.encoding import python_2_unicode_compatible
@@ -28,7 +30,7 @@ class ItemTag(models.Model):
     tag = models.CharField(max_length=100)
     content_type = models.ForeignKey(ContentType)
     object_id = models.PositiveIntegerField()
-    content_object = generic.GenericForeignKey('content_type', 'object_id')
+    content_object = GenericForeignKey('content_type', 'object_id')
 
 
 @python_2_unicode_compatible
@@ -42,7 +44,7 @@ class Book(models.Model):
     contact = models.ForeignKey(Author, related_name='book_contact_set')
     publisher = models.ForeignKey(Publisher)
     pubdate = models.DateField()
-    tags = generic.GenericRelation(ItemTag)
+    tags = GenericRelation(ItemTag)
 
     class Meta:
         ordering = ('name',)

+ 5 - 3
tests/backends/models.py

@@ -1,6 +1,8 @@
 from __future__ import unicode_literals
 
-from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.fields import (
+    GenericForeignKey, GenericRelation
+)
 from django.contrib.contenttypes.models import ContentType
 from django.db import models, connection
 from django.utils.encoding import python_2_unicode_compatible
@@ -46,13 +48,13 @@ class Tag(models.Model):
     name = models.CharField(max_length=30)
     content_type = models.ForeignKey(ContentType, related_name='backend_tags')
     object_id = models.PositiveIntegerField()
-    content_object = generic.GenericForeignKey('content_type', 'object_id')
+    content_object = GenericForeignKey('content_type', 'object_id')
 
 
 class Post(models.Model):
     name = models.CharField(max_length=30)
     text = models.TextField()
-    tags = generic.GenericRelation('Tag')
+    tags = GenericRelation('Tag')
 
     class Meta:
         db_table = 'CaseSensitive_Post'

+ 26 - 24
tests/contenttypes_tests/tests.py

@@ -2,7 +2,9 @@
 from __future__ import absolute_import, unicode_literals
 
 from django.apps.registry import Apps, apps
-from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.fields import (
+    GenericForeignKey, GenericRelation
+)
 from django.contrib.contenttypes.models import ContentType
 from django.core import checks
 from django.db import models
@@ -90,7 +92,7 @@ class GenericForeignKeyTests(IsolatedModelsTestCase):
 
     def test_str(self):
         class Model(models.Model):
-            field = generic.GenericForeignKey()
+            field = GenericForeignKey()
         expected = "contenttypes_tests.Model.field"
         actual = force_str(Model.field)
         self.assertEqual(expected, actual)
@@ -99,7 +101,7 @@ class GenericForeignKeyTests(IsolatedModelsTestCase):
         class TaggedItem(models.Model):
             # no content_type field
             object_id = models.PositiveIntegerField()
-            content_object = generic.GenericForeignKey()
+            content_object = GenericForeignKey()
 
         errors = TaggedItem.content_object.check()
         expected = [
@@ -116,7 +118,7 @@ class GenericForeignKeyTests(IsolatedModelsTestCase):
         class Model(models.Model):
             content_type = models.IntegerField()  # should be ForeignKey
             object_id = models.PositiveIntegerField()
-            content_object = generic.GenericForeignKey(
+            content_object = GenericForeignKey(
                 'content_type', 'object_id')
 
         errors = Model.content_object.check()
@@ -136,7 +138,7 @@ class GenericForeignKeyTests(IsolatedModelsTestCase):
         class Model(models.Model):
             content_type = models.ForeignKey('self')  # should point to ContentType
             object_id = models.PositiveIntegerField()
-            content_object = generic.GenericForeignKey(
+            content_object = GenericForeignKey(
                 'content_type', 'object_id')
 
         errors = Model.content_object.check()
@@ -156,7 +158,7 @@ class GenericForeignKeyTests(IsolatedModelsTestCase):
         class TaggedItem(models.Model):
             content_type = models.ForeignKey(ContentType)
             # missing object_id field
-            content_object = generic.GenericForeignKey()
+            content_object = GenericForeignKey()
 
         errors = TaggedItem.content_object.check()
         expected = [
@@ -173,7 +175,7 @@ class GenericForeignKeyTests(IsolatedModelsTestCase):
         class Model(models.Model):
             content_type = models.ForeignKey(ContentType)
             object_id = models.PositiveIntegerField()
-            content_object_ = generic.GenericForeignKey(
+            content_object_ = GenericForeignKey(
                 'content_type', 'object_id')
 
         errors = Model.content_object_.check()
@@ -188,7 +190,7 @@ class GenericForeignKeyTests(IsolatedModelsTestCase):
         self.assertEqual(errors, expected)
 
     def test_generic_foreign_key_checks_are_performed(self):
-        class MyGenericForeignKey(generic.GenericForeignKey):
+        class MyGenericForeignKey(GenericForeignKey):
             def check(self, **kwargs):
                 return ['performed!']
 
@@ -205,10 +207,10 @@ class GenericRelationshipTests(IsolatedModelsTestCase):
         class TaggedItem(models.Model):
             content_type = models.ForeignKey(ContentType)
             object_id = models.PositiveIntegerField()
-            content_object = generic.GenericForeignKey()
+            content_object = GenericForeignKey()
 
         class Bookmark(models.Model):
-            tags = generic.GenericRelation('TaggedItem')
+            tags = GenericRelation('TaggedItem')
 
         errors = Bookmark.tags.field.check()
         self.assertEqual(errors, [])
@@ -217,11 +219,11 @@ class GenericRelationshipTests(IsolatedModelsTestCase):
         class TaggedItem(models.Model):
             custom_content_type = models.ForeignKey(ContentType)
             custom_object_id = models.PositiveIntegerField()
-            content_object = generic.GenericForeignKey(
+            content_object = GenericForeignKey(
                 'custom_content_type', 'custom_object_id')
 
         class Bookmark(models.Model):
-            tags = generic.GenericRelation('TaggedItem',
+            tags = GenericRelation('TaggedItem',
                 content_type_field='custom_content_type',
                 object_id_field='custom_object_id')
 
@@ -230,7 +232,7 @@ class GenericRelationshipTests(IsolatedModelsTestCase):
 
     def test_pointing_to_missing_model(self):
         class Model(models.Model):
-            rel = generic.GenericRelation('MissingModel')
+            rel = GenericRelation('MissingModel')
 
         errors = Model.rel.field.check()
         expected = [
@@ -248,10 +250,10 @@ class GenericRelationshipTests(IsolatedModelsTestCase):
 
     def test_valid_self_referential_generic_relationship(self):
         class Model(models.Model):
-            rel = generic.GenericRelation('Model')
+            rel = GenericRelation('Model')
             content_type = models.ForeignKey(ContentType)
             object_id = models.PositiveIntegerField()
-            content_object = generic.GenericForeignKey(
+            content_object = GenericForeignKey(
                 'content_type', 'object_id')
 
         errors = Model.rel.field.check()
@@ -261,10 +263,10 @@ class GenericRelationshipTests(IsolatedModelsTestCase):
         class TaggedItem(models.Model):
             # no content_type field
             object_id = models.PositiveIntegerField()
-            content_object = generic.GenericForeignKey()
+            content_object = GenericForeignKey()
 
         class Bookmark(models.Model):
-            tags = generic.GenericRelation('TaggedItem')
+            tags = GenericRelation('TaggedItem')
 
         errors = Bookmark.tags.field.check()
         expected = [
@@ -281,10 +283,10 @@ class GenericRelationshipTests(IsolatedModelsTestCase):
         class TaggedItem(models.Model):
             content_type = models.ForeignKey(ContentType)
             # missing object_id field
-            content_object = generic.GenericForeignKey()
+            content_object = GenericForeignKey()
 
         class Bookmark(models.Model):
-            tags = generic.GenericRelation('TaggedItem')
+            tags = GenericRelation('TaggedItem')
 
         errors = Bookmark.tags.field.check()
         expected = [
@@ -303,7 +305,7 @@ class GenericRelationshipTests(IsolatedModelsTestCase):
             object_id = models.PositiveIntegerField()
 
         class Bookmark(models.Model):
-            tags = generic.GenericRelation('TaggedItem')
+            tags = GenericRelation('TaggedItem')
 
         errors = Bookmark.tags.field.check()
         expected = [
@@ -326,13 +328,13 @@ class GenericRelationshipTests(IsolatedModelsTestCase):
         class SwappedModel(models.Model):
             content_type = models.ForeignKey(ContentType)
             object_id = models.PositiveIntegerField()
-            content_object = generic.GenericForeignKey()
+            content_object = GenericForeignKey()
 
             class Meta:
                 swappable = 'TEST_SWAPPED_MODEL'
 
         class Model(models.Model):
-            rel = generic.GenericRelation('SwappedModel')
+            rel = GenericRelation('SwappedModel')
 
         errors = Model.rel.field.check()
         expected = [
@@ -351,10 +353,10 @@ class GenericRelationshipTests(IsolatedModelsTestCase):
         class TaggedItem(models.Model):
             content_type = models.ForeignKey(ContentType)
             object_id = models.PositiveIntegerField()
-            content_object = generic.GenericForeignKey()
+            content_object = GenericForeignKey()
 
         class InvalidBookmark(models.Model):
-            tags_ = generic.GenericRelation('TaggedItem')
+            tags_ = GenericRelation('TaggedItem')
 
         errors = InvalidBookmark.tags_.field.check()
         expected = [

+ 7 - 5
tests/custom_managers/models.py

@@ -11,7 +11,9 @@ returns.
 
 from __future__ import unicode_literals
 
-from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.fields import (
+    GenericForeignKey, GenericRelation
+)
 from django.db import models
 from django.utils.encoding import python_2_unicode_compatible
 
@@ -88,7 +90,7 @@ class Person(models.Model):
     favorite_book = models.ForeignKey('Book', null=True, related_name='favorite_books')
     favorite_thing_type = models.ForeignKey('contenttypes.ContentType', null=True)
     favorite_thing_id = models.IntegerField(null=True)
-    favorite_thing = generic.GenericForeignKey('favorite_thing_type', 'favorite_thing_id')
+    favorite_thing = GenericForeignKey('favorite_thing_type', 'favorite_thing_id')
 
     objects = PersonManager()
     fun_people = FunPeopleManager()
@@ -110,7 +112,7 @@ class FunPerson(models.Model):
     favorite_book = models.ForeignKey('Book', null=True, related_name='fun_people_favorite_books')
     favorite_thing_type = models.ForeignKey('contenttypes.ContentType', null=True)
     favorite_thing_id = models.IntegerField(null=True)
-    favorite_thing = generic.GenericForeignKey('favorite_thing_type', 'favorite_thing_id')
+    favorite_thing = GenericForeignKey('favorite_thing_type', 'favorite_thing_id')
 
     objects = FunPeopleManager()
 
@@ -127,10 +129,10 @@ class Book(models.Model):
     authors = models.ManyToManyField(Person, related_name='books')
     fun_authors = models.ManyToManyField(FunPerson, related_name='books')
 
-    favorite_things = generic.GenericRelation(Person,
+    favorite_things = GenericRelation(Person,
         content_type_field='favorite_thing_type', object_id_field='favorite_thing_id')
 
-    fun_people_favorite_things = generic.GenericRelation(FunPerson,
+    fun_people_favorite_things = GenericRelation(FunPerson,
         content_type_field='favorite_thing_type', object_id_field='favorite_thing_id')
 
     def __str__(self):

+ 5 - 3
tests/delete_regress/models.py

@@ -1,4 +1,6 @@
-from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.fields import (
+    GenericForeignKey, GenericRelation
+)
 from django.contrib.contenttypes.models import ContentType
 from django.db import models
 
@@ -7,7 +9,7 @@ class Award(models.Model):
     name = models.CharField(max_length=25)
     object_id = models.PositiveIntegerField()
     content_type = models.ForeignKey(ContentType)
-    content_object = generic.GenericForeignKey()
+    content_object = GenericForeignKey()
 
 
 class AwardNote(models.Model):
@@ -17,7 +19,7 @@ class AwardNote(models.Model):
 
 class Person(models.Model):
     name = models.CharField(max_length=25)
-    awards = generic.GenericRelation(Award)
+    awards = GenericRelation(Award)
 
 
 class Book(models.Model):

+ 2 - 3
tests/fixtures/models.py

@@ -9,7 +9,7 @@ in the application directory, or in one of the directories named in the
 """
 
 from django.contrib.auth.models import Permission
-from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.fields import GenericForeignKey
 from django.contrib.contenttypes.models import ContentType
 from django.db import models
 from django.utils.encoding import python_2_unicode_compatible
@@ -55,8 +55,7 @@ class Tag(models.Model):
     name = models.CharField(max_length=100)
     tagged_type = models.ForeignKey(ContentType, related_name="fixtures_tag_set")
     tagged_id = models.PositiveIntegerField(default=0)
-    tagged = generic.GenericForeignKey(ct_field='tagged_type',
-                                       fk_field='tagged_id')
+    tagged = GenericForeignKey(ct_field='tagged_type', fk_field='tagged_id')
 
     def __str__(self):
         return '<%s: %s> tagged "%s"' % (self.tagged.__class__.__name__,

+ 6 - 6
tests/generic_inline_admin/admin.py

@@ -1,5 +1,5 @@
 from django.contrib import admin
-from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.admin import GenericTabularInline
 
 from .models import (Media, PhoneNumber, Episode, EpisodeExtra, Contact,
     Category, EpisodePermanent, EpisodeMaxNum)
@@ -8,7 +8,7 @@ from .models import (Media, PhoneNumber, Episode, EpisodeExtra, Contact,
 site = admin.AdminSite(name="admin")
 
 
-class MediaInline(generic.GenericTabularInline):
+class MediaInline(GenericTabularInline):
     model = Media
 
 
@@ -18,22 +18,22 @@ class EpisodeAdmin(admin.ModelAdmin):
     ]
 
 
-class MediaExtraInline(generic.GenericTabularInline):
+class MediaExtraInline(GenericTabularInline):
     model = Media
     extra = 0
 
 
-class MediaMaxNumInline(generic.GenericTabularInline):
+class MediaMaxNumInline(GenericTabularInline):
     model = Media
     extra = 5
     max_num = 2
 
 
-class PhoneNumberInline(generic.GenericTabularInline):
+class PhoneNumberInline(GenericTabularInline):
     model = PhoneNumber
 
 
-class MediaPermanentInline(generic.GenericTabularInline):
+class MediaPermanentInline(GenericTabularInline):
     model = Media
     can_delete = False
 

+ 6 - 4
tests/generic_inline_admin/models.py

@@ -1,4 +1,6 @@
-from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.fields import (
+    GenericForeignKey, GenericRelation
+)
 from django.contrib.contenttypes.models import ContentType
 from django.db import models
 from django.utils.encoding import python_2_unicode_compatible
@@ -17,7 +19,7 @@ class Media(models.Model):
     """
     content_type = models.ForeignKey(ContentType)
     object_id = models.PositiveIntegerField()
-    content_object = generic.GenericForeignKey()
+    content_object = GenericForeignKey()
     url = models.URLField()
     description = models.CharField(max_length=100, blank=True)
     keywords = models.CharField(max_length=100, blank=True)
@@ -56,7 +58,7 @@ class Category(models.Model):
 class PhoneNumber(models.Model):
     content_type = models.ForeignKey(ContentType)
     object_id = models.PositiveIntegerField()
-    content_object = generic.GenericForeignKey('content_type', 'object_id')
+    content_object = GenericForeignKey('content_type', 'object_id')
     phone_number = models.CharField(max_length=30)
     category = models.ForeignKey(Category, null=True, blank=True)
 
@@ -66,7 +68,7 @@ class PhoneNumber(models.Model):
 
 class Contact(models.Model):
     name = models.CharField(max_length=50)
-    phone_numbers = generic.GenericRelation(PhoneNumber)
+    phone_numbers = GenericRelation(PhoneNumber)
 
 
 #

+ 2 - 2
tests/generic_inline_admin/tests.py

@@ -4,8 +4,8 @@ import warnings
 
 from django.contrib import admin
 from django.contrib.admin.sites import AdminSite
-from django.contrib.contenttypes.generic import (
-    generic_inlineformset_factory, GenericTabularInline)
+from django.contrib.contenttypes.admin import GenericTabularInline
+from django.contrib.contenttypes.forms import generic_inlineformset_factory
 from django.forms.formsets import DEFAULT_MAX_NUM
 from django.forms.models import ModelForm
 from django.test import TestCase, override_settings

+ 17 - 15
tests/generic_relations/models.py

@@ -11,7 +11,9 @@ from complete).
 
 from __future__ import unicode_literals
 
-from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.fields import (
+    GenericForeignKey, GenericRelation
+)
 from django.contrib.contenttypes.models import ContentType
 from django.db import models
 from django.utils.encoding import python_2_unicode_compatible
@@ -24,7 +26,7 @@ class TaggedItem(models.Model):
     content_type = models.ForeignKey(ContentType)
     object_id = models.PositiveIntegerField()
 
-    content_object = generic.GenericForeignKey()
+    content_object = GenericForeignKey()
 
     class Meta:
         ordering = ["tag", "content_type__name"]
@@ -43,7 +45,7 @@ class AbstractComparison(models.Model):
     content_type1 = models.ForeignKey(ContentType, related_name="comparative1_set")
     object_id1 = models.PositiveIntegerField()
 
-    first_obj = generic.GenericForeignKey(ct_field="content_type1", fk_field="object_id1")
+    first_obj = GenericForeignKey(ct_field="content_type1", fk_field="object_id1")
 
 
 @python_2_unicode_compatible
@@ -55,7 +57,7 @@ class Comparison(AbstractComparison):
     content_type2 = models.ForeignKey(ContentType, related_name="comparative2_set")
     object_id2 = models.PositiveIntegerField()
 
-    other_obj = generic.GenericForeignKey(ct_field="content_type2", fk_field="object_id2")
+    other_obj = GenericForeignKey(ct_field="content_type2", fk_field="object_id2")
 
     def __str__(self):
         return "%s is %s than %s" % (self.first_obj, self.comparative, self.other_obj)
@@ -66,10 +68,10 @@ class Animal(models.Model):
     common_name = models.CharField(max_length=150)
     latin_name = models.CharField(max_length=150)
 
-    tags = generic.GenericRelation(TaggedItem)
-    comparisons = generic.GenericRelation(Comparison,
-                                          object_id_field="object_id1",
-                                          content_type_field="content_type1")
+    tags = GenericRelation(TaggedItem)
+    comparisons = GenericRelation(Comparison,
+                                  object_id_field="object_id1",
+                                  content_type_field="content_type1")
 
     def __str__(self):
         return self.common_name
@@ -80,7 +82,7 @@ class Vegetable(models.Model):
     name = models.CharField(max_length=150)
     is_yucky = models.BooleanField(default=True)
 
-    tags = generic.GenericRelation(TaggedItem)
+    tags = GenericRelation(TaggedItem)
 
     def __str__(self):
         return self.name
@@ -109,29 +111,29 @@ class Gecko(models.Model):
 
 # To test fix for #11263
 class Rock(Mineral):
-    tags = generic.GenericRelation(TaggedItem)
+    tags = GenericRelation(TaggedItem)
 
 
 class ManualPK(models.Model):
     id = models.IntegerField(primary_key=True)
-    tags = generic.GenericRelation(TaggedItem)
+    tags = GenericRelation(TaggedItem)
 
 
 class ForProxyModelModel(models.Model):
     content_type = models.ForeignKey(ContentType)
     object_id = models.PositiveIntegerField()
-    obj = generic.GenericForeignKey(for_concrete_model=False)
+    obj = GenericForeignKey(for_concrete_model=False)
     title = models.CharField(max_length=255, null=True)
 
 
 class ForConcreteModelModel(models.Model):
     content_type = models.ForeignKey(ContentType)
     object_id = models.PositiveIntegerField()
-    obj = generic.GenericForeignKey()
+    obj = GenericForeignKey()
 
 
 class ConcreteRelatedModel(models.Model):
-    bases = generic.GenericRelation(ForProxyModelModel, for_concrete_model=False)
+    bases = GenericRelation(ForProxyModelModel, for_concrete_model=False)
 
 
 class ProxyRelatedModel(ConcreteRelatedModel):
@@ -143,4 +145,4 @@ class ProxyRelatedModel(ConcreteRelatedModel):
 class AllowsNullGFK(models.Model):
     content_type = models.ForeignKey(ContentType, null=True)
     object_id = models.PositiveIntegerField(null=True)
-    content_object = generic.GenericForeignKey()
+    content_object = GenericForeignKey()

+ 1 - 1
tests/generic_relations/tests.py

@@ -1,7 +1,7 @@
 from __future__ import unicode_literals
 
 from django import forms
-from django.contrib.contenttypes.generic import generic_inlineformset_factory
+from django.contrib.contenttypes.forms import generic_inlineformset_factory
 from django.contrib.contenttypes.models import ContentType
 from django.test import TestCase
 from django.utils import six

+ 18 - 16
tests/generic_relations_regress/models.py

@@ -1,4 +1,6 @@
-from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.fields import (
+    GenericForeignKey, GenericRelation
+)
 from django.contrib.contenttypes.models import ContentType
 from django.db import models
 from django.utils.encoding import python_2_unicode_compatible
@@ -13,7 +15,7 @@ __all__ = ('Link', 'Place', 'Restaurant', 'Person', 'Address',
 class Link(models.Model):
     content_type = models.ForeignKey(ContentType)
     object_id = models.PositiveIntegerField()
-    content_object = generic.GenericForeignKey()
+    content_object = GenericForeignKey()
 
     def __str__(self):
         return "Link to %s id=%s" % (self.content_type, self.object_id)
@@ -22,7 +24,7 @@ class Link(models.Model):
 @python_2_unicode_compatible
 class Place(models.Model):
     name = models.CharField(max_length=100)
-    links = generic.GenericRelation(Link)
+    links = GenericRelation(Link)
 
     def __str__(self):
         return "Place: %s" % self.name
@@ -42,7 +44,7 @@ class Address(models.Model):
     zipcode = models.CharField(max_length=5)
     content_type = models.ForeignKey(ContentType)
     object_id = models.PositiveIntegerField()
-    content_object = generic.GenericForeignKey()
+    content_object = GenericForeignKey()
 
     def __str__(self):
         return '%s %s, %s %s' % (self.street, self.city, self.state, self.zipcode)
@@ -52,7 +54,7 @@ class Address(models.Model):
 class Person(models.Model):
     account = models.IntegerField(primary_key=True)
     name = models.CharField(max_length=128)
-    addresses = generic.GenericRelation(Address)
+    addresses = GenericRelation(Address)
 
     def __str__(self):
         return self.name
@@ -61,35 +63,35 @@ class Person(models.Model):
 class CharLink(models.Model):
     content_type = models.ForeignKey(ContentType)
     object_id = models.CharField(max_length=100)
-    content_object = generic.GenericForeignKey()
+    content_object = GenericForeignKey()
 
 
 class TextLink(models.Model):
     content_type = models.ForeignKey(ContentType)
     object_id = models.TextField()
-    content_object = generic.GenericForeignKey()
+    content_object = GenericForeignKey()
 
 
 class OddRelation1(models.Model):
     name = models.CharField(max_length=100)
-    clinks = generic.GenericRelation(CharLink)
+    clinks = GenericRelation(CharLink)
 
 
 class OddRelation2(models.Model):
     name = models.CharField(max_length=100)
-    tlinks = generic.GenericRelation(TextLink)
+    tlinks = GenericRelation(TextLink)
 
 
 # models for test_q_object_or:
 class Note(models.Model):
     content_type = models.ForeignKey(ContentType)
     object_id = models.PositiveIntegerField()
-    content_object = generic.GenericForeignKey()
+    content_object = GenericForeignKey()
     note = models.TextField()
 
 
 class Contact(models.Model):
-    notes = generic.GenericRelation(Note)
+    notes = GenericRelation(Note)
 
 
 class Organization(models.Model):
@@ -100,7 +102,7 @@ class Organization(models.Model):
 @python_2_unicode_compatible
 class Company(models.Model):
     name = models.CharField(max_length=100)
-    links = generic.GenericRelation(Link)
+    links = GenericRelation(Link)
 
     def __str__(self):
         return "Company: %s" % self.name
@@ -135,7 +137,7 @@ class Guild(models.Model):
 class Tag(models.Model):
     content_type = models.ForeignKey(ContentType, related_name='g_r_r_tags')
     object_id = models.CharField(max_length=15)
-    content_object = generic.GenericForeignKey()
+    content_object = GenericForeignKey()
     label = models.CharField(max_length=15)
 
 
@@ -143,7 +145,7 @@ class Board(models.Model):
     name = models.CharField(primary_key=True, max_length=15)
 
 
-class SpecialGenericRelation(generic.GenericRelation):
+class SpecialGenericRelation(GenericRelation):
     def __init__(self, *args, **kwargs):
         super(SpecialGenericRelation, self).__init__(*args, **kwargs)
         self.editable = True
@@ -168,11 +170,11 @@ class A(models.Model):
     flag = models.NullBooleanField()
     content_type = models.ForeignKey(ContentType)
     object_id = models.PositiveIntegerField()
-    content_object = generic.GenericForeignKey('content_type', 'object_id')
+    content_object = GenericForeignKey('content_type', 'object_id')
 
 
 class B(models.Model):
-    a = generic.GenericRelation(A)
+    a = GenericRelation(A)
 
     class Meta:
         ordering = ('id',)

+ 6 - 4
tests/managers_regress/models.py

@@ -2,9 +2,11 @@
 Various edge-cases for model managers.
 """
 
-from django.db import models
-from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.fields import (
+    GenericForeignKey, GenericRelation
+)
 from django.contrib.contenttypes.models import ContentType
+from django.db import models
 from django.utils.encoding import python_2_unicode_compatible, force_text
 
 
@@ -126,7 +128,7 @@ class Child7(Parent):
 # RelatedManagers
 @python_2_unicode_compatible
 class RelatedModel(models.Model):
-    test_gfk = generic.GenericRelation('RelationModel', content_type_field='gfk_ctype', object_id_field='gfk_id')
+    test_gfk = GenericRelation('RelationModel', content_type_field='gfk_ctype', object_id_field='gfk_id')
 
     def __str__(self):
         return force_text(self.pk)
@@ -140,7 +142,7 @@ class RelationModel(models.Model):
 
     gfk_ctype = models.ForeignKey(ContentType)
     gfk_id = models.IntegerField()
-    gfk = generic.GenericForeignKey(ct_field='gfk_ctype', fk_field='gfk_id')
+    gfk = GenericForeignKey(ct_field='gfk_ctype', fk_field='gfk_id')
 
     def __str__(self):
         return force_text(self.pk)

+ 5 - 3
tests/multiple_database/models.py

@@ -1,6 +1,8 @@
 from django.contrib.auth.models import User
+from django.contrib.contenttypes.fields import (
+    GenericForeignKey, GenericRelation
+)
 from django.contrib.contenttypes.models import ContentType
-from django.contrib.contenttypes import generic
 from django.db import models
 from django.utils.encoding import python_2_unicode_compatible
 
@@ -10,7 +12,7 @@ class Review(models.Model):
     source = models.CharField(max_length=100)
     content_type = models.ForeignKey(ContentType)
     object_id = models.PositiveIntegerField()
-    content_object = generic.GenericForeignKey()
+    content_object = GenericForeignKey()
 
     def __str__(self):
         return self.source
@@ -57,7 +59,7 @@ class Book(models.Model):
     published = models.DateField()
     authors = models.ManyToManyField(Person)
     editor = models.ForeignKey(Person, null=True, related_name='edited')
-    reviews = generic.GenericRelation(Review)
+    reviews = GenericRelation(Review)
     pages = models.IntegerField(default=100)
 
     def __str__(self):

+ 9 - 7
tests/prefetch_related/models.py

@@ -1,4 +1,6 @@
-from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.fields import (
+    GenericForeignKey, GenericRelation
+)
 from django.contrib.contenttypes.models import ContentType
 from django.db import models
 from django.utils.encoding import python_2_unicode_compatible
@@ -124,15 +126,15 @@ class TaggedItem(models.Model):
     tag = models.SlugField()
     content_type = models.ForeignKey(ContentType, related_name="taggeditem_set2")
     object_id = models.PositiveIntegerField()
-    content_object = generic.GenericForeignKey('content_type', 'object_id')
+    content_object = GenericForeignKey('content_type', 'object_id')
     created_by_ct = models.ForeignKey(ContentType, null=True,
                                       related_name='taggeditem_set3')
     created_by_fkey = models.PositiveIntegerField(null=True)
-    created_by = generic.GenericForeignKey('created_by_ct', 'created_by_fkey',)
+    created_by = GenericForeignKey('created_by_ct', 'created_by_fkey',)
     favorite_ct = models.ForeignKey(ContentType, null=True,
                                     related_name='taggeditem_set4')
     favorite_fkey = models.CharField(max_length=64, null=True)
-    favorite = generic.GenericForeignKey('favorite_ct', 'favorite_fkey')
+    favorite = GenericForeignKey('favorite_ct', 'favorite_fkey')
 
     def __str__(self):
         return self.tag
@@ -143,8 +145,8 @@ class TaggedItem(models.Model):
 
 class Bookmark(models.Model):
     url = models.URLField()
-    tags = generic.GenericRelation(TaggedItem, related_name='bookmarks')
-    favorite_tags = generic.GenericRelation(TaggedItem,
+    tags = GenericRelation(TaggedItem, related_name='bookmarks')
+    favorite_tags = GenericRelation(TaggedItem,
                                     content_type_field='favorite_ct',
                                     object_id_field='favorite_fkey',
                                     related_name='favorite_bookmarks')
@@ -159,7 +161,7 @@ class Comment(models.Model):
     # Content-object field
     content_type = models.ForeignKey(ContentType)
     object_pk = models.TextField()
-    content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_pk")
+    content_object = GenericForeignKey(ct_field="content_type", fk_field="object_pk")
 
     class Meta:
         ordering = ['id']

+ 5 - 3
tests/serializers_regress/models.py

@@ -7,7 +7,9 @@ This class sets up a model for each model field type
 import warnings
 
 from django.db import models
-from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.fields import (
+    GenericForeignKey, GenericRelation
+)
 from django.contrib.contenttypes.models import ContentType
 
 # The following classes are for testing basic data
@@ -109,7 +111,7 @@ class Tag(models.Model):
     content_type = models.ForeignKey(ContentType)
     object_id = models.PositiveIntegerField()
 
-    content_object = generic.GenericForeignKey()
+    content_object = GenericForeignKey()
 
     class Meta:
         ordering = ["data"]
@@ -118,7 +120,7 @@ class Tag(models.Model):
 class GenericData(models.Model):
     data = models.CharField(max_length=30)
 
-    tags = generic.GenericRelation(Tag)
+    tags = GenericRelation(Tag)
 
 # The following test classes are all for validation
 # of related objects; in particular, forward, backward,