Browse Source

Fixed #21127 -- Started deprecation toward requiring on_delete for ForeignKey/OneToOneField

Flavio Curella 9 years ago
parent
commit
c2e70f0265
100 changed files with 1025 additions and 587 deletions
  1. 12 2
      django/contrib/admin/migrations/0001_initial.py
  2. 11 2
      django/contrib/admin/models.py
  3. 6 1
      django/contrib/auth/migrations/0001_initial.py
  4. 5 1
      django/contrib/auth/models.py
  5. 1 0
      django/contrib/contenttypes/fields.py
  6. 6 1
      django/contrib/redirects/migrations/0001_initial.py
  7. 1 1
      django/contrib/redirects/models.py
  8. 4 0
      django/core/management/commands/inspectdb.py
  9. 8 3
      django/db/models/base.py
  10. 59 9
      django/db/models/fields/related.py
  11. 3 0
      docs/internals/deprecation.txt
  12. 2 2
      docs/intro/overview.txt
  13. 1 1
      docs/intro/tutorial02.txt
  14. 6 2
      docs/ref/contrib/admin/admindocs.txt
  15. 6 6
      docs/ref/contrib/admin/index.txt
  16. 1 1
      docs/ref/contrib/contenttypes.txt
  17. 1 1
      docs/ref/contrib/gis/model-api.txt
  18. 3 3
      docs/ref/contrib/sites.txt
  19. 1 1
      docs/ref/models/database-functions.txt
  20. 138 79
      docs/ref/models/fields.txt
  21. 1 1
      docs/ref/models/options.txt
  22. 12 3
      docs/ref/models/querysets.txt
  23. 1 1
      docs/ref/models/relations.txt
  24. 13 0
      docs/releases/1.9.txt
  25. 5 2
      docs/topics/auth/customizing.txt
  26. 1 1
      docs/topics/class-based-views/generic-display.txt
  27. 1 1
      docs/topics/class-based-views/generic-editing.txt
  28. 1 1
      docs/topics/db/examples/many_to_one.txt
  29. 6 2
      docs/topics/db/examples/one_to_one.txt
  30. 1 1
      docs/topics/db/managers.txt
  31. 24 8
      docs/topics/db/models.txt
  32. 3 3
      docs/topics/db/queries.txt
  33. 11 3
      docs/topics/forms/modelforms.txt
  34. 12 4
      docs/topics/i18n/translation.txt
  35. 2 2
      docs/topics/serialization.txt
  36. 7 7
      tests/admin_changelist/models.py
  37. 7 7
      tests/admin_checks/models.py
  38. 2 2
      tests/admin_docs/models.py
  39. 9 3
      tests/admin_filters/models.py
  40. 25 25
      tests/admin_inlines/models.py
  41. 1 1
      tests/admin_ordering/models.py
  42. 1 1
      tests/admin_scripts/app_with_import/models.py
  43. 11 6
      tests/admin_utils/models.py
  44. 75 49
      tests/admin_views/models.py
  45. 14 9
      tests/admin_widgets/models.py
  46. 2 2
      tests/aggregation/models.py
  47. 7 7
      tests/aggregation_regress/models.py
  48. 3 3
      tests/annotations/models.py
  49. 1 1
      tests/app_loading/not_installed/models.py
  50. 3 3
      tests/auth_tests/models/with_foreign_key.py
  51. 9 5
      tests/backends/models.py
  52. 6 2
      tests/basic/models.py
  53. 6 2
      tests/check_framework/tests.py
  54. 1 1
      tests/contenttypes_tests/models.py
  55. 11 11
      tests/contenttypes_tests/tests.py
  56. 7 1
      tests/custom_columns/models.py
  57. 11 6
      tests/custom_managers/models.py
  58. 1 1
      tests/custom_pk/models.py
  59. 1 1
      tests/dates/models.py
  60. 1 1
      tests/datetimes/models.py
  61. 1 1
      tests/db_functions/models.py
  62. 1 1
      tests/defer/models.py
  63. 10 10
      tests/defer_regress/models.py
  64. 25 25
      tests/delete/models.py
  65. 15 15
      tests/delete_regress/models.py
  66. 16 6
      tests/distinct_on_fields/models.py
  67. 2 0
      tests/expressions/models.py
  68. 3 3
      tests/expressions_case/models.py
  69. 2 2
      tests/extra_regress/models.py
  70. 14 14
      tests/field_deconstruction/tests.py
  71. 3 3
      tests/fixtures/models.py
  72. 14 14
      tests/fixtures_regress/models.py
  73. 26 11
      tests/foreign_object/models.py
  74. 2 0
      tests/forms_tests/models.py
  75. 2 2
      tests/forms_tests/tests/tests.py
  76. 3 3
      tests/generic_inline_admin/models.py
  77. 6 6
      tests/generic_relations/models.py
  78. 11 11
      tests/generic_relations_regress/models.py
  79. 7 2
      tests/get_or_create/models.py
  80. 1 1
      tests/gis_tests/geoapp/models.py
  81. 7 2
      tests/gis_tests/gis_migrations/migrations/0001_initial.py
  82. 1 1
      tests/gis_tests/layermap/models.py
  83. 5 5
      tests/gis_tests/relatedapp/models.py
  84. 4 4
      tests/indexes/models.py
  85. 4 4
      tests/inline_formsets/models.py
  86. 4 4
      tests/inspectdb/models.py
  87. 18 8
      tests/inspectdb/tests.py
  88. 2 2
      tests/introspection/models.py
  89. 17 11
      tests/invalid_models_tests/test_models.py
  90. 152 59
      tests/invalid_models_tests/test_relative_fields.py
  91. 3 3
      tests/known_related_objects/models.py
  92. 2 2
      tests/lookup/models.py
  93. 1 1
      tests/m2m_and_m2o/models.py
  94. 2 2
      tests/m2m_intermediary/models.py
  95. 22 17
      tests/m2m_through/models.py
  96. 8 8
      tests/m2m_through_regress/models.py
  97. 4 4
      tests/m2o_recursive/models.py
  98. 2 2
      tests/managers_regress/models.py
  99. 10 10
      tests/many_to_one/models.py
  100. 2 2
      tests/many_to_one/tests.py

+ 12 - 2
django/contrib/admin/migrations/0001_initial.py

@@ -23,8 +23,18 @@ class Migration(migrations.Migration):
                 ('object_repr', models.CharField(max_length=200, verbose_name='object repr')),
                 ('action_flag', models.PositiveSmallIntegerField(verbose_name='action flag')),
                 ('change_message', models.TextField(verbose_name='change message', blank=True)),
-                ('content_type', models.ForeignKey(to_field='id', blank=True, to='contenttypes.ContentType', null=True, verbose_name='content type')),
-                ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name='user')),
+                ('content_type', models.ForeignKey(
+                    to_field='id',
+                    on_delete=models.SET_NULL,
+                    blank=True, null=True,
+                    to='contenttypes.ContentType',
+                    verbose_name='content type',
+                )),
+                ('user', models.ForeignKey(
+                    to=settings.AUTH_USER_MODEL,
+                    on_delete=models.CASCADE,
+                    verbose_name='user',
+                )),
             ],
             options={
                 'ordering': ('-action_time',),

+ 11 - 2
django/contrib/admin/models.py

@@ -27,8 +27,17 @@ class LogEntryManager(models.Manager):
 @python_2_unicode_compatible
 class LogEntry(models.Model):
     action_time = models.DateTimeField(_('action time'), auto_now=True)
-    user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('user'))
-    content_type = models.ForeignKey(ContentType, verbose_name=_('content type'), blank=True, null=True)
+    user = models.ForeignKey(
+        settings.AUTH_USER_MODEL,
+        models.CASCADE,
+        verbose_name=_('user'),
+    )
+    content_type = models.ForeignKey(
+        ContentType,
+        models.SET_NULL,
+        verbose_name=_('content type'),
+        blank=True, null=True,
+    )
     object_id = models.TextField(_('object id'), blank=True, null=True)
     object_repr = models.CharField(_('object repr'), max_length=200)
     action_flag = models.PositiveSmallIntegerField(_('action flag'))

+ 6 - 1
django/contrib/auth/migrations/0001_initial.py

@@ -19,7 +19,12 @@ class Migration(migrations.Migration):
             fields=[
                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                 ('name', models.CharField(max_length=50, verbose_name='name')),
-                ('content_type', models.ForeignKey(to='contenttypes.ContentType', to_field='id', verbose_name='content type')),
+                ('content_type', models.ForeignKey(
+                    to='contenttypes.ContentType',
+                    on_delete=models.CASCADE,
+                    to_field='id',
+                    verbose_name='content type',
+                )),
                 ('codename', models.CharField(max_length=100, verbose_name='codename')),
             ],
             options={

+ 5 - 1
django/contrib/auth/models.py

@@ -59,7 +59,11 @@ class Permission(models.Model):
     created for each Django model.
     """
     name = models.CharField(_('name'), max_length=255)
-    content_type = models.ForeignKey(ContentType, verbose_name=_('content type'))
+    content_type = models.ForeignKey(
+        ContentType,
+        models.CASCADE,
+        verbose_name=_('content type'),
+    )
     codename = models.CharField(_('codename'), max_length=100)
     objects = PermissionManager()
 

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

@@ -303,6 +303,7 @@ class GenericRelation(ForeignObject):
         )
 
         kwargs['blank'] = True
+        kwargs['on_delete'] = models.CASCADE
         kwargs['editable'] = False
         kwargs['serialize'] = False
 

+ 6 - 1
django/contrib/redirects/migrations/0001_initial.py

@@ -15,7 +15,12 @@ class Migration(migrations.Migration):
             name='Redirect',
             fields=[
                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-                ('site', models.ForeignKey(to='sites.Site', to_field='id', verbose_name='site')),
+                ('site', models.ForeignKey(
+                    to='sites.Site',
+                    to_field='id',
+                    on_delete=models.CASCADE,
+                    verbose_name='site',
+                )),
                 ('old_path', models.CharField(help_text="This should be an absolute path, excluding the domain name. Example: '/events/search/'.", max_length=200, verbose_name='redirect from', db_index=True)),
                 ('new_path', models.CharField(help_text="This can be either an absolute path (as above) or a full URL starting with 'http://'.", max_length=200, verbose_name='redirect to', blank=True)),
             ],

+ 1 - 1
django/contrib/redirects/models.py

@@ -6,7 +6,7 @@ from django.utils.translation import ugettext_lazy as _
 
 @python_2_unicode_compatible
 class Redirect(models.Model):
-    site = models.ForeignKey(Site, verbose_name=_('site'))
+    site = models.ForeignKey(Site, models.CASCADE, verbose_name=_('site'))
     old_path = models.CharField(_('redirect from'), max_length=200, db_index=True,
         help_text=_("This should be an absolute path, excluding the domain name. Example: '/events/search/'."))
     new_path = models.CharField(_('redirect to'), max_length=200, blank=True,

+ 4 - 0
django/core/management/commands/inspectdb.py

@@ -40,6 +40,7 @@ class Command(BaseCommand):
             yield "# You'll have to do the following manually to clean this up:"
             yield "#   * Rearrange models' order"
             yield "#   * Make sure each model has one field with primary_key=True"
+            yield "#   * Make sure each ForeignKey has `on_delete` set to the desidered behavior."
             yield (
                 "#   * Remove `managed = False` lines if you wish to allow "
                 "Django to create, modify, and delete the table"
@@ -128,6 +129,9 @@ class Command(BaseCommand):
                         '' if '.' in field_type else 'models.',
                         field_type,
                     )
+                    if field_type.startswith('ForeignKey('):
+                        field_desc += ', models.DO_NOTHING'
+
                     if extra_params:
                         if not field_desc.endswith('('):
                             field_desc += ', '

+ 8 - 3
django/db/models/base.py

@@ -18,7 +18,7 @@ from django.db import (
 )
 from django.db.models import signals
 from django.db.models.constants import LOOKUP_SEP
-from django.db.models.deletion import Collector
+from django.db.models.deletion import CASCADE, Collector
 from django.db.models.fields import AutoField
 from django.db.models.fields.related import (
     ForeignObjectRel, ManyToOneRel, OneToOneField, lazy_related_operation,
@@ -230,8 +230,13 @@ class ModelBase(type):
                     field = parent_links[base_key]
                 elif not is_proxy:
                     attr_name = '%s_ptr' % base._meta.model_name
-                    field = OneToOneField(base, name=attr_name,
-                            auto_created=True, parent_link=True)
+                    field = OneToOneField(
+                        base,
+                        on_delete=CASCADE,
+                        name=attr_name,
+                        auto_created=True,
+                        parent_link=True,
+                    )
                     # Only add the ptr field if it's not already present;
                     # e.g. migrations will already have it specified
                     if not hasattr(new_class, attr_name):

+ 59 - 9
django/db/models/fields/related.py

@@ -30,6 +30,7 @@ from django.utils.deprecation import (
 from django.utils.encoding import force_text, smart_text
 from django.utils.functional import cached_property, curry
 from django.utils.translation import ugettext_lazy as _
+from django.utils.version import get_docs_version
 
 RECURSIVE_RELATIONSHIP_CONSTANT = 'self'
 
@@ -1584,9 +1585,10 @@ class ForeignObject(RelatedField):
     related_accessor_class = ForeignRelatedObjectsDescriptor
     rel_class = ForeignObjectRel
 
-    def __init__(self, to, from_fields, to_fields, rel=None, related_name=None,
+    def __init__(self, to, on_delete, from_fields, to_fields, rel=None, related_name=None,
             related_query_name=None, limit_choices_to=None, parent_link=False,
-            on_delete=CASCADE, swappable=True, **kwargs):
+            swappable=True, **kwargs):
+
         if rel is None:
             rel = self.rel_class(
                 self, to,
@@ -1650,14 +1652,14 @@ class ForeignObject(RelatedField):
 
     def deconstruct(self):
         name, path, args, kwargs = super(ForeignObject, self).deconstruct()
+        kwargs['on_delete'] = self.remote_field.on_delete
         kwargs['from_fields'] = self.from_fields
         kwargs['to_fields'] = self.to_fields
+
         if self.remote_field.related_name is not None:
             kwargs['related_name'] = self.remote_field.related_name
         if self.remote_field.related_query_name is not None:
             kwargs['related_query_name'] = self.remote_field.related_query_name
-        if self.remote_field.on_delete != CASCADE:
-            kwargs['on_delete'] = self.remote_field.on_delete
         if self.remote_field.parent_link:
             kwargs['parent_link'] = self.remote_field.parent_link
         # Work out string form of "to"
@@ -1866,8 +1868,8 @@ class ForeignKey(ForeignObject):
     }
     description = _("Foreign Key (type determined by related field)")
 
-    def __init__(self, to, to_field=None, related_name=None, related_query_name=None,
-            limit_choices_to=None, parent_link=False, on_delete=CASCADE,
+    def __init__(self, to, on_delete=None, related_name=None, related_query_name=None,
+            limit_choices_to=None, parent_link=False, to_field=None,
             db_constraint=True, **kwargs):
         try:
             to._meta.model_name
@@ -1885,6 +1887,28 @@ class ForeignKey(ForeignObject):
             # be correct until contribute_to_class is called. Refs #12190.
             to_field = to_field or (to._meta.pk and to._meta.pk.name)
 
+        if on_delete is None:
+            warnings.warn(
+                "on_delete will be a required arg for %s in Django 2.0. "
+                "Set it to models.CASCADE if you want to maintain the current default behavior. "
+                "See https://docs.djangoproject.com/en/%s/ref/models/fields/"
+                "#django.db.models.ForeignKey.on_delete" % (
+                    self.__class__.__name__,
+                    get_docs_version(),
+                ),
+                RemovedInDjango20Warning, 2)
+            on_delete = CASCADE
+
+        elif not callable(on_delete):
+            warnings.warn(
+                "The signature for {0} will change in Django 2.0. "
+                "Pass to_field='{1}' as a kwarg instead of as an arg.".format(
+                    self.__class__.__name__,
+                    on_delete,
+                ),
+                RemovedInDjango20Warning, 2)
+            on_delete, to_field = to_field, on_delete
+
         kwargs['rel'] = self.rel_class(
             self, to, to_field,
             related_name=related_name,
@@ -1897,7 +1921,7 @@ class ForeignKey(ForeignObject):
         kwargs['db_index'] = kwargs.get('db_index', True)
 
         super(ForeignKey, self).__init__(
-            to, from_fields=['self'], to_fields=[to_field], **kwargs)
+            to, on_delete, from_fields=['self'], to_fields=[to_field], **kwargs)
 
         self.db_constraint = db_constraint
 
@@ -2101,9 +2125,33 @@ class OneToOneField(ForeignKey):
 
     description = _("One-to-one relationship")
 
-    def __init__(self, to, to_field=None, **kwargs):
+    def __init__(self, to, on_delete=None, to_field=None, **kwargs):
         kwargs['unique'] = True
-        super(OneToOneField, self).__init__(to, to_field, **kwargs)
+
+        if on_delete is None:
+            warnings.warn(
+                "on_delete will be a required arg for %s in Django 2.0. "
+                "Set it to models.CASCADE if you want to maintain the current default behavior. "
+                "See https://docs.djangoproject.com/en/%s/ref/models/fields/"
+                "#django.db.models.ForeignKey.on_delete" % (
+                    self.__class__.__name__,
+                    get_docs_version(),
+                ),
+                RemovedInDjango20Warning, 2)
+            on_delete = CASCADE
+
+        elif not callable(on_delete):
+            warnings.warn(
+                "The signature for {0} will change in Django 2.0. "
+                "Pass to_field='{1}' as a kwarg instead of as an arg.".format(
+                    self.__class__.__name__,
+                    on_delete,
+                ),
+                RemovedInDjango20Warning, 2)
+            to_field = on_delete
+            on_delete = CASCADE  # Avoid warning in superclass
+
+        super(OneToOneField, self).__init__(to, on_delete, to_field=to_field, **kwargs)
 
     def deconstruct(self):
         name, path, args, kwargs = super(OneToOneField, self).deconstruct()
@@ -2162,12 +2210,14 @@ def create_many_to_many_intermediary_model(field, klass):
             related_name='%s+' % name,
             db_tablespace=field.db_tablespace,
             db_constraint=field.remote_field.db_constraint,
+            on_delete=CASCADE,
         ),
         to: models.ForeignKey(
             to_model,
             related_name='%s+' % name,
             db_tablespace=field.db_tablespace,
             db_constraint=field.remote_field.db_constraint,
+            on_delete=CASCADE,
         )
     })
 

+ 3 - 0
docs/internals/deprecation.txt

@@ -30,6 +30,9 @@ details on these changes.
 
 * ``Field.remote_field.to`` attribute will be removed.
 
+* The ``on_delete`` argument for ``ForeignKey`` and ``OneToOneField``  will be
+  required.
+
 * ``django.db.models.fields.add_lazy_relation()`` will be removed.
 
 * When time zone support is enabled, database backends that don't support time

+ 2 - 2
docs/intro/overview.txt

@@ -40,7 +40,7 @@ database-schema problems. Here's a quick example:
         pub_date = models.DateField()
         headline = models.CharField(max_length=200)
         content = models.TextField()
-        reporter = models.ForeignKey(Reporter)
+        reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
 
         def __str__(self):              # __unicode__ on Python 2
             return self.headline
@@ -154,7 +154,7 @@ as easy as registering your model in the admin site:
         pub_date = models.DateField()
         headline = models.CharField(max_length=200)
         content = models.TextField()
-        reporter = models.ForeignKey(Reporter)
+        reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
 
 .. snippet::
     :filename: mysite/news/admin.py

+ 1 - 1
docs/intro/tutorial02.txt

@@ -141,7 +141,7 @@ These concepts are represented by simple Python classes. Edit the
 
 
     class Choice(models.Model):
-        question = models.ForeignKey(Question)
+        question = models.ForeignKey(Question, on_delete=models.CASCADE)
         choice_text = models.CharField(max_length=200)
         votes = models.IntegerField(default=0)
 

+ 6 - 2
docs/ref/contrib/admin/admindocs.txt

@@ -74,8 +74,12 @@ A model with useful documentation might look like this::
 
         """
         slug = models.SlugField(help_text="A short label, generally used in URLs.")
-        author = models.ForeignKey(User)
-        blog = models.ForeignKey(Blog)
+        author = models.ForeignKey(
+            User,
+            models.SET_NULL,
+            blank=True, null=True,
+        )
+        blog = models.ForeignKey(Blog, models.CASCADE)
         ...
 
         def publish(self):

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

@@ -1957,7 +1957,7 @@ information.
             name = models.CharField(max_length=100)
 
          class Book(models.Model):
-            author = models.ForeignKey(Author)
+            author = models.ForeignKey(Author, on_delete=models.CASCADE)
             title = models.CharField(max_length=100)
 
     You can edit the books authored by an author on the author page. You add
@@ -2174,8 +2174,8 @@ Take this model for instance::
     from django.db import models
 
     class Friendship(models.Model):
-        to_person = models.ForeignKey(Person, related_name="friends")
-        from_person = models.ForeignKey(Person, related_name="from_friends")
+        to_person = models.ForeignKey(Person, on_delete=models.CASCADE, related_name="friends")
+        from_person = models.ForeignKey(Person, on_delete=models.CASCADE, related_name="from_friends")
 
 If you wanted to display an inline on the ``Person`` admin add/change pages
 you need to explicitly define the foreign key since it is unable to do so
@@ -2281,8 +2281,8 @@ models::
         members = models.ManyToManyField(Person, through='Membership')
 
     class Membership(models.Model):
-        person = models.ForeignKey(Person)
-        group = models.ForeignKey(Group)
+        person = models.ForeignKey(Person, on_delete=models.CASCADE)
+        group = models.ForeignKey(Group, on_delete=models.CASCADE)
         date_joined = models.DateField()
         invite_reason = models.CharField(max_length=64)
 
@@ -2326,7 +2326,7 @@ you have the following models::
 
     class Image(models.Model):
         image = models.ImageField(upload_to="images")
-        content_type = models.ForeignKey(ContentType)
+        content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
         object_id = models.PositiveIntegerField()
         content_object = GenericForeignKey("content_type", "object_id")
 

+ 1 - 1
docs/ref/contrib/contenttypes.txt

@@ -256,7 +256,7 @@ A simple example is a tagging system, which might look like this::
 
     class TaggedItem(models.Model):
         tag = models.SlugField()
-        content_type = models.ForeignKey(ContentType)
+        content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
         object_id = models.PositiveIntegerField()
         content_object = GenericForeignKey('content_type', 'object_id')
 

+ 1 - 1
docs/ref/contrib/gis/model-api.txt

@@ -269,7 +269,7 @@ model::
         street = models.CharField(max_length=100)
         city = models.CharField(max_length=100)
         state = models.CharField(max_length=2)
-        zipcode = models.ForeignKey(Zipcode)
+        zipcode = models.ForeignKey(Zipcode, on_delete=models.CASCADE)
         objects = models.GeoManager()
 
 The geographic manager is needed to do spatial queries on related ``Zipcode`` objects,

+ 3 - 3
docs/ref/contrib/sites.txt

@@ -117,7 +117,7 @@ like this::
     class Article(models.Model):
         headline = models.CharField(max_length=200)
         # ...
-        site = models.ForeignKey(Site)
+        site = models.ForeignKey(Site, on_delete=models.CASCADE)
 
 This has the same benefits as described in the last section.
 
@@ -334,7 +334,7 @@ your model explicitly. For example::
         photo = models.FileField(upload_to='/home/photos')
         photographer_name = models.CharField(max_length=100)
         pub_date = models.DateField()
-        site = models.ForeignKey(Site)
+        site = models.ForeignKey(Site, on_delete=models.CASCADE)
         objects = models.Manager()
         on_site = CurrentSiteManager()
 
@@ -371,7 +371,7 @@ demonstrates this::
         photo = models.FileField(upload_to='/home/photos')
         photographer_name = models.CharField(max_length=100)
         pub_date = models.DateField()
-        publish_on = models.ForeignKey(Site)
+        publish_on = models.ForeignKey(Site, on_delete=models.CASCADE)
         objects = models.Manager()
         on_site = CurrentSiteManager('publish_on')
 

+ 1 - 1
docs/ref/models/database-functions.txt

@@ -113,7 +113,7 @@ Usage example::
     class Comment(models.Model):
         body = models.TextField()
         modified = models.DateTimeField(auto_now=True)
-        blog = models.ForeignKey(Blog)
+        blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
 
     >>> from django.db.models.functions import Greatest
     >>> blog = Blog.objects.create(body='Greatest is the best.')

+ 138 - 79
docs/ref/models/fields.txt

@@ -1116,15 +1116,22 @@ Django also defines a set of fields that represent relations.
 ``ForeignKey``
 --------------
 
-.. class:: ForeignKey(othermodel, **options)
+.. class:: ForeignKey(othermodel, on_delete, **options)
 
 A many-to-one relationship. Requires a positional argument: the class to which
 the model is related.
 
+.. versionchanged:: 1.9
+
+    ``on_delete`` can now be used as the second positional argument (previously
+    it was typically only passed as a keyword argument). It will be a required
+    argument in Django 2.0.
+
 .. _recursive-relationships:
 
 To create a recursive relationship -- an object that has a many-to-one
-relationship with itself -- use ``models.ForeignKey('self')``.
+relationship with itself -- use ``models.ForeignKey('self',
+on_delete=models.CASCADE)``.
 
 .. _lazy-relationships:
 
@@ -1134,7 +1141,10 @@ you can use the name of the model, rather than the model object itself::
     from django.db import models
 
     class Car(models.Model):
-        manufacturer = models.ForeignKey('Manufacturer')
+        manufacturer = models.ForeignKey(
+            'Manufacturer',
+            on_delete=models.CASCADE,
+        )
         # ...
 
     class Manufacturer(models.Model):
@@ -1147,7 +1157,10 @@ model above is defined in another application called ``production``, you'd
 need to use::
 
     class Car(models.Model):
-        manufacturer = models.ForeignKey('production.Manufacturer')
+        manufacturer = models.ForeignKey(
+            'production.Manufacturer',
+            on_delete=models.CASCADE,
+        )
 
 This sort of reference can be useful when resolving circular import
 dependencies between two applications.
@@ -1173,8 +1186,79 @@ deal with the field names of your model object.
 Arguments
 ~~~~~~~~~
 
-:class:`ForeignKey` accepts an extra set of arguments -- all optional -- that
-define the details of how the relation works.
+:class:`ForeignKey` accepts other arguments that define the details of how the
+relation works.
+
+.. attribute:: ForeignKey.on_delete
+
+    When an object referenced by a :class:`ForeignKey` is deleted, Django will
+    emulate the behavior of the SQL constraint specified by the
+    :attr:`on_delete` argument. For example, if you have a nullable
+    :class:`ForeignKey` and you want it to be set null when the referenced
+    object is deleted::
+
+        user = models.ForeignKey(
+            User,
+            models.SET_NULL,
+            blank=True,
+            null=True,
+        )
+
+    .. deprecated:: 1.9
+
+        :attr:`~ForeignKey.on_delete` will become a required argument in Django
+        2.0. In older versions it defaults to ``CASCADE``.
+
+The possible values for :attr:`~ForeignKey.on_delete` are found in
+:mod:`django.db.models`:
+
+* .. attribute:: CASCADE
+
+    Cascade deletes. Django emulates the behavior of the SQL constraint ON
+    DELETE CASCADE and also deletes the object containing the ForeignKey.
+
+* .. attribute:: PROTECT
+
+    Prevent deletion of the referenced object by raising
+    :exc:`~django.db.models.ProtectedError`, a subclass of
+    :exc:`django.db.IntegrityError`.
+
+* .. attribute:: SET_NULL
+
+    Set the :class:`ForeignKey` null; this is only possible if
+    :attr:`~Field.null` is ``True``.
+
+* .. attribute:: SET_DEFAULT
+
+    Set the :class:`ForeignKey` to its default value; a default for the
+    :class:`ForeignKey` must be set.
+
+* .. function:: SET()
+
+    Set the :class:`ForeignKey` to the value passed to
+    :func:`~django.db.models.SET()`, or if a callable is passed in,
+    the result of calling it. In most cases, passing a callable will be
+    necessary to avoid executing queries at the time your models.py is
+    imported::
+
+        from django.conf import settings
+        from django.contrib.auth import get_user_model
+        from django.db import models
+
+        def get_sentinel_user():
+            return get_user_model().objects.get_or_create(username='deleted')[0]
+
+        class MyModel(models.Model):
+            user = models.ForeignKey(
+                settings.AUTH_USER_MODEL,
+                on_delete=models.SET(get_sentinel_user),
+            )
+
+* .. attribute:: DO_NOTHING
+
+    Take no action. If your database backend enforces referential
+    integrity, this will cause an :exc:`~django.db.IntegrityError` unless
+    you manually add an SQL ``ON DELETE`` constraint to the database field.
 
 .. attribute:: ForeignKey.limit_choices_to
 
@@ -1186,7 +1270,11 @@ define the details of how the relation works.
 
     For example::
 
-        staff_member = models.ForeignKey(User, limit_choices_to={'is_staff': True})
+        staff_member = models.ForeignKey(
+            User,
+            on_delete=models.CASCADE,
+            limit_choices_to={'is_staff': True},
+        )
 
     causes the corresponding field on the ``ModelForm`` to list only ``Users``
     that have ``is_staff=True``. This may be helpful in the Django admin.
@@ -1231,7 +1319,11 @@ define the details of how the relation works.
     ensure that the ``User`` model won't have a backwards relation to this
     model::
 
-        user = models.ForeignKey(User, related_name='+')
+        user = models.ForeignKey(
+            User,
+            on_delete=models.CASCADE,
+            related_name='+',
+        )
 
 .. attribute:: ForeignKey.related_query_name
 
@@ -1241,7 +1333,12 @@ define the details of how the relation works.
 
         # Declare the ForeignKey with related_query_name
         class Tag(models.Model):
-            article = models.ForeignKey(Article, related_name="tags", related_query_name="tag")
+            article = models.ForeignKey(
+                Article,
+                on_delete=models.CASCADE,
+                related_name="tags",
+                related_query_name="tag",
+            )
             name = models.CharField(max_length=255)
 
         # That's now the name of the reverse filter
@@ -1265,65 +1362,6 @@ define the details of how the relation works.
     If this is set to ``False``, accessing a related object that doesn't exist
     will raise its ``DoesNotExist`` exception.
 
-.. attribute:: ForeignKey.on_delete
-
-    When an object referenced by a :class:`ForeignKey` is deleted, Django by
-    default emulates the behavior of the SQL constraint ``ON DELETE CASCADE``
-    and also deletes the object containing the ``ForeignKey``. This behavior
-    can be overridden by specifying the :attr:`on_delete` argument. For
-    example, if you have a nullable :class:`ForeignKey` and you want it to be
-    set null when the referenced object is deleted::
-
-        user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
-
-The possible values for :attr:`~ForeignKey.on_delete` are found in
-:mod:`django.db.models`:
-
-* .. attribute:: CASCADE
-
-    Cascade deletes; the default.
-
-* .. attribute:: PROTECT
-
-    Prevent deletion of the referenced object by raising
-    :exc:`~django.db.models.ProtectedError`, a subclass of
-    :exc:`django.db.IntegrityError`.
-
-* .. attribute:: SET_NULL
-
-    Set the :class:`ForeignKey` null; this is only possible if
-    :attr:`~Field.null` is ``True``.
-
-* .. attribute:: SET_DEFAULT
-
-    Set the :class:`ForeignKey` to its default value; a default for the
-    :class:`ForeignKey` must be set.
-
-* .. function:: SET()
-
-    Set the :class:`ForeignKey` to the value passed to
-    :func:`~django.db.models.SET()`, or if a callable is passed in,
-    the result of calling it. In most cases, passing a callable will be
-    necessary to avoid executing queries at the time your models.py is
-    imported::
-
-        from django.conf import settings
-        from django.contrib.auth import get_user_model
-        from django.db import models
-
-        def get_sentinel_user():
-            return get_user_model().objects.get_or_create(username='deleted')[0]
-
-        class MyModel(models.Model):
-            user = models.ForeignKey(settings.AUTH_USER_MODEL,
-                                     on_delete=models.SET(get_sentinel_user))
-
-* .. attribute:: DO_NOTHING
-
-    Take no action. If your database backend enforces referential
-    integrity, this will cause an :exc:`~django.db.IntegrityError` unless
-    you manually add an SQL ``ON DELETE`` constraint to the database field.
-
 .. attribute:: ForeignKey.swappable
 
     Controls the migration framework's reaction if this :class:`ForeignKey`
@@ -1367,7 +1405,7 @@ The possible values for :attr:`~ForeignKey.on_delete` are found in
             allow_unsaved_instance_assignment = True
 
         class Book(models.Model):
-            author = UnsavedForeignKey(Author)
+            author = UnsavedForeignKey(Author, on_delete=models.CASCADE)
 
 .. _ref-manytomany:
 
@@ -1492,12 +1530,20 @@ that control how the relationship functions.
 
         class Group(models.Model):
             name = models.CharField(max_length=128)
-            members = models.ManyToManyField(Person, through='Membership', through_fields=('group', 'person'))
+            members = models.ManyToManyField(
+                Person,
+                through='Membership',
+                through_fields=('group', 'person'),
+            )
 
         class Membership(models.Model):
-            group = models.ForeignKey(Group)
-            person = models.ForeignKey(Person)
-            inviter = models.ForeignKey(Person, related_name="membership_invites")
+            group = models.ForeignKey(Group, on_delete=models.CASCADE)
+            person = models.ForeignKey(Person, on_delete=models.CASCADE)
+            inviter = models.ForeignKey(
+                Person,
+                on_delete=models.CASCADE,
+                related_name="membership_invites",
+            )
             invite_reason = models.CharField(max_length=64)
 
     ``Membership`` has *two* foreign keys to ``Person`` (``person`` and
@@ -1577,12 +1623,18 @@ relationship at the database level.
 ``OneToOneField``
 -----------------
 
-.. class:: OneToOneField(othermodel, parent_link=False, **options)
+.. class:: OneToOneField(othermodel, on_delete, parent_link=False, **options)
 
 A one-to-one relationship. Conceptually, this is similar to a
 :class:`ForeignKey` with :attr:`unique=True <Field.unique>`, but the
 "reverse" side of the relation will directly return a single object.
 
+.. versionchanged:: 1.9
+
+    ``on_delete`` can now be used as the second positional argument (previously
+    it was typically only passed as a keyword argument). It will be a required
+    argument in Django 2.0.
+
 This is most useful as the primary key of a model which "extends"
 another model in some way; :ref:`multi-table-inheritance` is
 implemented by adding an implicit one-to-one relation from the child
@@ -1603,8 +1655,15 @@ With the following example::
     from django.db import models
 
     class MySpecialUser(models.Model):
-        user = models.OneToOneField(settings.AUTH_USER_MODEL)
-        supervisor = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='supervisor_of')
+        user = models.OneToOneField(
+            settings.AUTH_USER_MODEL,
+            on_delete=models.CASCADE,
+        )
+        supervisor = models.OneToOneField(
+            settings.AUTH_USER_MODEL,
+            on_delete=models.CASCADE,
+            related_name='supervisor_of',
+        )
 
 your resulting ``User`` model will have the following attributes::
 
@@ -1931,6 +1990,6 @@ have boolean values (rather than ``None``) if the field is a relation type
 .. attribute:: Field.related_model
 
     Points to the model the field relates to. For example, ``Author`` in
-    ``ForeignKey(Author)``. If a field has a generic relation (such as a
-    ``GenericForeignKey`` or a ``GenericRelation``) then ``related_model``
-    will be ``None``.
+    ``ForeignKey(Author, on_delete=models.CASCADE)``. If a field has a generic
+    relation (such as a ``GenericForeignKey`` or a ``GenericRelation``) then
+    ``related_model`` will be ``None``.

+ 1 - 1
docs/ref/models/options.txt

@@ -186,7 +186,7 @@ Django quotes column and table names behind the scenes.
             # ...
 
         class Answer(models.Model):
-            question = models.ForeignKey(Question)
+            question = models.ForeignKey(Question, on_delete=models.CASCADE)
             # ...
 
             class Meta:

+ 12 - 3
docs/ref/models/querysets.txt

@@ -350,7 +350,11 @@ related model ordering can change the expected results.
     Consider this case::
 
          class Event(Model):
-            parent = models.ForeignKey('self', related_name='children')
+            parent = models.ForeignKey(
+                'self',
+                on_delete=models.CASCADE,
+                related_name='children',
+            )
             date = models.DateField()
 
          Event.objects.order_by('children__date')
@@ -806,11 +810,16 @@ following models::
 
     class Person(models.Model):
         # ...
-        hometown = models.ForeignKey(City)
+        hometown = models.ForeignKey(
+            City,
+            on_delete=models.SET_NULL,
+            blank=True,
+            null=True,
+        )
 
     class Book(models.Model):
         # ...
-        author = models.ForeignKey(Person)
+        author = models.ForeignKey(Person, on_delete=models.CASCADE)
 
 ... then a call to ``Book.objects.select_related('author__hometown').get(id=4)``
 will cache the related ``Person`` *and* the related ``City``::

+ 1 - 1
docs/ref/models/relations.txt

@@ -19,7 +19,7 @@ Related objects reference
                 pass
 
             class Article(models.Model):
-                reporter = models.ForeignKey(Reporter)
+                reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
 
       In the above example, the methods below will be available on
       the manager ``reporter.article_set``.

+ 13 - 0
docs/releases/1.9.txt

@@ -928,6 +928,19 @@ versions:
 Its parsing caused bugs with the current syntax, so support for the old syntax
 will be removed in Django 2.0 following an accelerated deprecation.
 
+``ForeignKey`` and ``OneToOneField`` ``on_delete`` argument
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In order to increase awareness about cascading model deletion, the
+``on_delete`` argument of ``ForeignKey`` and ``OneToOneField`` will be required
+in Django 2.0.
+
+Update models and existing migrations to explicitly set the argument. Since the
+default is ``models.CASCADE``, add ``on_delete=models.CASCADE`` to all
+``ForeignKey`` and ``OneToOneField``\s that don't use a different option. You
+can also pass it as the second positional argument if you don't care about
+compatibility with older versions of Django.
+
 ``Field.rel`` changes
 ~~~~~~~~~~~~~~~~~~~~~
 

+ 5 - 2
docs/topics/auth/customizing.txt

@@ -312,7 +312,7 @@ you might create an Employee model::
     from django.contrib.auth.models import User
 
     class Employee(models.Model):
-        user = models.OneToOneField(User)
+        user = models.OneToOneField(User, on_delete=models.CASCADE)
         department = models.CharField(max_length=100)
 
 Assuming an existing Employee Fred Smith who has both a User and Employee
@@ -443,7 +443,10 @@ different User model.
         from django.db import models
 
         class Article(models.Model):
-            author = models.ForeignKey(settings.AUTH_USER_MODEL)
+            author = models.ForeignKey
+                settings.AUTH_USER_MODEL,
+                on_delete=models.CASCADE,
+            )
 
     When connecting to signals sent by the ``User`` model, you should specify
     the custom model using the :setting:`AUTH_USER_MODEL` setting. For example::

+ 1 - 1
docs/topics/class-based-views/generic-display.txt

@@ -104,7 +104,7 @@ We'll be using these models::
     class Book(models.Model):
         title = models.CharField(max_length=100)
         authors = models.ManyToManyField('Author')
-        publisher = models.ForeignKey(Publisher)
+        publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
         publication_date = models.DateField()
 
 Now we need to define a view::

+ 1 - 1
docs/topics/class-based-views/generic-editing.txt

@@ -204,7 +204,7 @@ the foreign key relation to the model:
 
     class Author(models.Model):
         name = models.CharField(max_length=200)
-        created_by = models.ForeignKey(User)
+        created_by = models.ForeignKey(User, on_delete=models.CASCADE)
 
         # ...
 

+ 1 - 1
docs/topics/db/examples/many_to_one.txt

@@ -17,7 +17,7 @@ To define a many-to-one relationship, use :class:`~django.db.models.ForeignKey`:
     class Article(models.Model):
         headline = models.CharField(max_length=100)
         pub_date = models.DateField()
-        reporter = models.ForeignKey(Reporter)
+        reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
 
         def __str__(self):              # __unicode__ on Python 2
             return self.headline

+ 6 - 2
docs/topics/db/examples/one_to_one.txt

@@ -16,7 +16,11 @@ In this example, a ``Place`` optionally can be a ``Restaurant``::
             return "%s the place" % self.name
 
     class Restaurant(models.Model):
-        place = models.OneToOneField(Place, primary_key=True)
+        place = models.OneToOneField(
+            Place,
+            on_delete=models.CASCADE,
+            primary_key=True,
+        )
         serves_hot_dogs = models.BooleanField(default=False)
         serves_pizza = models.BooleanField(default=False)
 
@@ -24,7 +28,7 @@ In this example, a ``Place`` optionally can be a ``Restaurant``::
             return "%s the restaurant" % self.place.name
 
     class Waiter(models.Model):
-        restaurant = models.ForeignKey(Restaurant)
+        restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
         name = models.CharField(max_length=50)
 
         def __str__(self):              # __unicode__ on Python 2

+ 1 - 1
docs/topics/db/managers.txt

@@ -87,7 +87,7 @@ returns a list of all ``OpinionPoll`` objects, each with an extra
         objects = PollManager()
 
     class Response(models.Model):
-        poll = models.ForeignKey(OpinionPoll)
+        poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE)
         person_name = models.CharField(max_length=50)
         response = models.TextField()
 

+ 24 - 8
docs/topics/db/models.txt

@@ -99,7 +99,7 @@ Example::
         instrument = models.CharField(max_length=100)
 
     class Album(models.Model):
-        artist = models.ForeignKey(Musician)
+        artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
         name = models.CharField(max_length=100)
         release_date = models.DateField()
         num_stars = models.IntegerField()
@@ -281,9 +281,17 @@ In this example, the verbose name is ``"first name"``::
 :class:`~django.db.models.OneToOneField` require the first argument to be a
 model class, so use the :attr:`~Field.verbose_name` keyword argument::
 
-    poll = models.ForeignKey(Poll, verbose_name="the related poll")
+    poll = models.ForeignKey(
+        Poll,
+        on_delete=models.CASCADE,
+        verbose_name="the related poll",
+    )
     sites = models.ManyToManyField(Site, verbose_name="list of sites")
-    place = models.OneToOneField(Place, verbose_name="related place")
+    place = models.OneToOneField(
+        Place,
+        on_delete=models.CASCADE,
+        verbose_name="related place",
+    )
 
 The convention is not to capitalize the first letter of the
 :attr:`~Field.verbose_name`. Django will automatically capitalize the first
@@ -317,7 +325,7 @@ For example, if a ``Car`` model has a ``Manufacturer`` -- that is, a
         pass
 
     class Car(models.Model):
-        manufacturer = models.ForeignKey(Manufacturer)
+        manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
         # ...
 
 You can also create :ref:`recursive relationships <recursive-relationships>` (an
@@ -331,7 +339,10 @@ above) be the name of the model, lowercase. You can, of course, call the field
 whatever you want. For example::
 
     class Car(models.Model):
-        company_that_makes_it = models.ForeignKey(Manufacturer)
+        company_that_makes_it = models.ForeignKey(
+            Manufacturer,
+            on_delete=models.CASCADE,
+        )
         # ...
 
 .. seealso::
@@ -445,8 +456,8 @@ something like this::
             return self.name
 
     class Membership(models.Model):
-        person = models.ForeignKey(Person)
-        group = models.ForeignKey(Group)
+        person = models.ForeignKey(Person, on_delete=models.CASCADE)
+        group = models.ForeignKey(Group, on_delete=models.CASCADE)
         date_joined = models.DateField()
         invite_reason = models.CharField(max_length=64)
 
@@ -621,7 +632,12 @@ just refer to the other model class wherever needed. For example::
 
     class Restaurant(models.Model):
         # ...
-        zip_code = models.ForeignKey(ZipCode)
+        zip_code = models.ForeignKey(
+            ZipCode,
+            on_delete=models.SET_NULL,
+            blank=True,
+            null=True,
+        )
 
 Field name restrictions
 -----------------------

+ 3 - 3
docs/topics/db/queries.txt

@@ -1160,8 +1160,8 @@ Example::
 You can override the ``FOO_set`` name by setting the
 :attr:`~django.db.models.ForeignKey.related_name` parameter in the
 :class:`~django.db.models.ForeignKey` definition. For example, if the ``Entry``
-model was altered to ``blog = ForeignKey(Blog, related_name='entries')``, the
-above example code would look like this::
+model was altered to ``blog = ForeignKey(Blog, on_delete=models.CASCADE,
+related_name='entries')``, the above example code would look like this::
 
     >>> b = Blog.objects.get(id=1)
     >>> b.entries.all() # Returns all Entry objects related to Blog.
@@ -1284,7 +1284,7 @@ model.
 For example::
 
     class EntryDetail(models.Model):
-        entry = models.OneToOneField(Entry)
+        entry = models.OneToOneField(Entry, on_delete=models.CASCADE)
         details = models.TextField()
 
     ed = EntryDetail.objects.get(id=2)

+ 11 - 3
docs/topics/forms/modelforms.txt

@@ -1119,7 +1119,7 @@ you have these two models::
         name = models.CharField(max_length=100)
 
     class Book(models.Model):
-        author = models.ForeignKey(Author)
+        author = models.ForeignKey(Author, on_delete=models.CASCADE)
         title = models.CharField(max_length=100)
 
 If you want to create a formset that allows you to edit books belonging to
@@ -1178,8 +1178,16 @@ need to resolve the ambiguity manually using ``fk_name``. For example, consider
 the following model::
 
     class Friendship(models.Model):
-        from_friend = models.ForeignKey(Friend, related_name='from_friends')
-        to_friend = models.ForeignKey(Friend, related_name='friends')
+        from_friend = models.ForeignKey(
+            Friend,
+            on_delete=models.CASCADE,
+            related_name='from_friends',
+        )
+        to_friend = models.ForeignKey(
+            Friend,
+            on_delete=models.CASCADE,
+            related_name='friends',
+        )
         length_in_months = models.IntegerField()
 
 To resolve this, you can use ``fk_name`` to

+ 12 - 4
docs/topics/i18n/translation.txt

@@ -355,8 +355,12 @@ You can mark names of :class:`~django.db.models.ForeignKey`,
 their :attr:`~django.db.models.Options.verbose_name` options::
 
     class MyThing(models.Model):
-        kind = models.ForeignKey(ThingKind, related_name='kinds',
-                                 verbose_name=_('kind'))
+        kind = models.ForeignKey(
+            ThingKind,
+            on_delete=models.CASCADE,
+            related_name='kinds',
+            verbose_name=_('kind'),
+        )
 
 Just like you would do in :attr:`~django.db.models.Options.verbose_name` you
 should provide a lowercase verbose name text for the relation as Django will
@@ -391,8 +395,12 @@ with the ``short_description`` attribute::
     from django.utils.translation import ugettext_lazy as _
 
     class MyThing(models.Model):
-        kind = models.ForeignKey(ThingKind, related_name='kinds',
-                                 verbose_name=_('kind'))
+        kind = models.ForeignKey(
+            ThingKind,
+            on_delete=models.CASCADE,
+            related_name='kinds',
+            verbose_name=_('kind'),
+        )
 
         def is_mouse(self):
             return self.kind.type == MOUSE_TYPE

+ 2 - 2
docs/topics/serialization.txt

@@ -344,7 +344,7 @@ Consider the following two models::
 
     class Book(models.Model):
         name = models.CharField(max_length=100)
-        author = models.ForeignKey(Person)
+        author = models.ForeignKey(Person, on_delete=models.CASCADE)
 
 Ordinarily, serialized data for ``Book`` would use an integer to refer to
 the author. For example, in JSON, a Book might be serialized as::
@@ -514,7 +514,7 @@ example above::
 
     class Book(models.Model):
         name = models.CharField(max_length=100)
-        author = models.ForeignKey(Person)
+        author = models.ForeignKey(Person, on_delete=models.CASCADE)
 
         def natural_key(self):
             return (self.name,) + self.author.natural_key()

+ 7 - 7
tests/admin_changelist/models.py

@@ -12,7 +12,7 @@ class Parent(models.Model):
 
 
 class Child(models.Model):
-    parent = models.ForeignKey(Parent, editable=False, null=True)
+    parent = models.ForeignKey(Parent, models.SET_NULL, editable=False, null=True)
     name = models.CharField(max_length=30, blank=True)
     age = models.IntegerField(null=True, blank=True)
 
@@ -46,12 +46,12 @@ class Group(models.Model):
 
 class Concert(models.Model):
     name = models.CharField(max_length=30)
-    group = models.ForeignKey(Group)
+    group = models.ForeignKey(Group, models.CASCADE)
 
 
 class Membership(models.Model):
-    music = models.ForeignKey(Musician)
-    group = models.ForeignKey(Group)
+    music = models.ForeignKey(Musician, models.CASCADE)
+    group = models.ForeignKey(Group, models.CASCADE)
     role = models.CharField(max_length=15)
 
 
@@ -69,8 +69,8 @@ class ChordsBand(models.Model):
 
 
 class Invitation(models.Model):
-    player = models.ForeignKey(ChordsMusician)
-    band = models.ForeignKey(ChordsBand)
+    player = models.ForeignKey(ChordsMusician, models.CASCADE)
+    band = models.ForeignKey(ChordsBand, models.CASCADE)
     instrument = models.CharField(max_length=15)
 
 
@@ -84,7 +84,7 @@ class Swallow(models.Model):
 
 
 class SwallowOneToOne(models.Model):
-    swallow = models.OneToOneField(Swallow)
+    swallow = models.OneToOneField(Swallow, models.CASCADE)
 
 
 class UnorderedObject(models.Model):

+ 7 - 7
tests/admin_checks/models.py

@@ -15,7 +15,7 @@ class Album(models.Model):
 @python_2_unicode_compatible
 class Song(models.Model):
     title = models.CharField(max_length=150)
-    album = models.ForeignKey(Album)
+    album = models.ForeignKey(Album, models.CASCADE)
     original_release = models.DateField(editable=False)
 
     class Meta:
@@ -30,8 +30,8 @@ class Song(models.Model):
 
 
 class TwoAlbumFKAndAnE(models.Model):
-    album1 = models.ForeignKey(Album, related_name="album1_set")
-    album2 = models.ForeignKey(Album, related_name="album2_set")
+    album1 = models.ForeignKey(Album, models.CASCADE, related_name="album1_set")
+    album2 = models.ForeignKey(Album, models.CASCADE, related_name="album2_set")
     e = models.CharField(max_length=1)
 
 
@@ -47,8 +47,8 @@ class Book(models.Model):
 
 
 class AuthorsBooks(models.Model):
-    author = models.ForeignKey(Author)
-    book = models.ForeignKey(Book)
+    author = models.ForeignKey(Author, models.CASCADE)
+    book = models.ForeignKey(Book, models.CASCADE)
     featured = models.BooleanField()
 
 
@@ -57,12 +57,12 @@ class State(models.Model):
 
 
 class City(models.Model):
-    state = models.ForeignKey(State)
+    state = models.ForeignKey(State, models.CASCADE)
 
 
 class Influence(models.Model):
     name = models.TextField()
 
-    content_type = models.ForeignKey(ContentType)
+    content_type = models.ForeignKey(ContentType, models.CASCADE)
     object_id = models.PositiveIntegerField()
     content_object = GenericForeignKey('content_type', 'object_id')

+ 2 - 2
tests/admin_docs/models.py

@@ -38,8 +38,8 @@ class Person(models.Model):
     """
     first_name = models.CharField(max_length=200, help_text="The person's first name")
     last_name = models.CharField(max_length=200, help_text="The person's last name")
-    company = models.ForeignKey(Company, help_text="place of work")
-    family = models.ForeignKey(Family, related_name='+', null=True)
+    company = models.ForeignKey(Company, models.CASCADE, help_text="place of work")
+    family = models.ForeignKey(Family, models.SET_NULL, related_name='+', null=True)
     groups = models.ManyToManyField(Group, help_text="has membership")
 
     def _get_full_name(self):

+ 9 - 3
tests/admin_filters/models.py

@@ -13,7 +13,13 @@ from django.utils.encoding import python_2_unicode_compatible
 class Book(models.Model):
     title = models.CharField(max_length=50)
     year = models.PositiveIntegerField(null=True, blank=True)
-    author = models.ForeignKey(User, verbose_name="Verbose Author", related_name='books_authored', blank=True, null=True)
+    author = models.ForeignKey(
+        User,
+        models.SET_NULL,
+        verbose_name="Verbose Author",
+        related_name='books_authored',
+        blank=True, null=True,
+    )
     contributors = models.ManyToManyField(User, verbose_name="Verbose Contributors", related_name='books_contributed', blank=True)
     is_best_seller = models.NullBooleanField(default=0)
     date_registered = models.DateField(null=True)
@@ -34,7 +40,7 @@ class Department(models.Model):
 
 @python_2_unicode_compatible
 class Employee(models.Model):
-    department = models.ForeignKey(Department, to_field="code")
+    department = models.ForeignKey(Department, models.CASCADE, to_field="code")
     name = models.CharField(max_length=100)
 
     def __str__(self):
@@ -44,7 +50,7 @@ class Employee(models.Model):
 @python_2_unicode_compatible
 class TaggedItem(models.Model):
     tag = models.SlugField()
-    content_type = models.ForeignKey(ContentType, related_name='tagged_items')
+    content_type = models.ForeignKey(ContentType, models.CASCADE, related_name='tagged_items')
     object_id = models.PositiveIntegerField()
     content_object = GenericForeignKey('content_type', 'object_id')
 

+ 25 - 25
tests/admin_inlines/models.py

@@ -31,9 +31,9 @@ class Teacher(models.Model):
 @python_2_unicode_compatible
 class Child(models.Model):
     name = models.CharField(max_length=50)
-    teacher = models.ForeignKey(Teacher)
+    teacher = models.ForeignKey(Teacher, models.CASCADE)
 
-    content_type = models.ForeignKey(ContentType)
+    content_type = models.ForeignKey(ContentType, models.CASCADE)
     object_id = models.PositiveIntegerField()
     parent = GenericForeignKey()
 
@@ -52,7 +52,7 @@ class Author(models.Model):
 
 class NonAutoPKBook(models.Model):
     rand_pk = models.IntegerField(primary_key=True, editable=False)
-    author = models.ForeignKey(Author)
+    author = models.ForeignKey(Author, models.CASCADE)
     title = models.CharField(max_length=50)
 
     def save(self, *args, **kwargs):
@@ -65,7 +65,7 @@ class NonAutoPKBook(models.Model):
 
 class EditablePKBook(models.Model):
     manual_pk = models.IntegerField(primary_key=True)
-    author = models.ForeignKey(Author)
+    author = models.ForeignKey(Author, models.CASCADE)
     title = models.CharField(max_length=50)
 
 
@@ -75,7 +75,7 @@ class Holder(models.Model):
 
 class Inner(models.Model):
     dummy = models.IntegerField()
-    holder = models.ForeignKey(Holder)
+    holder = models.ForeignKey(Holder, models.CASCADE)
     readonly = models.CharField("Inner readonly label", max_length=1)
 
     def get_absolute_url(self):
@@ -88,7 +88,7 @@ class Holder2(models.Model):
 
 class Inner2(models.Model):
     dummy = models.IntegerField()
-    holder = models.ForeignKey(Holder2)
+    holder = models.ForeignKey(Holder2, models.CASCADE)
 
 
 class Holder3(models.Model):
@@ -97,7 +97,7 @@ class Holder3(models.Model):
 
 class Inner3(models.Model):
     dummy = models.IntegerField()
-    holder = models.ForeignKey(Holder3)
+    holder = models.ForeignKey(Holder3, models.CASCADE)
 
 # Models for ticket #8190
 
@@ -108,12 +108,12 @@ class Holder4(models.Model):
 
 class Inner4Stacked(models.Model):
     dummy = models.IntegerField(help_text="Awesome stacked help text is awesome.")
-    holder = models.ForeignKey(Holder4)
+    holder = models.ForeignKey(Holder4, models.CASCADE)
 
 
 class Inner4Tabular(models.Model):
     dummy = models.IntegerField(help_text="Awesome tabular help text is awesome.")
-    holder = models.ForeignKey(Holder4)
+    holder = models.ForeignKey(Holder4, models.CASCADE)
 
 # Models for #12749
 
@@ -127,13 +127,13 @@ class OutfitItem(models.Model):
 
 
 class Fashionista(models.Model):
-    person = models.OneToOneField(Person, primary_key=True)
+    person = models.OneToOneField(Person, models.CASCADE, primary_key=True)
     weaknesses = models.ManyToManyField(OutfitItem, through='ShoppingWeakness', blank=True)
 
 
 class ShoppingWeakness(models.Model):
-    fashionista = models.ForeignKey(Fashionista)
-    item = models.ForeignKey(OutfitItem)
+    fashionista = models.ForeignKey(Fashionista, models.CASCADE)
+    item = models.ForeignKey(OutfitItem, models.CASCADE)
 
 # Models for #13510
 
@@ -143,7 +143,7 @@ class TitleCollection(models.Model):
 
 
 class Title(models.Model):
-    collection = models.ForeignKey(TitleCollection, blank=True, null=True)
+    collection = models.ForeignKey(TitleCollection, models.SET_NULL, blank=True, null=True)
     title1 = models.CharField(max_length=100)
     title2 = models.CharField(max_length=100)
 
@@ -155,7 +155,7 @@ class Poll(models.Model):
 
 
 class Question(models.Model):
-    poll = models.ForeignKey(Poll)
+    poll = models.ForeignKey(Poll, models.CASCADE)
 
 
 class Novel(models.Model):
@@ -164,14 +164,14 @@ class Novel(models.Model):
 
 class Chapter(models.Model):
     name = models.CharField(max_length=40)
-    novel = models.ForeignKey(Novel)
+    novel = models.ForeignKey(Novel, models.CASCADE)
 
 
 class FootNote(models.Model):
     """
     Model added for ticket 19838
     """
-    chapter = models.ForeignKey(Chapter, on_delete=models.PROTECT)
+    chapter = models.ForeignKey(Chapter, models.PROTECT)
     note = models.CharField(max_length=40)
 
 # Models for #16838
@@ -183,17 +183,17 @@ class CapoFamiglia(models.Model):
 
 class Consigliere(models.Model):
     name = models.CharField(max_length=100, help_text='Help text for Consigliere')
-    capo_famiglia = models.ForeignKey(CapoFamiglia, related_name='+')
+    capo_famiglia = models.ForeignKey(CapoFamiglia, models.CASCADE, related_name='+')
 
 
 class SottoCapo(models.Model):
     name = models.CharField(max_length=100)
-    capo_famiglia = models.ForeignKey(CapoFamiglia, related_name='+')
+    capo_famiglia = models.ForeignKey(CapoFamiglia, models.CASCADE, related_name='+')
 
 
 class ReadOnlyInline(models.Model):
     name = models.CharField(max_length=100, help_text='Help text for ReadOnlyInline')
-    capo_famiglia = models.ForeignKey(CapoFamiglia)
+    capo_famiglia = models.ForeignKey(CapoFamiglia, models.CASCADE)
 
 
 # Models for #18433
@@ -206,7 +206,7 @@ class ParentModelWithCustomPk(models.Model):
 class ChildModel1(models.Model):
     my_own_pk = models.CharField(max_length=100, primary_key=True)
     name = models.CharField(max_length=100)
-    parent = models.ForeignKey(ParentModelWithCustomPk)
+    parent = models.ForeignKey(ParentModelWithCustomPk, models.CASCADE)
 
     def get_absolute_url(self):
         return '/child_model1/'
@@ -215,7 +215,7 @@ class ChildModel1(models.Model):
 class ChildModel2(models.Model):
     my_own_pk = models.CharField(max_length=100, primary_key=True)
     name = models.CharField(max_length=100)
-    parent = models.ForeignKey(ParentModelWithCustomPk)
+    parent = models.ForeignKey(ParentModelWithCustomPk, models.CASCADE)
 
     def get_absolute_url(self):
         return '/child_model2/'
@@ -224,7 +224,7 @@ class ChildModel2(models.Model):
 # Models for #19425
 class BinaryTree(models.Model):
     name = models.CharField(max_length=100)
-    parent = models.ForeignKey('self', null=True, blank=True)
+    parent = models.ForeignKey('self', models.SET_NULL, null=True, blank=True)
 
 # Models for #19524
 
@@ -238,7 +238,7 @@ class ExtraTerrestrial(LifeForm):
 
 
 class Sighting(models.Model):
-    et = models.ForeignKey(ExtraTerrestrial)
+    et = models.ForeignKey(ExtraTerrestrial, models.CASCADE)
     place = models.CharField(max_length=100)
 
 
@@ -250,7 +250,7 @@ class SomeParentModel(models.Model):
 class SomeChildModel(models.Model):
     name = models.CharField(max_length=1)
     position = models.PositiveIntegerField()
-    parent = models.ForeignKey(SomeParentModel)
+    parent = models.ForeignKey(SomeParentModel, models.CASCADE)
 
 # Other models
 
@@ -260,6 +260,6 @@ class ProfileCollection(models.Model):
 
 
 class Profile(models.Model):
-    collection = models.ForeignKey(ProfileCollection, blank=True, null=True)
+    collection = models.ForeignKey(ProfileCollection, models.SET_NULL, blank=True, null=True)
     first_name = models.CharField(max_length=100)
     last_name = models.CharField(max_length=100)

+ 1 - 1
tests/admin_ordering/models.py

@@ -13,7 +13,7 @@ class Band(models.Model):
 
 
 class Song(models.Model):
-    band = models.ForeignKey(Band)
+    band = models.ForeignKey(Band, models.CASCADE)
     name = models.CharField(max_length=100)
     duration = models.IntegerField()
     other_interpreters = models.ManyToManyField(Band, related_name='covers')

+ 1 - 1
tests/admin_scripts/app_with_import/models.py

@@ -5,4 +5,4 @@ from django.db import models
 # Regression for #13368. This is an example of a model
 # that imports a class that has an abstract base class.
 class UserProfile(models.Model):
-    user = models.OneToOneField(User, primary_key=True)
+    user = models.OneToOneField(User, models.CASCADE, primary_key=True)

+ 11 - 6
tests/admin_utils/models.py

@@ -15,7 +15,7 @@ class Article(models.Model):
     """
     A simple Article model for testing
     """
-    site = models.ForeignKey(Site, related_name="admin_articles")
+    site = models.ForeignKey(Site, models.CASCADE, related_name="admin_articles")
     title = models.CharField(max_length=100)
     title2 = models.CharField(max_length=100, verbose_name="another name")
     created = models.DateTimeField()
@@ -31,7 +31,7 @@ class Article(models.Model):
 @python_2_unicode_compatible
 class Count(models.Model):
     num = models.PositiveSmallIntegerField()
-    parent = models.ForeignKey('self', null=True)
+    parent = models.ForeignKey('self', models.CASCADE, null=True)
 
     def __str__(self):
         return six.text_type(self.num)
@@ -42,11 +42,11 @@ class Event(models.Model):
 
 
 class Location(models.Model):
-    event = models.OneToOneField(Event, verbose_name='awesome event')
+    event = models.OneToOneField(Event, models.CASCADE, verbose_name='awesome event')
 
 
 class Guest(models.Model):
-    event = models.OneToOneField(Event)
+    event = models.OneToOneField(Event, models.CASCADE)
     name = models.CharField(max_length=255)
 
     class Meta:
@@ -54,7 +54,7 @@ class Guest(models.Model):
 
 
 class EventGuide(models.Model):
-    event = models.ForeignKey(Event, on_delete=models.DO_NOTHING)
+    event = models.ForeignKey(Event, models.DO_NOTHING)
 
 
 class Vehicle(models.Model):
@@ -62,7 +62,12 @@ class Vehicle(models.Model):
 
 
 class VehicleMixin(Vehicle):
-    vehicle = models.OneToOneField(Vehicle, parent_link=True, related_name='vehicle_%(app_label)s_%(class)s')
+    vehicle = models.OneToOneField(
+        Vehicle,
+        models.CASCADE,
+        parent_link=True,
+        related_name='vehicle_%(app_label)s_%(class)s',
+    )
 
     class Meta:
         abstract = True

+ 75 - 49
tests/admin_views/models.py

@@ -39,8 +39,8 @@ class Article(models.Model):
     title = models.CharField(max_length=100)
     content = models.TextField()
     date = models.DateTimeField()
-    section = models.ForeignKey(Section, null=True, blank=True)
-    sub_section = models.ForeignKey(Section, null=True, blank=True, on_delete=models.SET_NULL, related_name='+')
+    section = models.ForeignKey(Section, models.CASCADE, null=True, blank=True)
+    sub_section = models.ForeignKey(Section, models.SET_NULL, null=True, blank=True, related_name='+')
 
     def __str__(self):
         return self.title
@@ -70,7 +70,7 @@ class Book(models.Model):
 @python_2_unicode_compatible
 class Promo(models.Model):
     name = models.CharField(max_length=100, verbose_name='¿Name?')
-    book = models.ForeignKey(Book)
+    book = models.ForeignKey(Book, models.CASCADE)
 
     def __str__(self):
         return self.name
@@ -80,7 +80,7 @@ class Promo(models.Model):
 class Chapter(models.Model):
     title = models.CharField(max_length=100, verbose_name='¿Title?')
     content = models.TextField()
-    book = models.ForeignKey(Book)
+    book = models.ForeignKey(Book, models.CASCADE)
 
     def __str__(self):
         return self.title
@@ -92,7 +92,7 @@ class Chapter(models.Model):
 
 @python_2_unicode_compatible
 class ChapterXtra1(models.Model):
-    chap = models.OneToOneField(Chapter, verbose_name='¿Chap?')
+    chap = models.OneToOneField(Chapter, models.CASCADE, verbose_name='¿Chap?')
     xtra = models.CharField(max_length=100, verbose_name='¿Xtra?')
 
     def __str__(self):
@@ -101,7 +101,7 @@ class ChapterXtra1(models.Model):
 
 @python_2_unicode_compatible
 class ChapterXtra2(models.Model):
-    chap = models.OneToOneField(Chapter, verbose_name='¿Chap?')
+    chap = models.OneToOneField(Chapter, models.CASCADE, verbose_name='¿Chap?')
     xtra = models.CharField(max_length=100, verbose_name='¿Xtra?')
 
     def __str__(self):
@@ -146,7 +146,7 @@ class Color2(Color):
 @python_2_unicode_compatible
 class Thing(models.Model):
     title = models.CharField(max_length=20)
-    color = models.ForeignKey(Color, limit_choices_to={'warm': True})
+    color = models.ForeignKey(Color, models.CASCADE, limit_choices_to={'warm': True})
     pub_date = models.DateField(blank=True, null=True)
 
     def __str__(self):
@@ -166,7 +166,7 @@ class Actor(models.Model):
 @python_2_unicode_compatible
 class Inquisition(models.Model):
     expected = models.BooleanField(default=False)
-    leader = models.ForeignKey(Actor)
+    leader = models.ForeignKey(Actor, models.CASCADE)
     country = models.CharField(max_length=20)
 
     def __str__(self):
@@ -176,12 +176,27 @@ class Inquisition(models.Model):
 @python_2_unicode_compatible
 class Sketch(models.Model):
     title = models.CharField(max_length=100)
-    inquisition = models.ForeignKey(Inquisition, limit_choices_to={'leader__name': 'Palin',
-                                                                   'leader__age': 27,
-                                                                   'expected': False,
-                                                                   })
-    defendant0 = models.ForeignKey(Actor, limit_choices_to={'title__isnull': False}, related_name='as_defendant0')
-    defendant1 = models.ForeignKey(Actor, limit_choices_to={'title__isnull': True}, related_name='as_defendant1')
+    inquisition = models.ForeignKey(
+        Inquisition,
+        models.CASCADE,
+        limit_choices_to={
+            'leader__name': 'Palin',
+            'leader__age': 27,
+            'expected': False,
+        },
+    )
+    defendant0 = models.ForeignKey(
+        Actor,
+        models.CASCADE,
+        limit_choices_to={'title__isnull': False},
+        related_name='as_defendant0',
+    )
+    defendant1 = models.ForeignKey(
+        Actor,
+        models.CASCADE,
+        limit_choices_to={'title__isnull': True},
+        related_name='as_defendant1',
+    )
 
     def __str__(self):
         return self.title
@@ -207,7 +222,12 @@ class Character(models.Model):
 @python_2_unicode_compatible
 class StumpJoke(models.Model):
     variation = models.CharField(max_length=100)
-    most_recently_fooled = models.ForeignKey(Character, limit_choices_to=today_callable_dict, related_name="+")
+    most_recently_fooled = models.ForeignKey(
+        Character,
+        models.CASCADE,
+        limit_choices_to=today_callable_dict,
+        related_name="+",
+    )
     has_fooled_today = models.ManyToManyField(Character, limit_choices_to=today_callable_q, related_name="+")
 
     def __str__(self):
@@ -259,7 +279,7 @@ class Account(models.Model):
     types of accounts.
     """
     username = models.CharField(blank=False, max_length=80)
-    persona = models.ForeignKey(Persona, related_name="accounts")
+    persona = models.ForeignKey(Persona, models.CASCADE, related_name="accounts")
     servicename = 'generic service'
 
     def __str__(self):
@@ -305,7 +325,7 @@ class Podcast(Media):
 
 
 class Vodcast(Media):
-    media = models.OneToOneField(Media, primary_key=True, parent_link=True)
+    media = models.OneToOneField(Media, models.CASCADE, primary_key=True, parent_link=True)
     released = models.BooleanField(default=False)
 
 
@@ -318,7 +338,7 @@ class Parent(models.Model):
 
 
 class Child(models.Model):
-    parent = models.ForeignKey(Parent, editable=False)
+    parent = models.ForeignKey(Parent, models.CASCADE, editable=False)
     name = models.CharField(max_length=30, blank=True)
 
     def clean(self):
@@ -343,7 +363,7 @@ class Gallery(models.Model):
 class Picture(models.Model):
     name = models.CharField(max_length=100)
     image = models.FileField(storage=temp_storage, upload_to='test_upload')
-    gallery = models.ForeignKey(Gallery, related_name="pictures")
+    gallery = models.ForeignKey(Gallery, models.CASCADE, related_name="pictures")
 
 
 class Language(models.Model):
@@ -362,7 +382,7 @@ class Title(models.Model):
 
 
 class TitleTranslation(models.Model):
-    title = models.ForeignKey(Title)
+    title = models.ForeignKey(Title, models.CASCADE)
     text = models.CharField(max_length=100)
 
 
@@ -371,7 +391,7 @@ class Recommender(Title):
 
 
 class Recommendation(Title):
-    recommender = models.ForeignKey(Recommender)
+    recommender = models.ForeignKey(Recommender, models.CASCADE)
 
 
 class Collector(models.Model):
@@ -379,25 +399,25 @@ class Collector(models.Model):
 
 
 class Widget(models.Model):
-    owner = models.ForeignKey(Collector)
+    owner = models.ForeignKey(Collector, models.CASCADE)
     name = models.CharField(max_length=100)
 
 
 class DooHickey(models.Model):
     code = models.CharField(max_length=10, primary_key=True)
-    owner = models.ForeignKey(Collector)
+    owner = models.ForeignKey(Collector, models.CASCADE)
     name = models.CharField(max_length=100)
 
 
 class Grommet(models.Model):
     code = models.AutoField(primary_key=True)
-    owner = models.ForeignKey(Collector)
+    owner = models.ForeignKey(Collector, models.CASCADE)
     name = models.CharField(max_length=100)
 
 
 class Whatsit(models.Model):
     index = models.IntegerField(primary_key=True)
-    owner = models.ForeignKey(Collector)
+    owner = models.ForeignKey(Collector, models.CASCADE)
     name = models.CharField(max_length=100)
 
 
@@ -406,13 +426,13 @@ class Doodad(models.Model):
 
 
 class FancyDoodad(Doodad):
-    owner = models.ForeignKey(Collector)
+    owner = models.ForeignKey(Collector, models.CASCADE)
     expensive = models.BooleanField(default=True)
 
 
 @python_2_unicode_compatible
 class Category(models.Model):
-    collector = models.ForeignKey(Collector)
+    collector = models.ForeignKey(Collector, models.CASCADE)
     order = models.PositiveIntegerField()
 
     class Meta:
@@ -429,7 +449,7 @@ def link_posted_default():
 class Link(models.Model):
     posted = models.DateField(default=link_posted_default)
     url = models.URLField()
-    post = models.ForeignKey("Post")
+    post = models.ForeignKey("Post", models.CASCADE)
 
 
 class PrePopulatedPost(models.Model):
@@ -439,7 +459,7 @@ class PrePopulatedPost(models.Model):
 
 
 class PrePopulatedSubPost(models.Model):
-    post = models.ForeignKey(PrePopulatedPost)
+    post = models.ForeignKey(PrePopulatedPost, models.CASCADE)
     subtitle = models.CharField(max_length=100)
     subslug = models.SlugField()
 
@@ -488,7 +508,7 @@ class SuperVillain(Villain):
 class FunkyTag(models.Model):
     "Because we all know there's only one real use case for GFKs."
     name = models.CharField(max_length=25)
-    content_type = models.ForeignKey(ContentType)
+    content_type = models.ForeignKey(ContentType, models.CASCADE)
     object_id = models.PositiveIntegerField()
     content_object = GenericForeignKey('content_type', 'object_id')
 
@@ -499,8 +519,8 @@ class FunkyTag(models.Model):
 @python_2_unicode_compatible
 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')
+    team_leader = models.ForeignKey(Villain, models.CASCADE, related_name='lead_plots')
+    contact = models.ForeignKey(Villain, models.CASCADE, related_name='contact_plots')
     tags = GenericRelation(FunkyTag)
 
     def __str__(self):
@@ -510,7 +530,7 @@ class Plot(models.Model):
 @python_2_unicode_compatible
 class PlotDetails(models.Model):
     details = models.CharField(max_length=100)
-    plot = models.OneToOneField(Plot)
+    plot = models.OneToOneField(Plot, models.CASCADE)
 
     def __str__(self):
         return self.details
@@ -520,7 +540,7 @@ class PlotDetails(models.Model):
 class SecretHideout(models.Model):
     """ Secret! Not registered with the admin! """
     location = models.CharField(max_length=100)
-    villain = models.ForeignKey(Villain)
+    villain = models.ForeignKey(Villain, models.CASCADE)
 
     def __str__(self):
         return self.location
@@ -530,7 +550,7 @@ class SecretHideout(models.Model):
 class SuperSecretHideout(models.Model):
     """ Secret! Not registered with the admin! """
     location = models.CharField(max_length=100)
-    supervillain = models.ForeignKey(SuperVillain)
+    supervillain = models.ForeignKey(SuperVillain, models.CASCADE)
 
     def __str__(self):
         return self.location
@@ -539,7 +559,7 @@ class SuperSecretHideout(models.Model):
 @python_2_unicode_compatible
 class CyclicOne(models.Model):
     name = models.CharField(max_length=25)
-    two = models.ForeignKey('CyclicTwo')
+    two = models.ForeignKey('CyclicTwo', models.CASCADE)
 
     def __str__(self):
         return self.name
@@ -548,7 +568,7 @@ class CyclicOne(models.Model):
 @python_2_unicode_compatible
 class CyclicTwo(models.Model):
     name = models.CharField(max_length=25)
-    one = models.ForeignKey(CyclicOne)
+    one = models.ForeignKey(CyclicOne, models.CASCADE)
 
     def __str__(self):
         return self.name
@@ -564,7 +584,7 @@ class Pizza(models.Model):
 
 
 class Album(models.Model):
-    owner = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL)
+    owner = models.ForeignKey(User, models.SET_NULL, null=True, blank=True)
     title = models.CharField(max_length=30)
 
 
@@ -574,7 +594,7 @@ class Employee(Person):
 
 class WorkHour(models.Model):
     datum = models.DateField()
-    employee = models.ForeignKey(Employee)
+    employee = models.ForeignKey(Employee, models.CASCADE)
 
 
 class Question(models.Model):
@@ -583,7 +603,7 @@ class Question(models.Model):
 
 @python_2_unicode_compatible
 class Answer(models.Model):
-    question = models.ForeignKey(Question, on_delete=models.PROTECT)
+    question = models.ForeignKey(Question, models.PROTECT)
     answer = models.CharField(max_length=20)
 
     def __str__(self):
@@ -722,7 +742,7 @@ class MainPrepopulated(models.Model):
 
 
 class RelatedPrepopulated(models.Model):
-    parent = models.ForeignKey(MainPrepopulated)
+    parent = models.ForeignKey(MainPrepopulated, models.CASCADE)
     name = models.CharField(max_length=75)
     pubdate = models.DateField()
     status = models.CharField(
@@ -790,7 +810,7 @@ class DependentChild(models.Model):
     Model that depends on validation of the parent class for one of its
     fields to validate during clean
     """
-    parent = models.ForeignKey(ParentWithDependentChildren)
+    parent = models.ForeignKey(ParentWithDependentChildren, models.CASCADE)
     family_name = models.CharField(max_length=255)
 
 
@@ -824,7 +844,7 @@ class State(models.Model):
 
 
 class City(models.Model):
-    state = models.ForeignKey(State)
+    state = models.ForeignKey(State, models.CASCADE)
     name = models.CharField(max_length=100)
 
     def get_absolute_url(self):
@@ -832,7 +852,7 @@ class City(models.Model):
 
 
 class Restaurant(models.Model):
-    city = models.ForeignKey(City)
+    city = models.ForeignKey(City, models.CASCADE)
     name = models.CharField(max_length=100)
 
     def get_absolute_url(self):
@@ -840,7 +860,7 @@ class Restaurant(models.Model):
 
 
 class Worker(models.Model):
-    work_at = models.ForeignKey(Restaurant)
+    work_at = models.ForeignKey(Restaurant, models.CASCADE)
     name = models.CharField(max_length=50)
     surname = models.CharField(max_length=50)
 
@@ -852,7 +872,10 @@ class ReferencedByParent(models.Model):
 
 class ParentWithFK(models.Model):
     fk = models.ForeignKey(
-        ReferencedByParent, to_field='name', related_name='hidden+',
+        ReferencedByParent,
+        models.CASCADE,
+        to_field='name',
+        related_name='hidden+',
     )
 
 
@@ -867,7 +890,10 @@ class ReferencedByInline(models.Model):
 
 class InlineReference(models.Model):
     fk = models.ForeignKey(
-        ReferencedByInline, to_field='name', related_name='hidden+',
+        ReferencedByInline,
+        models.CASCADE,
+        to_field='name',
+        related_name='hidden+',
     )
 
 
@@ -886,8 +912,8 @@ class Ingredient(models.Model):
 
 
 class RecipeIngredient(models.Model):
-    ingredient = models.ForeignKey(Ingredient, to_field='iname')
-    recipe = models.ForeignKey(Recipe, to_field='rname')
+    ingredient = models.ForeignKey(Ingredient, models.CASCADE, to_field='iname')
+    recipe = models.ForeignKey(Recipe, models.CASCADE, to_field='rname')
 
 
 # Model for #23839

+ 14 - 9
tests/admin_widgets/models.py

@@ -32,7 +32,7 @@ class Band(models.Model):
 
 @python_2_unicode_compatible
 class Album(models.Model):
-    band = models.ForeignKey(Band)
+    band = models.ForeignKey(Band, models.CASCADE)
     name = models.CharField(max_length=100)
     cover_art = models.FileField(upload_to='albums')
     backside_art = MyFileField(upload_to='albums_back', null=True)
@@ -49,7 +49,7 @@ class HiddenInventoryManager(models.Manager):
 @python_2_unicode_compatible
 class Inventory(models.Model):
     barcode = models.PositiveIntegerField(unique=True)
-    parent = models.ForeignKey('self', to_field='barcode', blank=True, null=True)
+    parent = models.ForeignKey('self', models.SET_NULL, to_field='barcode', blank=True, null=True)
     name = models.CharField(blank=False, max_length=20)
     hidden = models.BooleanField(default=False)
 
@@ -62,7 +62,12 @@ class Inventory(models.Model):
 
 
 class Event(models.Model):
-    main_band = models.ForeignKey(Band, limit_choices_to=models.Q(pk__gt=0), related_name='events_main_band_at')
+    main_band = models.ForeignKey(
+        Band,
+        models.CASCADE,
+        limit_choices_to=models.Q(pk__gt=0),
+        related_name='events_main_band_at',
+    )
     supporting_bands = models.ManyToManyField(Band, blank=True, related_name='events_supporting_band_at')
     start_date = models.DateField(blank=True, null=True)
     start_time = models.TimeField(blank=True, null=True)
@@ -73,7 +78,7 @@ class Event(models.Model):
 
 @python_2_unicode_compatible
 class Car(models.Model):
-    owner = models.ForeignKey(User)
+    owner = models.ForeignKey(User, models.CASCADE)
     make = models.CharField(max_length=30)
     model = models.CharField(max_length=30)
 
@@ -85,7 +90,7 @@ class CarTire(models.Model):
     """
     A single car tire. This to test that a user can only select their own cars.
     """
-    car = models.ForeignKey(Car)
+    car = models.ForeignKey(Car, models.CASCADE)
 
 
 class Honeycomb(models.Model):
@@ -98,7 +103,7 @@ class Bee(models.Model):
     (Honeycomb) so the corresponding raw ID widget won't have a magnifying
     glass link to select related honeycomb instances.
     """
-    honeycomb = models.ForeignKey(Honeycomb)
+    honeycomb = models.ForeignKey(Honeycomb, models.CASCADE)
 
 
 class Individual(models.Model):
@@ -108,8 +113,8 @@ class Individual(models.Model):
     related instances (rendering will be called programmatically in this case).
     """
     name = models.CharField(max_length=20)
-    parent = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
-    soulmate = models.ForeignKey('self', null=True, on_delete=models.CASCADE, related_name='soulmates')
+    parent = models.ForeignKey('self', models.SET_NULL, null=True)
+    soulmate = models.ForeignKey('self', models.CASCADE, null=True, related_name='soulmates')
 
 
 class Company(models.Model):
@@ -149,7 +154,7 @@ class School(models.Model):
 
 @python_2_unicode_compatible
 class Profile(models.Model):
-    user = models.ForeignKey('auth.User', 'username')
+    user = models.ForeignKey('auth.User', models.CASCADE, to_field='username')
 
     def __str__(self):
         return self.user.username

+ 2 - 2
tests/aggregation/models.py

@@ -31,8 +31,8 @@ class Book(models.Model):
     rating = models.FloatField()
     price = models.DecimalField(decimal_places=2, max_digits=6)
     authors = models.ManyToManyField(Author)
-    contact = models.ForeignKey(Author, related_name='book_contact_set')
-    publisher = models.ForeignKey(Publisher)
+    contact = models.ForeignKey(Author, models.CASCADE, related_name='book_contact_set')
+    publisher = models.ForeignKey(Publisher, models.CASCADE)
     pubdate = models.DateField()
 
     def __str__(self):

+ 7 - 7
tests/aggregation_regress/models.py

@@ -28,7 +28,7 @@ class Publisher(models.Model):
 
 class ItemTag(models.Model):
     tag = models.CharField(max_length=100)
-    content_type = models.ForeignKey(ContentType)
+    content_type = models.ForeignKey(ContentType, models.CASCADE)
     object_id = models.PositiveIntegerField()
     content_object = GenericForeignKey('content_type', 'object_id')
 
@@ -41,8 +41,8 @@ class Book(models.Model):
     rating = models.FloatField()
     price = models.DecimalField(decimal_places=2, max_digits=6)
     authors = models.ManyToManyField(Author)
-    contact = models.ForeignKey(Author, related_name='book_contact_set')
-    publisher = models.ForeignKey(Publisher)
+    contact = models.ForeignKey(Author, models.CASCADE, related_name='book_contact_set')
+    publisher = models.ForeignKey(Publisher, models.CASCADE)
     pubdate = models.DateField()
     tags = GenericRelation(ItemTag)
 
@@ -72,7 +72,7 @@ class Entries(models.Model):
 
 class Clues(models.Model):
     ID = models.AutoField(primary_key=True)
-    EntryID = models.ForeignKey(Entries, verbose_name='Entry', db_column='Entry ID')
+    EntryID = models.ForeignKey(Entries, models.CASCADE, verbose_name='Entry', db_column='Entry ID')
     Clue = models.CharField(max_length=150)
 
 
@@ -102,10 +102,10 @@ class Bravo(models.Model):
 
 
 class Charlie(models.Model):
-    alfa = models.ForeignKey(Alfa, null=True)
-    bravo = models.ForeignKey(Bravo, null=True)
+    alfa = models.ForeignKey(Alfa, models.SET_NULL, null=True)
+    bravo = models.ForeignKey(Bravo, models.SET_NULL, null=True)
 
 
 class SelfRefFK(models.Model):
     name = models.CharField(max_length=50)
-    parent = models.ForeignKey('self', null=True, blank=True, related_name='children')
+    parent = models.ForeignKey('self', models.SET_NULL, null=True, blank=True, related_name='children')

+ 3 - 3
tests/annotations/models.py

@@ -30,8 +30,8 @@ class Book(models.Model):
     rating = models.FloatField()
     price = models.DecimalField(decimal_places=2, max_digits=6)
     authors = models.ManyToManyField(Author)
-    contact = models.ForeignKey(Author, related_name='book_contact_set')
-    publisher = models.ForeignKey(Publisher)
+    contact = models.ForeignKey(Author, models.CASCADE, related_name='book_contact_set')
+    publisher = models.ForeignKey(Publisher, models.CASCADE)
     pubdate = models.DateField()
 
     def __str__(self):
@@ -65,7 +65,7 @@ class Employee(models.Model):
     first_name = models.CharField(max_length=20)
     manager = models.BooleanField(default=False)
     last_name = models.CharField(max_length=20)
-    store = models.ForeignKey(Store)
+    store = models.ForeignKey(Store, models.CASCADE)
     age = models.IntegerField()
     salary = models.DecimalField(max_digits=8, decimal_places=2)
 

+ 1 - 1
tests/app_loading/not_installed/models.py

@@ -12,7 +12,7 @@ class RelatedModel(models.Model):
     class Meta:
         app_label = 'not_installed'
 
-    not_installed = models.ForeignKey(NotInstalledModel)
+    not_installed = models.ForeignKey(NotInstalledModel, models.CASCADE)
 
 
 class M2MRelatedModel(models.Model):

+ 3 - 3
tests/auth_tests/models/with_foreign_key.py

@@ -18,9 +18,9 @@ class CustomUserWithFKManager(BaseUserManager):
 
 
 class CustomUserWithFK(AbstractBaseUser):
-    username = models.ForeignKey(Email, related_name='primary')
-    email = models.ForeignKey(Email, to_field='email', related_name='secondary')
-    group = models.ForeignKey(Group)
+    username = models.ForeignKey(Email, models.CASCADE, related_name='primary')
+    email = models.ForeignKey(Email, models.CASCADE, to_field='email', related_name='secondary')
+    group = models.ForeignKey(Group, models.CASCADE)
 
     custom_objects = CustomUserWithFKManager()
 

+ 9 - 5
tests/backends/models.py

@@ -44,7 +44,7 @@ class VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ(models.Model):
 
 class Tag(models.Model):
     name = models.CharField(max_length=30)
-    content_type = models.ForeignKey(ContentType, related_name='backend_tags')
+    content_type = models.ForeignKey(ContentType, models.CASCADE, related_name='backend_tags')
     object_id = models.PositiveIntegerField()
     content_object = GenericForeignKey('content_type', 'object_id')
 
@@ -76,9 +76,13 @@ class ReporterProxy(Reporter):
 class Article(models.Model):
     headline = models.CharField(max_length=100)
     pub_date = models.DateField()
-    reporter = models.ForeignKey(Reporter)
-    reporter_proxy = models.ForeignKey(ReporterProxy, null=True,
-                                       related_name='reporter_proxy')
+    reporter = models.ForeignKey(Reporter, models.CASCADE)
+    reporter_proxy = models.ForeignKey(
+        ReporterProxy,
+        models.SET_NULL,
+        null=True,
+        related_name='reporter_proxy',
+    )
 
     def __str__(self):
         return self.headline
@@ -105,7 +109,7 @@ class Object(models.Model):
 
 @python_2_unicode_compatible
 class ObjectReference(models.Model):
-    obj = models.ForeignKey(Object, db_constraint=False)
+    obj = models.ForeignKey(Object, models.CASCADE, db_constraint=False)
 
     def __str__(self):
         return str(self.obj_id)

+ 6 - 2
tests/basic/models.py

@@ -28,8 +28,12 @@ class ArticleSelectOnSave(Article):
 
 @python_2_unicode_compatible
 class SelfRef(models.Model):
-    selfref = models.ForeignKey('self', null=True, blank=True,
-                                related_name='+')
+    selfref = models.ForeignKey(
+        'self',
+        models.SET_NULL,
+        null=True, blank=True,
+        related_name='+',
+    )
 
     def __str__(self):
         # This method intentionally doesn't work for all cases - part

+ 6 - 2
tests/check_framework/tests.py

@@ -282,8 +282,12 @@ class CheckFrameworkReservedNamesTests(IsolateModelsMixin, SimpleTestCase):
             pass
 
         class ModelWithDescriptorCalledCheck(models.Model):
-            check = models.ForeignKey(ModelWithRelatedManagerCalledCheck)
-            article = models.ForeignKey(ModelWithRelatedManagerCalledCheck, related_name='check')
+            check = models.ForeignKey(ModelWithRelatedManagerCalledCheck, models.CASCADE)
+            article = models.ForeignKey(
+                ModelWithRelatedManagerCalledCheck,
+                models.CASCADE,
+                related_name='check',
+            )
 
         errors = checks.run_checks()
         expected = [

+ 1 - 1
tests/contenttypes_tests/models.py

@@ -20,7 +20,7 @@ class Author(models.Model):
 class Article(models.Model):
     title = models.CharField(max_length=100)
     slug = models.SlugField()
-    author = models.ForeignKey(Author)
+    author = models.ForeignKey(Author, models.CASCADE)
     date_created = models.DateTimeField()
 
     def __str__(self):

+ 11 - 11
tests/contenttypes_tests/tests.py

@@ -173,7 +173,7 @@ class GenericForeignKeyTests(IsolatedModelsTestCase):
 
     def test_content_type_field_pointing_to_wrong_model(self):
         class Model(models.Model):
-            content_type = models.ForeignKey('self')  # should point to ContentType
+            content_type = models.ForeignKey('self', models.CASCADE)  # should point to ContentType
             object_id = models.PositiveIntegerField()
             content_object = GenericForeignKey(
                 'content_type', 'object_id')
@@ -191,7 +191,7 @@ class GenericForeignKeyTests(IsolatedModelsTestCase):
 
     def test_missing_object_id_field(self):
         class TaggedItem(models.Model):
-            content_type = models.ForeignKey(ContentType)
+            content_type = models.ForeignKey(ContentType, models.CASCADE)
             # missing object_id field
             content_object = GenericForeignKey()
 
@@ -208,7 +208,7 @@ class GenericForeignKeyTests(IsolatedModelsTestCase):
 
     def test_field_name_ending_with_underscore(self):
         class Model(models.Model):
-            content_type = models.ForeignKey(ContentType)
+            content_type = models.ForeignKey(ContentType, models.CASCADE)
             object_id = models.PositiveIntegerField()
             content_object_ = GenericForeignKey(
                 'content_type', 'object_id')
@@ -242,7 +242,7 @@ class GenericForeignKeyTests(IsolatedModelsTestCase):
         should raise an exception.
         """
         class Model(models.Model):
-            content_type = models.ForeignKey(ContentType, null=True)
+            content_type = models.ForeignKey(ContentType, models.SET_NULL, null=True)
             object_id = models.PositiveIntegerField(null=True)
             content_object = GenericForeignKey('content_type', 'object_id')
 
@@ -271,7 +271,7 @@ class GenericForeignKeyTests(IsolatedModelsTestCase):
             name = models.CharField(max_length=50)
 
         class BandMember(models.Model):
-            band_ct = models.ForeignKey(ContentType)
+            band_ct = models.ForeignKey(ContentType, models.CASCADE)
             band_id = models.PositiveIntegerField()
             band = UnsavedGenericForeignKey('band_ct', 'band_id')
             first_name = models.CharField(max_length=50)
@@ -289,7 +289,7 @@ class GenericRelationshipTests(IsolatedModelsTestCase):
 
     def test_valid_generic_relationship(self):
         class TaggedItem(models.Model):
-            content_type = models.ForeignKey(ContentType)
+            content_type = models.ForeignKey(ContentType, models.CASCADE)
             object_id = models.PositiveIntegerField()
             content_object = GenericForeignKey()
 
@@ -301,7 +301,7 @@ class GenericRelationshipTests(IsolatedModelsTestCase):
 
     def test_valid_generic_relationship_with_explicit_fields(self):
         class TaggedItem(models.Model):
-            custom_content_type = models.ForeignKey(ContentType)
+            custom_content_type = models.ForeignKey(ContentType, models.CASCADE)
             custom_object_id = models.PositiveIntegerField()
             content_object = GenericForeignKey(
                 'custom_content_type', 'custom_object_id')
@@ -333,7 +333,7 @@ class GenericRelationshipTests(IsolatedModelsTestCase):
     def test_valid_self_referential_generic_relationship(self):
         class Model(models.Model):
             rel = GenericRelation('Model')
-            content_type = models.ForeignKey(ContentType)
+            content_type = models.ForeignKey(ContentType, models.CASCADE)
             object_id = models.PositiveIntegerField()
             content_object = GenericForeignKey(
                 'content_type', 'object_id')
@@ -343,7 +343,7 @@ class GenericRelationshipTests(IsolatedModelsTestCase):
 
     def test_missing_generic_foreign_key(self):
         class TaggedItem(models.Model):
-            content_type = models.ForeignKey(ContentType)
+            content_type = models.ForeignKey(ContentType, models.CASCADE)
             object_id = models.PositiveIntegerField()
 
         class Bookmark(models.Model):
@@ -368,7 +368,7 @@ class GenericRelationshipTests(IsolatedModelsTestCase):
             pass
 
         class SwappedModel(models.Model):
-            content_type = models.ForeignKey(ContentType)
+            content_type = models.ForeignKey(ContentType, models.CASCADE)
             object_id = models.PositiveIntegerField()
             content_object = GenericForeignKey()
 
@@ -393,7 +393,7 @@ class GenericRelationshipTests(IsolatedModelsTestCase):
 
     def test_field_name_ending_with_underscore(self):
         class TaggedItem(models.Model):
-            content_type = models.ForeignKey(ContentType)
+            content_type = models.ForeignKey(ContentType, models.CASCADE)
             object_id = models.PositiveIntegerField()
             content_object = GenericForeignKey()
 

+ 7 - 1
tests/custom_columns/models.py

@@ -40,7 +40,13 @@ class Article(models.Model):
     Article_ID = models.AutoField(primary_key=True, db_column='Article ID')
     headline = models.CharField(max_length=100)
     authors = models.ManyToManyField(Author, db_table='my_m2m_table')
-    primary_author = models.ForeignKey(Author, db_column='Author ID', related_name='primary_set', null=True)
+    primary_author = models.ForeignKey(
+        Author,
+        models.SET_NULL,
+        db_column='Author ID',
+        related_name='primary_set',
+        null=True,
+    )
 
     def __str__(self):
         return self.headline

+ 11 - 6
tests/custom_managers/models.py

@@ -94,8 +94,8 @@ class Person(models.Model):
     last_name = models.CharField(max_length=30)
     fun = models.BooleanField(default=False)
 
-    favorite_book = models.ForeignKey('Book', null=True, related_name='favorite_books')
-    favorite_thing_type = models.ForeignKey('contenttypes.ContentType', null=True)
+    favorite_book = models.ForeignKey('Book', models.SET_NULL, null=True, related_name='favorite_books')
+    favorite_thing_type = models.ForeignKey('contenttypes.ContentType', models.SET_NULL, null=True)
     favorite_thing_id = models.IntegerField(null=True)
     favorite_thing = GenericForeignKey('favorite_thing_type', 'favorite_thing_id')
 
@@ -117,8 +117,13 @@ class FunPerson(models.Model):
     last_name = models.CharField(max_length=30)
     fun = models.BooleanField(default=True)
 
-    favorite_book = models.ForeignKey('Book', null=True, related_name='fun_people_favorite_books')
-    favorite_thing_type = models.ForeignKey('contenttypes.ContentType', null=True)
+    favorite_book = models.ForeignKey(
+        'Book',
+        models.SET_NULL,
+        null=True,
+        related_name='fun_people_favorite_books',
+    )
+    favorite_thing_type = models.ForeignKey('contenttypes.ContentType', models.SET_NULL, null=True)
     favorite_thing_id = models.IntegerField(null=True)
     favorite_thing = GenericForeignKey('favorite_thing_type', 'favorite_thing_id')
 
@@ -181,7 +186,7 @@ class RelatedModel(models.Model):
 class RestrictedModel(models.Model):
     name = models.CharField(max_length=50)
     is_public = models.BooleanField(default=False)
-    related = models.ForeignKey(RelatedModel)
+    related = models.ForeignKey(RelatedModel, models.CASCADE)
 
     objects = RestrictedManager()
     plain_manager = models.Manager()
@@ -194,7 +199,7 @@ class RestrictedModel(models.Model):
 class OneToOneRestrictedModel(models.Model):
     name = models.CharField(max_length=50)
     is_public = models.BooleanField(default=False)
-    related = models.OneToOneField(RelatedModel)
+    related = models.OneToOneField(RelatedModel, models.CASCADE)
 
     objects = RestrictedManager()
     plain_manager = models.Manager()

+ 1 - 1
tests/custom_pk/models.py

@@ -48,4 +48,4 @@ class Bar(models.Model):
 
 
 class Foo(models.Model):
-    bar = models.ForeignKey(Bar)
+    bar = models.ForeignKey(Bar, models.CASCADE)

+ 1 - 1
tests/dates/models.py

@@ -17,7 +17,7 @@ class Article(models.Model):
 
 @python_2_unicode_compatible
 class Comment(models.Model):
-    article = models.ForeignKey(Article, related_name="comments")
+    article = models.ForeignKey(Article, models.CASCADE, related_name="comments")
     text = models.TextField()
     pub_date = models.DateField()
     approval_date = models.DateField(null=True)

+ 1 - 1
tests/datetimes/models.py

@@ -17,7 +17,7 @@ class Article(models.Model):
 
 @python_2_unicode_compatible
 class Comment(models.Model):
-    article = models.ForeignKey(Article, related_name="comments")
+    article = models.ForeignKey(Article, models.CASCADE, related_name="comments")
     text = models.TextField()
     pub_date = models.DateTimeField()
     approval_date = models.DateTimeField(null=True)

+ 1 - 1
tests/db_functions/models.py

@@ -37,7 +37,7 @@ class Article(models.Model):
 class Fan(models.Model):
     name = models.CharField(max_length=50)
     age = models.PositiveSmallIntegerField(default=30)
-    author = models.ForeignKey(Author, related_name='fans')
+    author = models.ForeignKey(Author, models.CASCADE, related_name='fans')
 
     def __str__(self):
         return self.name

+ 1 - 1
tests/defer/models.py

@@ -15,7 +15,7 @@ class Secondary(models.Model):
 class Primary(models.Model):
     name = models.CharField(max_length=50)
     value = models.CharField(max_length=50)
-    related = models.ForeignKey(Secondary)
+    related = models.ForeignKey(Secondary, models.CASCADE)
 
     def __str__(self):
         return self.name

+ 10 - 10
tests/defer_regress/models.py

@@ -18,7 +18,7 @@ class Item(models.Model):
 
 
 class RelatedItem(models.Model):
-    item = models.ForeignKey(Item)
+    item = models.ForeignKey(Item, models.CASCADE)
 
 
 class ProxyRelated(RelatedItem):
@@ -34,8 +34,8 @@ class Child(models.Model):
 @python_2_unicode_compatible
 class Leaf(models.Model):
     name = models.CharField(max_length=10)
-    child = models.ForeignKey(Child)
-    second_child = models.ForeignKey(Child, related_name="other", null=True)
+    child = models.ForeignKey(Child, models.CASCADE)
+    second_child = models.ForeignKey(Child, models.SET_NULL, related_name="other", null=True)
     value = models.IntegerField(default=42)
 
     def __str__(self):
@@ -62,21 +62,21 @@ class SimpleItem(models.Model):
 
 
 class Feature(models.Model):
-    item = models.ForeignKey(SimpleItem)
+    item = models.ForeignKey(SimpleItem, models.CASCADE)
 
 
 class SpecialFeature(models.Model):
-    feature = models.ForeignKey(Feature)
+    feature = models.ForeignKey(Feature, models.CASCADE)
 
 
 class OneToOneItem(models.Model):
-    item = models.OneToOneField(Item, related_name="one_to_one_item")
+    item = models.OneToOneField(Item, models.CASCADE, related_name="one_to_one_item")
     name = models.CharField(max_length=15)
 
 
 class ItemAndSimpleItem(models.Model):
-    item = models.ForeignKey(Item)
-    simple = models.ForeignKey(SimpleItem)
+    item = models.ForeignKey(Item, models.CASCADE)
+    simple = models.ForeignKey(SimpleItem, models.CASCADE)
 
 
 class Profile(models.Model):
@@ -88,8 +88,8 @@ class Location(models.Model):
 
 
 class Request(models.Model):
-    profile = models.ForeignKey(Profile, null=True, blank=True)
-    location = models.ForeignKey(Location)
+    profile = models.ForeignKey(Profile, models.SET_NULL, null=True, blank=True)
+    location = models.ForeignKey(Location, models.CASCADE)
     items = models.ManyToManyField(Item)
 
     request1 = models.CharField(default='request1', max_length=1000)

+ 25 - 25
tests/delete/models.py

@@ -17,15 +17,15 @@ def get_default_r():
 
 
 class S(models.Model):
-    r = models.ForeignKey(R)
+    r = models.ForeignKey(R, models.CASCADE)
 
 
 class T(models.Model):
-    s = models.ForeignKey(S)
+    s = models.ForeignKey(S, models.CASCADE)
 
 
 class U(models.Model):
-    t = models.ForeignKey(T)
+    t = models.ForeignKey(T, models.CASCADE)
 
 
 class RChild(R):
@@ -35,33 +35,33 @@ class RChild(R):
 class A(models.Model):
     name = models.CharField(max_length=30)
 
-    auto = models.ForeignKey(R, related_name="auto_set")
-    auto_nullable = models.ForeignKey(R, null=True,
+    auto = models.ForeignKey(R, models.CASCADE, related_name="auto_set")
+    auto_nullable = models.ForeignKey(R, models.CASCADE, null=True,
         related_name='auto_nullable_set')
-    setvalue = models.ForeignKey(R, on_delete=models.SET(get_default_r),
+    setvalue = models.ForeignKey(R, models.SET(get_default_r),
         related_name='setvalue')
-    setnull = models.ForeignKey(R, on_delete=models.SET_NULL, null=True,
+    setnull = models.ForeignKey(R, models.SET_NULL, null=True,
         related_name='setnull_set')
-    setdefault = models.ForeignKey(R, on_delete=models.SET_DEFAULT,
+    setdefault = models.ForeignKey(R, models.SET_DEFAULT,
         default=get_default_r, related_name='setdefault_set')
-    setdefault_none = models.ForeignKey(R, on_delete=models.SET_DEFAULT,
+    setdefault_none = models.ForeignKey(R, models.SET_DEFAULT,
         default=None, null=True, related_name='setnull_nullable_set')
-    cascade = models.ForeignKey(R, on_delete=models.CASCADE,
+    cascade = models.ForeignKey(R, models.CASCADE,
         related_name='cascade_set')
-    cascade_nullable = models.ForeignKey(R, on_delete=models.CASCADE, null=True,
+    cascade_nullable = models.ForeignKey(R, models.CASCADE, null=True,
         related_name='cascade_nullable_set')
-    protect = models.ForeignKey(R, on_delete=models.PROTECT, null=True)
-    donothing = models.ForeignKey(R, on_delete=models.DO_NOTHING, null=True,
+    protect = models.ForeignKey(R, models.PROTECT, null=True)
+    donothing = models.ForeignKey(R, models.DO_NOTHING, null=True,
         related_name='donothing_set')
-    child = models.ForeignKey(RChild, related_name="child")
-    child_setnull = models.ForeignKey(RChild, on_delete=models.SET_NULL, null=True,
+    child = models.ForeignKey(RChild, models.CASCADE, related_name="child")
+    child_setnull = models.ForeignKey(RChild, models.SET_NULL, null=True,
         related_name="child_setnull")
 
     # A OneToOneField is just a ForeignKey unique=True, so we don't duplicate
     # all the tests; just one smoke test to ensure on_delete works for it as
     # well.
-    o2o_setnull = models.ForeignKey(R, null=True,
-        on_delete=models.SET_NULL, related_name="o2o_nullable_set")
+    o2o_setnull = models.ForeignKey(R, models.SET_NULL, null=True,
+        related_name="o2o_nullable_set")
 
 
 def create_a(name):
@@ -86,13 +86,13 @@ class M(models.Model):
 
 
 class MR(models.Model):
-    m = models.ForeignKey(M)
-    r = models.ForeignKey(R)
+    m = models.ForeignKey(M, models.CASCADE)
+    r = models.ForeignKey(R, models.CASCADE)
 
 
 class MRNull(models.Model):
-    m = models.ForeignKey(M)
-    r = models.ForeignKey(R, null=True, on_delete=models.SET_NULL)
+    m = models.ForeignKey(M, models.CASCADE)
+    r = models.ForeignKey(R, models.SET_NULL, null=True)
 
 
 class Avatar(models.Model):
@@ -100,15 +100,15 @@ class Avatar(models.Model):
 
 
 class User(models.Model):
-    avatar = models.ForeignKey(Avatar, null=True)
+    avatar = models.ForeignKey(Avatar, models.CASCADE, null=True)
 
 
 class HiddenUser(models.Model):
-    r = models.ForeignKey(R, related_name="+")
+    r = models.ForeignKey(R, models.CASCADE, related_name="+")
 
 
 class HiddenUserProfile(models.Model):
-    user = models.ForeignKey(HiddenUser)
+    user = models.ForeignKey(HiddenUser, models.CASCADE)
 
 
 class M2MTo(models.Model):
@@ -132,4 +132,4 @@ class Base(models.Model):
 
 
 class RelToBase(models.Model):
-    base = models.ForeignKey(Base, on_delete=models.DO_NOTHING)
+    base = models.ForeignKey(Base, models.DO_NOTHING)

+ 15 - 15
tests/delete_regress/models.py

@@ -8,12 +8,12 @@ from django.db import models
 class Award(models.Model):
     name = models.CharField(max_length=25)
     object_id = models.PositiveIntegerField()
-    content_type = models.ForeignKey(ContentType)
+    content_type = models.ForeignKey(ContentType, models.CASCADE)
     content_object = GenericForeignKey()
 
 
 class AwardNote(models.Model):
-    award = models.ForeignKey(Award)
+    award = models.ForeignKey(Award, models.CASCADE)
     note = models.CharField(max_length=100)
 
 
@@ -36,13 +36,13 @@ class Child(models.Model):
 
 
 class PlayedWith(models.Model):
-    child = models.ForeignKey(Child)
-    toy = models.ForeignKey(Toy)
+    child = models.ForeignKey(Child, models.CASCADE)
+    toy = models.ForeignKey(Toy, models.CASCADE)
     date = models.DateField(db_column='date_col')
 
 
 class PlayedWithNote(models.Model):
-    played = models.ForeignKey(PlayedWith)
+    played = models.ForeignKey(PlayedWith, models.CASCADE)
     note = models.TextField()
 
 
@@ -63,7 +63,7 @@ class Food(models.Model):
 
 
 class Eaten(models.Model):
-    food = models.ForeignKey(Food, to_field="name")
+    food = models.ForeignKey(Food, models.CASCADE, to_field="name")
     meal = models.CharField(max_length=20)
 
 
@@ -75,16 +75,16 @@ class Policy(models.Model):
 
 
 class Version(models.Model):
-    policy = models.ForeignKey(Policy)
+    policy = models.ForeignKey(Policy, models.CASCADE)
 
 
 class Location(models.Model):
-    version = models.ForeignKey(Version, blank=True, null=True)
+    version = models.ForeignKey(Version, models.SET_NULL, blank=True, null=True)
 
 
 class Item(models.Model):
-    version = models.ForeignKey(Version)
-    location = models.ForeignKey(Location, blank=True, null=True)
+    version = models.ForeignKey(Version, models.CASCADE)
+    location = models.ForeignKey(Location, models.SET_NULL, blank=True, null=True)
 
 # Models for #16128
 
@@ -104,15 +104,15 @@ class Photo(Image):
 
 
 class FooImage(models.Model):
-    my_image = models.ForeignKey(Image)
+    my_image = models.ForeignKey(Image, models.CASCADE)
 
 
 class FooFile(models.Model):
-    my_file = models.ForeignKey(File)
+    my_file = models.ForeignKey(File, models.CASCADE)
 
 
 class FooPhoto(models.Model):
-    my_photo = models.ForeignKey(Photo)
+    my_photo = models.ForeignKey(Photo, models.CASCADE)
 
 
 class FooFileProxy(FooFile):
@@ -126,7 +126,7 @@ class OrgUnit(models.Model):
 
 class Login(models.Model):
     description = models.CharField(max_length=32)
-    orgunit = models.ForeignKey(OrgUnit)
+    orgunit = models.ForeignKey(OrgUnit, models.CASCADE)
 
 
 class House(models.Model):
@@ -135,7 +135,7 @@ class House(models.Model):
 
 class OrderedPerson(models.Model):
     name = models.CharField(max_length=32)
-    lives_in = models.ForeignKey(House)
+    lives_in = models.ForeignKey(House, models.CASCADE)
 
     class Meta:
         ordering = ['name']

+ 16 - 6
tests/distinct_on_fields/models.py

@@ -7,8 +7,13 @@ from django.utils.encoding import python_2_unicode_compatible
 @python_2_unicode_compatible
 class Tag(models.Model):
     name = models.CharField(max_length=10)
-    parent = models.ForeignKey('self', blank=True, null=True,
-            related_name='children')
+    parent = models.ForeignKey(
+        'self',
+        models.SET_NULL,
+        blank=True,
+        null=True,
+        related_name='children',
+    )
 
     class Meta:
         ordering = ['name']
@@ -20,14 +25,19 @@ class Tag(models.Model):
 @python_2_unicode_compatible
 class Celebrity(models.Model):
     name = models.CharField("Name", max_length=20)
-    greatest_fan = models.ForeignKey("Fan", null=True, unique=True)
+    greatest_fan = models.ForeignKey(
+        "Fan",
+        models.SET_NULL,
+        null=True,
+        unique=True,
+    )
 
     def __str__(self):
         return self.name
 
 
 class Fan(models.Model):
-    fan_of = models.ForeignKey(Celebrity)
+    fan_of = models.ForeignKey(Celebrity, models.CASCADE)
 
 
 @python_2_unicode_compatible
@@ -44,8 +54,8 @@ class Staff(models.Model):
 
 @python_2_unicode_compatible
 class StaffTag(models.Model):
-    staff = models.ForeignKey(Staff)
-    tag = models.ForeignKey(Tag)
+    staff = models.ForeignKey(Staff, models.CASCADE)
+    tag = models.ForeignKey(Tag, models.CASCADE)
 
     def __str__(self):
         return "%s -> %s" % (self.tag, self.staff)

+ 2 - 0
tests/expressions/models.py

@@ -25,9 +25,11 @@ class Company(models.Model):
     num_chairs = models.PositiveIntegerField()
     ceo = models.ForeignKey(
         Employee,
+        models.CASCADE,
         related_name='company_ceo_set')
     point_of_contact = models.ForeignKey(
         Employee,
+        models.SET_NULL,
         related_name='company_point_of_contact_set',
         null=True)
 

+ 3 - 3
tests/expressions_case/models.py

@@ -39,7 +39,7 @@ class CaseTestModel(models.Model):
     time = models.TimeField(null=True, db_column='time_field')
     url = models.URLField(default='')
     uuid = models.UUIDField(null=True)
-    fk = models.ForeignKey('self', null=True)
+    fk = models.ForeignKey('self', models.CASCADE, null=True)
 
     def __str__(self):
         return "%i, %s" % (self.integer, self.string)
@@ -47,7 +47,7 @@ class CaseTestModel(models.Model):
 
 @python_2_unicode_compatible
 class O2OCaseTestModel(models.Model):
-    o2o = models.OneToOneField(CaseTestModel, related_name='o2o_rel')
+    o2o = models.OneToOneField(CaseTestModel, models.CASCADE, related_name='o2o_rel')
     integer = models.IntegerField()
 
     def __str__(self):
@@ -56,7 +56,7 @@ class O2OCaseTestModel(models.Model):
 
 @python_2_unicode_compatible
 class FKCaseTestModel(models.Model):
-    fk = models.ForeignKey(CaseTestModel, related_name='fk_rel')
+    fk = models.ForeignKey(CaseTestModel, models.CASCADE, related_name='fk_rel')
     integer = models.IntegerField()
 
     def __str__(self):

+ 2 - 2
tests/extra_regress/models.py

@@ -10,7 +10,7 @@ from django.utils.encoding import python_2_unicode_compatible
 
 @python_2_unicode_compatible
 class RevisionableModel(models.Model):
-    base = models.ForeignKey('self', null=True)
+    base = models.ForeignKey('self', models.SET_NULL, null=True)
     title = models.CharField(blank=True, max_length=255)
     when = models.DateTimeField(default=datetime.datetime.now)
 
@@ -32,7 +32,7 @@ class RevisionableModel(models.Model):
 
 
 class Order(models.Model):
-    created_by = models.ForeignKey(User)
+    created_by = models.ForeignKey(User, models.CASCADE)
     text = models.TextField()
 
 

+ 14 - 14
tests/field_deconstruction/tests.py

@@ -23,7 +23,7 @@ class FieldDeconstructionTests(SimpleTestCase):
         self.assertEqual(name, "is_awesome_test")
         self.assertIsInstance(name, six.text_type)
         # Now try with a ForeignKey
-        field = models.ForeignKey("some_fake.ModelName")
+        field = models.ForeignKey("some_fake.ModelName", models.CASCADE)
         name, path, args, kwargs = field.deconstruct()
         self.assertIsNone(name)
         field.set_attributes_from_name("author")
@@ -177,55 +177,55 @@ class FieldDeconstructionTests(SimpleTestCase):
     def test_foreign_key(self):
         # Test basic pointing
         from django.contrib.auth.models import Permission
-        field = models.ForeignKey("auth.Permission")
+        field = models.ForeignKey("auth.Permission", models.CASCADE)
         field.remote_field.model = Permission
         field.remote_field.field_name = "id"
         name, path, args, kwargs = field.deconstruct()
         self.assertEqual(path, "django.db.models.ForeignKey")
         self.assertEqual(args, [])
-        self.assertEqual(kwargs, {"to": "auth.Permission"})
+        self.assertEqual(kwargs, {"to": "auth.Permission", "on_delete": models.CASCADE})
         self.assertFalse(hasattr(kwargs['to'], "setting_name"))
         # Test swap detection for swappable model
-        field = models.ForeignKey("auth.User")
+        field = models.ForeignKey("auth.User", models.CASCADE)
         name, path, args, kwargs = field.deconstruct()
         self.assertEqual(path, "django.db.models.ForeignKey")
         self.assertEqual(args, [])
-        self.assertEqual(kwargs, {"to": "auth.User"})
+        self.assertEqual(kwargs, {"to": "auth.User", "on_delete": models.CASCADE})
         self.assertEqual(kwargs['to'].setting_name, "AUTH_USER_MODEL")
         # Test nonexistent (for now) model
-        field = models.ForeignKey("something.Else")
+        field = models.ForeignKey("something.Else", models.CASCADE)
         name, path, args, kwargs = field.deconstruct()
         self.assertEqual(path, "django.db.models.ForeignKey")
         self.assertEqual(args, [])
-        self.assertEqual(kwargs, {"to": "something.Else"})
+        self.assertEqual(kwargs, {"to": "something.Else", "on_delete": models.CASCADE})
         # Test on_delete
-        field = models.ForeignKey("auth.User", on_delete=models.SET_NULL)
+        field = models.ForeignKey("auth.User", models.SET_NULL)
         name, path, args, kwargs = field.deconstruct()
         self.assertEqual(path, "django.db.models.ForeignKey")
         self.assertEqual(args, [])
         self.assertEqual(kwargs, {"to": "auth.User", "on_delete": models.SET_NULL})
         # Test to_field preservation
-        field = models.ForeignKey("auth.Permission", to_field="foobar")
+        field = models.ForeignKey("auth.Permission", models.CASCADE, to_field="foobar")
         name, path, args, kwargs = field.deconstruct()
         self.assertEqual(path, "django.db.models.ForeignKey")
         self.assertEqual(args, [])
-        self.assertEqual(kwargs, {"to": "auth.Permission", "to_field": "foobar"})
+        self.assertEqual(kwargs, {"to": "auth.Permission", "to_field": "foobar", "on_delete": models.CASCADE})
         # Test related_name preservation
-        field = models.ForeignKey("auth.Permission", related_name="foobar")
+        field = models.ForeignKey("auth.Permission", models.CASCADE, related_name="foobar")
         name, path, args, kwargs = field.deconstruct()
         self.assertEqual(path, "django.db.models.ForeignKey")
         self.assertEqual(args, [])
-        self.assertEqual(kwargs, {"to": "auth.Permission", "related_name": "foobar"})
+        self.assertEqual(kwargs, {"to": "auth.Permission", "related_name": "foobar", "on_delete": models.CASCADE})
 
     @override_settings(AUTH_USER_MODEL="auth.Permission")
     def test_foreign_key_swapped(self):
         # It doesn't matter that we swapped out user for permission;
         # there's no validation. We just want to check the setting stuff works.
-        field = models.ForeignKey("auth.Permission")
+        field = models.ForeignKey("auth.Permission", models.CASCADE)
         name, path, args, kwargs = field.deconstruct()
         self.assertEqual(path, "django.db.models.ForeignKey")
         self.assertEqual(args, [])
-        self.assertEqual(kwargs, {"to": "auth.Permission"})
+        self.assertEqual(kwargs, {"to": "auth.Permission", "on_delete": models.CASCADE})
         self.assertEqual(kwargs['to'].setting_name, "AUTH_USER_MODEL")
 
     def test_image_field(self):

+ 3 - 3
tests/fixtures/models.py

@@ -42,7 +42,7 @@ class Article(models.Model):
 @python_2_unicode_compatible
 class Blog(models.Model):
     name = models.CharField(max_length=100)
-    featured = models.ForeignKey(Article, related_name='fixtures_featured_set')
+    featured = models.ForeignKey(Article, models.CASCADE, related_name='fixtures_featured_set')
     articles = models.ManyToManyField(Article, blank=True,
                                       related_name='fixtures_articles_set')
 
@@ -53,7 +53,7 @@ class Blog(models.Model):
 @python_2_unicode_compatible
 class Tag(models.Model):
     name = models.CharField(max_length=100)
-    tagged_type = models.ForeignKey(ContentType, related_name="fixtures_tag_set")
+    tagged_type = models.ForeignKey(ContentType, models.CASCADE, related_name="fixtures_tag_set")
     tagged_id = models.PositiveIntegerField(default=0)
     tagged = GenericForeignKey(ct_field='tagged_type', fk_field='tagged_id')
 
@@ -94,7 +94,7 @@ class Spy(Person):
 
 @python_2_unicode_compatible
 class Visa(models.Model):
-    person = models.ForeignKey(Person)
+    person = models.ForeignKey(Person, models.CASCADE)
     permissions = models.ManyToManyField(Permission, blank=True)
 
     def __str__(self):

+ 14 - 14
tests/fixtures_regress/models.py

@@ -31,7 +31,7 @@ class Plant(models.Model):
 @python_2_unicode_compatible
 class Stuff(models.Model):
     name = models.CharField(max_length=20, null=True)
-    owner = models.ForeignKey(User, null=True)
+    owner = models.ForeignKey(User, models.SET_NULL, null=True)
 
     def __str__(self):
         return six.text_type(self.name) + ' is owned by ' + six.text_type(self.owner)
@@ -108,7 +108,7 @@ class TestManager(models.Manager):
 class Store(models.Model):
     objects = TestManager()
     name = models.CharField(max_length=255)
-    main = models.ForeignKey('self', null=True)
+    main = models.ForeignKey('self', models.SET_NULL, null=True)
 
     class Meta:
         ordering = ('name',)
@@ -141,7 +141,7 @@ class Person(models.Model):
 @python_2_unicode_compatible
 class Book(models.Model):
     name = models.CharField(max_length=255)
-    author = models.ForeignKey(Person)
+    author = models.ForeignKey(Person, models.CASCADE)
     stores = models.ManyToManyField(Store)
 
     class Meta:
@@ -175,7 +175,7 @@ class NKChild(Parent):
 @python_2_unicode_compatible
 class RefToNKChild(models.Model):
     text = models.CharField(max_length=10)
-    nk_fk = models.ForeignKey(NKChild, related_name='ref_fks')
+    nk_fk = models.ForeignKey(NKChild, models.CASCADE, related_name='ref_fks')
     nk_m2m = models.ManyToManyField(NKChild, related_name='ref_m2ms')
 
     def __str__(self):
@@ -295,8 +295,8 @@ class M2MComplexB(BaseNKModel):
 
 
 class M2MThroughAB(BaseNKModel):
-    a = models.ForeignKey(M2MComplexA)
-    b = models.ForeignKey(M2MComplexB)
+    a = models.ForeignKey(M2MComplexA, models.CASCADE)
+    b = models.ForeignKey(M2MComplexB, models.CASCADE)
 
 
 class M2MComplexCircular1A(BaseNKModel):
@@ -315,18 +315,18 @@ class M2MComplexCircular1C(BaseNKModel):
 
 
 class M2MCircular1ThroughAB(BaseNKModel):
-    a = models.ForeignKey(M2MComplexCircular1A)
-    b = models.ForeignKey(M2MComplexCircular1B)
+    a = models.ForeignKey(M2MComplexCircular1A, models.CASCADE)
+    b = models.ForeignKey(M2MComplexCircular1B, models.CASCADE)
 
 
 class M2MCircular1ThroughBC(BaseNKModel):
-    b = models.ForeignKey(M2MComplexCircular1B)
-    c = models.ForeignKey(M2MComplexCircular1C)
+    b = models.ForeignKey(M2MComplexCircular1B, models.CASCADE)
+    c = models.ForeignKey(M2MComplexCircular1C, models.CASCADE)
 
 
 class M2MCircular1ThroughCA(BaseNKModel):
-    c = models.ForeignKey(M2MComplexCircular1C)
-    a = models.ForeignKey(M2MComplexCircular1A)
+    c = models.ForeignKey(M2MComplexCircular1C, models.CASCADE)
+    a = models.ForeignKey(M2MComplexCircular1A, models.CASCADE)
 
 
 class M2MComplexCircular2A(BaseNKModel):
@@ -342,5 +342,5 @@ class M2MComplexCircular2B(BaseNKModel):
 
 
 class M2MCircular2ThroughAB(BaseNKModel):
-    a = models.ForeignKey(M2MComplexCircular2A)
-    b = models.ForeignKey(M2MComplexCircular2B)
+    a = models.ForeignKey(M2MComplexCircular2A, models.CASCADE)
+    b = models.ForeignKey(M2MComplexCircular2B, models.CASCADE)

+ 26 - 11
tests/foreign_object/models.py

@@ -24,7 +24,11 @@ class Person(models.Model):
 
     # Relation Fields
     person_country = models.ForeignObject(
-        Country, from_fields=['person_country_id'], to_fields=['id'])
+        Country,
+        from_fields=['person_country_id'],
+        to_fields=['id'],
+        on_delete=models.CASCADE,
+    )
     friends = models.ManyToManyField('self', through='Friendship', symmetrical=False)
 
     class Meta:
@@ -38,7 +42,7 @@ class Person(models.Model):
 class Group(models.Model):
     # Table Column Fields
     name = models.CharField(max_length=128)
-    group_country = models.ForeignKey(Country)
+    group_country = models.ForeignKey(Country, models.CASCADE)
     members = models.ManyToManyField(Person, related_name='groups', through='Membership')
 
     class Meta:
@@ -51,7 +55,7 @@ class Group(models.Model):
 @python_2_unicode_compatible
 class Membership(models.Model):
     # Table Column Fields
-    membership_country = models.ForeignKey(Country)
+    membership_country = models.ForeignKey(Country, models.CASCADE)
     date_joined = models.DateTimeField(default=datetime.datetime.now)
     invite_reason = models.CharField(max_length=64, null=True)
     person_id = models.IntegerField()
@@ -61,11 +65,15 @@ class Membership(models.Model):
     person = models.ForeignObject(
         Person,
         from_fields=['membership_country', 'person_id'],
-        to_fields=['person_country_id', 'id'])
+        to_fields=['person_country_id', 'id'],
+        on_delete=models.CASCADE,
+    )
     group = models.ForeignObject(
         Group,
         from_fields=['membership_country', 'group_id'],
-        to_fields=['group_country', 'id'])
+        to_fields=['group_country', 'id'],
+        on_delete=models.CASCADE,
+    )
 
     class Meta:
         ordering = ('date_joined', 'invite_reason')
@@ -76,7 +84,7 @@ class Membership(models.Model):
 
 class Friendship(models.Model):
     # Table Column Fields
-    from_friend_country = models.ForeignKey(Country, related_name="from_friend_country")
+    from_friend_country = models.ForeignKey(Country, models.CASCADE, related_name="from_friend_country")
     from_friend_id = models.IntegerField()
     to_friend_country_id = models.IntegerField()
     to_friend_id = models.IntegerField()
@@ -84,6 +92,7 @@ class Friendship(models.Model):
     # Relation Fields
     from_friend = models.ForeignObject(
         Person,
+        on_delete=models.CASCADE,
         from_fields=['from_friend_country', 'from_friend_id'],
         to_fields=['person_country_id', 'id'],
         related_name='from_friend')
@@ -92,13 +101,17 @@ class Friendship(models.Model):
         Country,
         from_fields=['to_friend_country_id'],
         to_fields=['id'],
-        related_name='to_friend_country')
+        related_name='to_friend_country',
+        on_delete=models.CASCADE,
+    )
 
     to_friend = models.ForeignObject(
         Person,
         from_fields=['to_friend_country_id', 'to_friend_id'],
         to_fields=['person_country_id', 'id'],
-        related_name='to_friend')
+        related_name='to_friend',
+        on_delete=models.CASCADE,
+    )
 
 
 class ArticleTranslationDescriptor(ReverseSingleRelatedObjectDescriptor):
@@ -148,7 +161,9 @@ class Article(models.Model):
         from_fields=['id'],
         to_fields=['article'],
         related_name='+',
-        null=True)
+        on_delete=models.CASCADE,
+        null=True,
+    )
     pub_date = models.DateField()
 
     def __str__(self):
@@ -163,7 +178,7 @@ class NewsArticle(Article):
 
 
 class ArticleTranslation(models.Model):
-    article = models.ForeignKey(Article)
+    article = models.ForeignKey(Article, models.CASCADE)
     lang = models.CharField(max_length=2)
     title = models.CharField(max_length=100)
     body = models.TextField()
@@ -175,7 +190,7 @@ class ArticleTranslation(models.Model):
 
 
 class ArticleTag(models.Model):
-    article = models.ForeignKey(Article, related_name="tags", related_query_name="tag")
+    article = models.ForeignKey(Article, models.CASCADE, related_name="tags", related_query_name="tag")
     name = models.CharField(max_length=255)
 
 

+ 2 - 0
tests/forms_tests/models.py

@@ -92,11 +92,13 @@ class ChoiceFieldModel(models.Model):
     generation with ModelChoiceField."""
     choice = models.ForeignKey(
         ChoiceOptionModel,
+        models.CASCADE,
         blank=False,
         default=choice_default,
     )
     choice_int = models.ForeignKey(
         ChoiceOptionModel,
+        models.CASCADE,
         blank=False,
         related_name='choice_int',
         default=int_default,

+ 2 - 2
tests/forms_tests/tests/tests.py

@@ -228,7 +228,7 @@ class RelatedModelFormTests(SimpleTestCase):
         Test for issue 10405
         """
         class A(models.Model):
-            ref = models.ForeignKey("B")
+            ref = models.ForeignKey("B", models.CASCADE)
 
         class Meta:
             model = A
@@ -244,7 +244,7 @@ class RelatedModelFormTests(SimpleTestCase):
         Test for issue 10405
         """
         class C(models.Model):
-            ref = models.ForeignKey("D")
+            ref = models.ForeignKey("D", models.CASCADE)
 
         class D(models.Model):
             pass

+ 3 - 3
tests/generic_inline_admin/models.py

@@ -17,7 +17,7 @@ class Media(models.Model):
     """
     Media that can associated to any object.
     """
-    content_type = models.ForeignKey(ContentType)
+    content_type = models.ForeignKey(ContentType, models.CASCADE)
     object_id = models.PositiveIntegerField()
     content_object = GenericForeignKey()
     url = models.URLField()
@@ -36,11 +36,11 @@ class Category(models.Model):
 
 
 class PhoneNumber(models.Model):
-    content_type = models.ForeignKey(ContentType)
+    content_type = models.ForeignKey(ContentType, models.CASCADE)
     object_id = models.PositiveIntegerField()
     content_object = GenericForeignKey('content_type', 'object_id')
     phone_number = models.CharField(max_length=30)
-    category = models.ForeignKey(Category, null=True, blank=True)
+    category = models.ForeignKey(Category, models.SET_NULL, null=True, blank=True)
 
     class Meta:
         unique_together = (('content_type', 'object_id', 'phone_number',),)

+ 6 - 6
tests/generic_relations/models.py

@@ -23,7 +23,7 @@ from django.utils.encoding import python_2_unicode_compatible
 class TaggedItem(models.Model):
     """A tag on an item."""
     tag = models.SlugField()
-    content_type = models.ForeignKey(ContentType)
+    content_type = models.ForeignKey(ContentType, models.CASCADE)
     object_id = models.PositiveIntegerField()
 
     content_object = GenericForeignKey()
@@ -42,7 +42,7 @@ class ValuableTaggedItem(TaggedItem):
 class AbstractComparison(models.Model):
     comparative = models.CharField(max_length=50)
 
-    content_type1 = models.ForeignKey(ContentType, related_name="comparative1_set")
+    content_type1 = models.ForeignKey(ContentType, models.CASCADE, related_name="comparative1_set")
     object_id1 = models.PositiveIntegerField()
 
     first_obj = GenericForeignKey(ct_field="content_type1", fk_field="object_id1")
@@ -54,7 +54,7 @@ class Comparison(AbstractComparison):
     A model that tests having multiple GenericForeignKeys. One is defined
     through an inherited abstract model and one defined directly on this class.
     """
-    content_type2 = models.ForeignKey(ContentType, related_name="comparative2_set")
+    content_type2 = models.ForeignKey(ContentType, models.CASCADE, related_name="comparative2_set")
     object_id2 = models.PositiveIntegerField()
 
     other_obj = GenericForeignKey(ct_field="content_type2", fk_field="object_id2")
@@ -120,14 +120,14 @@ class ManualPK(models.Model):
 
 
 class ForProxyModelModel(models.Model):
-    content_type = models.ForeignKey(ContentType)
+    content_type = models.ForeignKey(ContentType, models.CASCADE)
     object_id = models.PositiveIntegerField()
     obj = GenericForeignKey(for_concrete_model=False)
     title = models.CharField(max_length=255, null=True)
 
 
 class ForConcreteModelModel(models.Model):
-    content_type = models.ForeignKey(ContentType)
+    content_type = models.ForeignKey(ContentType, models.CASCADE)
     object_id = models.PositiveIntegerField()
     obj = GenericForeignKey()
 
@@ -143,6 +143,6 @@ class ProxyRelatedModel(ConcreteRelatedModel):
 
 # To test fix for #7551
 class AllowsNullGFK(models.Model):
-    content_type = models.ForeignKey(ContentType, null=True)
+    content_type = models.ForeignKey(ContentType, models.SET_NULL, null=True)
     object_id = models.PositiveIntegerField(null=True)
     content_object = GenericForeignKey()

+ 11 - 11
tests/generic_relations_regress/models.py

@@ -13,7 +13,7 @@ __all__ = ('Link', 'Place', 'Restaurant', 'Person', 'Address',
 
 @python_2_unicode_compatible
 class Link(models.Model):
-    content_type = models.ForeignKey(ContentType)
+    content_type = models.ForeignKey(ContentType, models.CASCADE)
     object_id = models.PositiveIntegerField()
     content_object = GenericForeignKey()
 
@@ -42,7 +42,7 @@ class Address(models.Model):
     city = models.CharField(max_length=50)
     state = models.CharField(max_length=2)
     zipcode = models.CharField(max_length=5)
-    content_type = models.ForeignKey(ContentType)
+    content_type = models.ForeignKey(ContentType, models.CASCADE)
     object_id = models.PositiveIntegerField()
     content_object = GenericForeignKey()
 
@@ -61,13 +61,13 @@ class Person(models.Model):
 
 
 class CharLink(models.Model):
-    content_type = models.ForeignKey(ContentType)
+    content_type = models.ForeignKey(ContentType, models.CASCADE)
     object_id = models.CharField(max_length=100)
     content_object = GenericForeignKey()
 
 
 class TextLink(models.Model):
-    content_type = models.ForeignKey(ContentType)
+    content_type = models.ForeignKey(ContentType, models.CASCADE)
     object_id = models.TextField()
     content_object = GenericForeignKey()
 
@@ -84,7 +84,7 @@ class OddRelation2(models.Model):
 
 # models for test_q_object_or:
 class Note(models.Model):
-    content_type = models.ForeignKey(ContentType)
+    content_type = models.ForeignKey(ContentType, models.CASCADE)
     object_id = models.PositiveIntegerField()
     content_object = GenericForeignKey()
     note = models.TextField()
@@ -135,7 +135,7 @@ class Guild(models.Model):
 
 
 class Tag(models.Model):
-    content_type = models.ForeignKey(ContentType, related_name='g_r_r_tags')
+    content_type = models.ForeignKey(ContentType, models.CASCADE, related_name='g_r_r_tags')
     object_id = models.CharField(max_length=15)
     content_object = GenericForeignKey()
     label = models.CharField(max_length=15)
@@ -168,7 +168,7 @@ class HasLinkThing(HasLinks):
 
 class A(models.Model):
     flag = models.NullBooleanField()
-    content_type = models.ForeignKey(ContentType)
+    content_type = models.ForeignKey(ContentType, models.CASCADE)
     object_id = models.PositiveIntegerField()
     content_object = GenericForeignKey('content_type', 'object_id')
 
@@ -181,14 +181,14 @@ class B(models.Model):
 
 
 class C(models.Model):
-    b = models.ForeignKey(B)
+    b = models.ForeignKey(B, models.CASCADE)
 
     class Meta:
         ordering = ('id',)
 
 
 class D(models.Model):
-    b = models.ForeignKey(B, null=True)
+    b = models.ForeignKey(B, models.SET_NULL, null=True)
 
     class Meta:
         ordering = ('id',)
@@ -197,14 +197,14 @@ class D(models.Model):
 # Ticket #22998
 
 class Node(models.Model):
-    content_type = models.ForeignKey(ContentType)
+    content_type = models.ForeignKey(ContentType, models.CASCADE)
     object_id = models.PositiveIntegerField()
     content = GenericForeignKey('content_type', 'object_id')
 
 
 class Content(models.Model):
     nodes = GenericRelation(Node)
-    related_obj = models.ForeignKey('Related', on_delete=models.CASCADE)
+    related_obj = models.ForeignKey('Related', models.CASCADE)
 
 
 class Related(models.Model):

+ 7 - 2
tests/get_or_create/models.py

@@ -25,7 +25,7 @@ class ManualPrimaryKeyTest(models.Model):
 
 
 class Profile(models.Model):
-    person = models.ForeignKey(Person, primary_key=True)
+    person = models.ForeignKey(Person, models.CASCADE, primary_key=True)
 
 
 class Tag(models.Model):
@@ -48,4 +48,9 @@ class Author(models.Model):
 class Book(models.Model):
     name = models.CharField(max_length=100)
     authors = models.ManyToManyField(Author, related_name='books')
-    publisher = models.ForeignKey(Publisher, related_name='books', db_column="publisher_id_column")
+    publisher = models.ForeignKey(
+        Publisher,
+        models.CASCADE,
+        related_name='books',
+        db_column="publisher_id_column",
+    )

+ 1 - 1
tests/gis_tests/geoapp/models.py

@@ -57,7 +57,7 @@ class Track(NamedModel):
 
 
 class MultiFields(NamedModel):
-    city = models.ForeignKey(City)
+    city = models.ForeignKey(City, models.CASCADE)
     point = models.PointField()
     poly = models.PolygonField()
 

+ 7 - 2
tests/gis_tests/gis_migrations/migrations/0001_initial.py

@@ -19,7 +19,12 @@ ops = [
         name='Household',
         fields=[
             ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-            ('neighborhood', models.ForeignKey(to='gis_migrations.Neighborhood', to_field='id', null=True)),
+            ('neighborhood', models.ForeignKey(
+                'gis_migrations.Neighborhood',
+                models.SET_NULL,
+                to_field='id',
+                null=True,
+            )),
             ('address', models.CharField(max_length=100)),
             ('zip_code', models.IntegerField(null=True, blank=True)),
             ('geom', gis_models.PointField(srid=4326, geography=True)),
@@ -42,7 +47,7 @@ ops = [
     migrations.AddField(
         model_name='household',
         name='family',
-        field=models.ForeignKey(blank=True, to='gis_migrations.Family', null=True),
+        field=models.ForeignKey('gis_migrations.Family', models.SET_NULL, blank=True, null=True),
         preserve_default=True,
     )
 ]

+ 1 - 1
tests/gis_tests/layermap/models.py

@@ -22,7 +22,7 @@ class State(NamedModel):
 
 
 class County(NamedModel):
-    state = models.ForeignKey(State)
+    state = models.ForeignKey(State, models.CASCADE)
     mpoly = models.MultiPolygonField(srid=4269)  # Multipolygon in NAD83
 
 

+ 5 - 5
tests/gis_tests/relatedapp/models.py

@@ -24,7 +24,7 @@ class Location(SimpleModel):
 class City(SimpleModel):
     name = models.CharField(max_length=50)
     state = models.CharField(max_length=2)
-    location = models.ForeignKey(Location)
+    location = models.ForeignKey(Location, models.CASCADE)
 
     def __str__(self):
         return self.name
@@ -38,13 +38,13 @@ class AugmentedLocation(Location):
 
 class DirectoryEntry(SimpleModel):
     listing_text = models.CharField(max_length=50)
-    location = models.ForeignKey(AugmentedLocation)
+    location = models.ForeignKey(AugmentedLocation, models.CASCADE)
 
 
 @python_2_unicode_compatible
 class Parcel(SimpleModel):
     name = models.CharField(max_length=30)
-    city = models.ForeignKey(City)
+    city = models.ForeignKey(City, models.CASCADE)
     center1 = models.PointField()
     # Throwing a curveball w/`db_column` here.
     center2 = models.PointField(srid=2276, db_column='mycenter')
@@ -63,12 +63,12 @@ class Author(SimpleModel):
 
 class Article(SimpleModel):
     title = models.CharField(max_length=100)
-    author = models.ForeignKey(Author, unique=True)
+    author = models.ForeignKey(Author, models.CASCADE, unique=True)
 
 
 class Book(SimpleModel):
     title = models.CharField(max_length=100)
-    author = models.ForeignKey(Author, related_name='books', null=True)
+    author = models.ForeignKey(Author, models.SET_NULL, related_name='books', null=True)
 
 
 class Event(SimpleModel):

+ 4 - 4
tests/indexes/models.py

@@ -8,17 +8,17 @@ class CurrentTranslation(models.ForeignObject):
     # Avoid validation
     requires_unique_target = False
 
-    def __init__(self, to, from_fields, to_fields, **kwargs):
+    def __init__(self, to, on_delete, from_fields, to_fields, **kwargs):
         # Disable reverse relation
         kwargs['related_name'] = '+'
         # Set unique to enable model cache.
         kwargs['unique'] = True
-        super(CurrentTranslation, self).__init__(to, from_fields, to_fields, **kwargs)
+        super(CurrentTranslation, self).__init__(to, on_delete, from_fields, to_fields, **kwargs)
 
 
 class ArticleTranslation(models.Model):
 
-    article = models.ForeignKey('indexes.Article')
+    article = models.ForeignKey('indexes.Article', models.CASCADE)
     language = models.CharField(max_length=10, unique=True)
     content = models.TextField()
 
@@ -28,7 +28,7 @@ class Article(models.Model):
     pub_date = models.DateTimeField()
 
     # Add virtual relation to the ArticleTranslation model.
-    translation = CurrentTranslation(ArticleTranslation, ['id'], ['article'])
+    translation = CurrentTranslation(ArticleTranslation, models.CASCADE, ['id'], ['article'])
 
     class Meta:
         index_together = [

+ 4 - 4
tests/inline_formsets/models.py

@@ -12,9 +12,9 @@ class Parent(models.Model):
 
 
 class Child(models.Model):
-    mother = models.ForeignKey(Parent, related_name='mothers_children')
-    father = models.ForeignKey(Parent, related_name='fathers_children')
-    school = models.ForeignKey(School)
+    mother = models.ForeignKey(Parent, models.CASCADE, related_name='mothers_children')
+    father = models.ForeignKey(Parent, models.CASCADE, related_name='fathers_children')
+    school = models.ForeignKey(School, models.CASCADE)
     name = models.CharField(max_length=100)
 
 
@@ -28,7 +28,7 @@ class Poet(models.Model):
 
 @python_2_unicode_compatible
 class Poem(models.Model):
-    poet = models.ForeignKey(Poet)
+    poet = models.ForeignKey(Poet, models.CASCADE)
     name = models.CharField(max_length=100)
 
     def __str__(self):

+ 4 - 4
tests/inspectdb/models.py

@@ -6,20 +6,20 @@ from django.db import models
 
 class People(models.Model):
     name = models.CharField(max_length=255)
-    parent = models.ForeignKey('self')
+    parent = models.ForeignKey('self', models.CASCADE)
 
 
 class Message(models.Model):
-    from_field = models.ForeignKey(People, db_column='from_id')
+    from_field = models.ForeignKey(People, models.CASCADE, db_column='from_id')
 
 
 class PeopleData(models.Model):
-    people_pk = models.ForeignKey(People, primary_key=True)
+    people_pk = models.ForeignKey(People, models.CASCADE, primary_key=True)
     ssn = models.CharField(max_length=11)
 
 
 class PeopleMoreData(models.Model):
-    people_unique = models.ForeignKey(People, unique=True)
+    people_unique = models.ForeignKey(People, models.CASCADE, unique=True)
     license = models.CharField(max_length=255)
 
 

+ 18 - 8
tests/inspectdb/tests.py

@@ -144,15 +144,25 @@ class InspectDBTestCase(TestCase):
         output = out.getvalue()
         error_message = "inspectdb generated an attribute name which is a python keyword"
         # Recursive foreign keys should be set to 'self'
-        self.assertIn("parent = models.ForeignKey('self')", output)
-        self.assertNotIn("from = models.ForeignKey(InspectdbPeople)", output, msg=error_message)
+        self.assertIn("parent = models.ForeignKey('self', models.DO_NOTHING)", output)
+        self.assertNotIn(
+            "from = models.ForeignKey(InspectdbPeople, models.DO_NOTHING)",
+            output,
+            msg=error_message,
+        )
         # As InspectdbPeople model is defined after InspectdbMessage, it should be quoted
-        self.assertIn("from_field = models.ForeignKey('InspectdbPeople', db_column='from_id')",
-                      output)
-        self.assertIn("people_pk = models.ForeignKey(InspectdbPeople, primary_key=True)",
-                      output)
-        self.assertIn("people_unique = models.ForeignKey(InspectdbPeople, unique=True)",
-                      output)
+        self.assertIn(
+            "from_field = models.ForeignKey('InspectdbPeople', models.DO_NOTHING, db_column='from_id')",
+            output,
+        )
+        self.assertIn(
+            "people_pk = models.ForeignKey(InspectdbPeople, models.DO_NOTHING, primary_key=True)",
+            output,
+        )
+        self.assertIn(
+            "people_unique = models.ForeignKey(InspectdbPeople, models.DO_NOTHING, unique=True)",
+            output,
+        )
 
     def test_digits_column_name_introspection(self):
         """Introspection of column names consist/start with digits (#16536/#17676)"""

+ 2 - 2
tests/introspection/models.py

@@ -25,8 +25,8 @@ class Article(models.Model):
     headline = models.CharField(max_length=100)
     pub_date = models.DateField()
     body = models.TextField(default='')
-    reporter = models.ForeignKey(Reporter)
-    response_to = models.ForeignKey('self', null=True)
+    reporter = models.ForeignKey(Reporter, models.CASCADE)
+    response_to = models.ForeignKey('self', models.SET_NULL, null=True)
 
     def __str__(self):
         return self.headline

+ 17 - 11
tests/invalid_models_tests/test_models.py

@@ -305,22 +305,28 @@ class FieldNamesTests(IsolatedModelsTestCase):
                 related_name="rn3",
                 through='m2mcomplex'
             )
-            fk = models.ForeignKey(VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, related_name="rn4")
+            fk = models.ForeignKey(
+                VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz,
+                models.CASCADE,
+                related_name="rn4",
+            )
 
         # Models used for setting `through` in M2M field.
         class m2msimple(models.Model):
-            id2 = models.ForeignKey(ModelWithLongField)
+            id2 = models.ForeignKey(ModelWithLongField, models.CASCADE)
 
         class m2mcomplex(models.Model):
-            id2 = models.ForeignKey(ModelWithLongField)
+            id2 = models.ForeignKey(ModelWithLongField, models.CASCADE)
 
         long_field_name = 'a' * (self.max_column_name_length + 1)
         models.ForeignKey(
-            VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
+            VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz,
+            models.CASCADE,
         ).contribute_to_class(m2msimple, long_field_name)
 
         models.ForeignKey(
             VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz,
+            models.CASCADE,
             db_column=long_field_name
         ).contribute_to_class(m2mcomplex, long_field_name)
 
@@ -473,7 +479,7 @@ class ShadowingFieldsTests(IsolatedModelsTestCase):
 
         class Child(Parent):
             # This field clashes with parent "f_id" field.
-            f = models.ForeignKey(Target)
+            f = models.ForeignKey(Target, models.CASCADE)
 
         errors = Child.check()
         expected = [
@@ -517,7 +523,7 @@ class ShadowingFieldsTests(IsolatedModelsTestCase):
             pass
 
         class Model(models.Model):
-            fk = models.ForeignKey(Target)
+            fk = models.ForeignKey(Target, models.CASCADE)
             fk_id = models.IntegerField()
 
         errors = Model.check()
@@ -584,7 +590,7 @@ class OtherModelTests(IsolatedModelsTestCase):
             pass
 
         class Answer(models.Model):
-            question = models.ForeignKey(Question)
+            question = models.ForeignKey(Question, models.CASCADE)
 
             class Meta:
                 order_with_respect_to = 'question'
@@ -596,7 +602,7 @@ class OtherModelTests(IsolatedModelsTestCase):
             pass
 
         class Answer(models.Model):
-            question = models.ForeignKey(Question)
+            question = models.ForeignKey(Question, models.CASCADE)
             order = models.IntegerField()
 
             class Meta:
@@ -678,7 +684,7 @@ class OtherModelTests(IsolatedModelsTestCase):
             pass
 
         class Child(models.Model):
-            parent = models.ForeignKey(Parent)
+            parent = models.ForeignKey(Parent, models.CASCADE)
 
             class Meta:
                 ordering = ("parent_id",)
@@ -731,8 +737,8 @@ class OtherModelTests(IsolatedModelsTestCase):
                 related_name="secondary")
 
         class Membership(models.Model):
-            person = models.ForeignKey(Person)
-            group = models.ForeignKey(Group)
+            person = models.ForeignKey(Person, models.CASCADE)
+            group = models.ForeignKey(Group, models.CASCADE)
 
         errors = Group.check()
         expected = [

+ 152 - 59
tests/invalid_models_tests/test_relative_fields.py

@@ -1,11 +1,16 @@
 # -*- encoding: utf-8 -*-
 from __future__ import unicode_literals
 
+import warnings
+
 from django.core.checks import Error, Warning as DjangoWarning
 from django.db import models
+from django.test import ignore_warnings
 from django.test.testcases import skipIfDBFeature
 from django.test.utils import override_settings
 from django.utils import six
+from django.utils.deprecation import RemovedInDjango20Warning
+from django.utils.version import get_docs_version
 
 from .base import IsolatedModelsTestCase
 
@@ -18,18 +23,100 @@ class RelativeFieldTests(IsolatedModelsTestCase):
             model = models.IntegerField()
 
         class Model(models.Model):
-            field = models.ForeignKey(Target, related_name='+')
+            field = models.ForeignKey(Target, models.CASCADE, related_name='+')
 
         field = Model._meta.get_field('field')
         errors = field.check()
         self.assertEqual(errors, [])
 
+    @ignore_warnings(category=RemovedInDjango20Warning)
+    def test_valid_foreign_key_without_on_delete(self):
+        class Target(models.Model):
+            model = models.IntegerField()
+
+        class Model(models.Model):
+            field = models.ForeignKey(Target, related_name='+')
+
+    def test_foreign_key_without_on_delete_warning(self):
+        with warnings.catch_warnings(record=True) as warns:
+            warnings.simplefilter('always')  # prevent warnings from appearing as errors
+
+            class Target(models.Model):
+                model = models.IntegerField()
+
+            class Model(models.Model):
+                field = models.ForeignKey(Target, related_name='+')
+
+            self.assertEqual(len(warns), 1)
+            self.assertEqual(
+                str(warns[0].message),
+                'on_delete will be a required arg for ForeignKey in Django '
+                '2.0. Set it to models.CASCADE if you want to maintain the '
+                'current default behavior. See '
+                'https://docs.djangoproject.com/en/%s/ref/models/fields/'
+                '#django.db.models.ForeignKey.on_delete' % get_docs_version(),
+            )
+
+    def test_foreign_key_to_field_as_arg(self):
+        with warnings.catch_warnings(record=True) as warns:
+            warnings.simplefilter('always')  # prevent warnings from appearing as errors
+
+            class Target(models.Model):
+                model = models.IntegerField()
+
+            class Model(models.Model):
+                field = models.ForeignKey(Target, 'id')
+
+            self.assertEqual(len(warns), 1)
+            self.assertEqual(
+                str(warns[0].message),
+                "The signature for ForeignKey will change in Django 2.0. "
+                "Pass to_field='id' as a kwarg instead of as an arg."
+            )
+
+    def test_one_to_one_field_without_on_delete_warning(self):
+        with warnings.catch_warnings(record=True) as warns:
+            warnings.simplefilter('always')  # prevent warnings from appearing as errors
+
+            class Target(models.Model):
+                model = models.IntegerField()
+
+            class Model(models.Model):
+                field = models.OneToOneField(Target, related_name='+')
+
+            self.assertEqual(len(warns), 1)
+            self.assertEqual(
+                str(warns[0].message),
+                'on_delete will be a required arg for OneToOneField in Django '
+                '2.0. Set it to models.CASCADE if you want to maintain the '
+                'current default behavior. See '
+                'https://docs.djangoproject.com/en/%s/ref/models/fields/'
+                '#django.db.models.ForeignKey.on_delete' % get_docs_version(),
+            )
+
+    def test_one_to_one_field_to_field_as_arg(self):
+        with warnings.catch_warnings(record=True) as warns:
+            warnings.simplefilter('always')  # prevent warnings from appearing as errors
+
+            class Target(models.Model):
+                model = models.IntegerField()
+
+            class Model(models.Model):
+                field = models.OneToOneField(Target, 'id')
+
+            self.assertEqual(len(warns), 1)
+            self.assertEqual(
+                str(warns[0].message),
+                "The signature for OneToOneField will change in Django 2.0. "
+                "Pass to_field='id' as a kwarg instead of as an arg."
+            )
+
     def test_foreign_key_to_missing_model(self):
         # Model names are resolved when a model is being created, so we cannot
         # test relative fields in isolation and we need to attach them to a
         # model.
         class Model(models.Model):
-            foreign_key = models.ForeignKey('Rel1')
+            foreign_key = models.ForeignKey('Rel1', models.CASCADE)
 
         field = Model._meta.get_field('foreign_key')
         errors = field.check()
@@ -101,9 +188,9 @@ class RelativeFieldTests(IsolatedModelsTestCase):
 
         class AmbiguousRelationship(models.Model):
             # Too much foreign keys to Person.
-            first_person = models.ForeignKey(Person, related_name="first")
-            second_person = models.ForeignKey(Person, related_name="second")
-            second_model = models.ForeignKey(Group)
+            first_person = models.ForeignKey(Person, models.CASCADE, related_name="first")
+            second_person = models.ForeignKey(Person, models.CASCADE, related_name="second")
+            second_model = models.ForeignKey(Group, models.CASCADE)
 
         field = Group._meta.get_field('field')
         errors = field.check(from_model=Group)
@@ -135,8 +222,8 @@ class RelativeFieldTests(IsolatedModelsTestCase):
                 through="InvalidRelationship")
 
         class InvalidRelationship(models.Model):
-            person = models.ForeignKey(Person)
-            wrong_foreign_key = models.ForeignKey(WrongModel)
+            person = models.ForeignKey(Person, models.CASCADE)
+            wrong_foreign_key = models.ForeignKey(WrongModel, models.CASCADE)
             # The last foreign key should point to Group model.
 
         field = Group._meta.get_field('members')
@@ -162,7 +249,7 @@ class RelativeFieldTests(IsolatedModelsTestCase):
                 through="InvalidRelationship")
 
         class InvalidRelationship(models.Model):
-            group = models.ForeignKey(Group)
+            group = models.ForeignKey(Group, models.CASCADE)
             # No foreign key to Person
 
         field = Group._meta.get_field('members')
@@ -206,8 +293,8 @@ class RelativeFieldTests(IsolatedModelsTestCase):
             friends = models.ManyToManyField('self', through="Relationship")
 
         class Relationship(models.Model):
-            first = models.ForeignKey(Person, related_name="rel_from_set")
-            second = models.ForeignKey(Person, related_name="rel_to_set")
+            first = models.ForeignKey(Person, models.CASCADE, related_name="rel_from_set")
+            second = models.ForeignKey(Person, models.CASCADE, related_name="rel_to_set")
 
         field = Person._meta.get_field('friends')
         errors = field.check(from_model=Person)
@@ -227,9 +314,9 @@ class RelativeFieldTests(IsolatedModelsTestCase):
                 through="InvalidRelationship", symmetrical=False)
 
         class InvalidRelationship(models.Model):
-            first = models.ForeignKey(Person, related_name="rel_from_set_2")
-            second = models.ForeignKey(Person, related_name="rel_to_set_2")
-            third = models.ForeignKey(Person, related_name="too_many_by_far")
+            first = models.ForeignKey(Person, models.CASCADE, related_name="rel_from_set_2")
+            second = models.ForeignKey(Person, models.CASCADE, related_name="rel_to_set_2")
+            third = models.ForeignKey(Person, models.CASCADE, related_name="too_many_by_far")
 
         field = Person._meta.get_field('friends')
         errors = field.check(from_model=Person)
@@ -254,8 +341,8 @@ class RelativeFieldTests(IsolatedModelsTestCase):
                 through="Relationship", symmetrical=True)
 
         class Relationship(models.Model):
-            first = models.ForeignKey(Person, related_name="rel_from_set")
-            second = models.ForeignKey(Person, related_name="rel_to_set")
+            first = models.ForeignKey(Person, models.CASCADE, related_name="rel_from_set")
+            second = models.ForeignKey(Person, models.CASCADE, related_name="rel_to_set")
 
         field = Person._meta.get_field('friends')
         errors = field.check(from_model=Person)
@@ -279,9 +366,9 @@ class RelativeFieldTests(IsolatedModelsTestCase):
                 through_fields=('first', 'second'))
 
         class Relationship(models.Model):
-            first = models.ForeignKey(Person, related_name="rel_from_set")
-            second = models.ForeignKey(Person, related_name="rel_to_set")
-            referee = models.ForeignKey(Person, related_name="referred")
+            first = models.ForeignKey(Person, models.CASCADE, related_name="rel_from_set")
+            second = models.ForeignKey(Person, models.CASCADE, related_name="rel_to_set")
+            referee = models.ForeignKey(Person, models.CASCADE, related_name="referred")
 
         field = Person._meta.get_field('friends')
         errors = field.check(from_model=Person)
@@ -297,7 +384,7 @@ class RelativeFieldTests(IsolatedModelsTestCase):
 
     def test_foreign_key_to_abstract_model(self):
         class Model(models.Model):
-            foreign_key = models.ForeignKey('AbstractModel')
+            foreign_key = models.ForeignKey('AbstractModel', models.CASCADE)
 
         class AbstractModel(models.Model):
             class Meta:
@@ -361,7 +448,7 @@ class RelativeFieldTests(IsolatedModelsTestCase):
             bad = models.IntegerField()  # No unique=True
 
         class Model(models.Model):
-            foreign_key = models.ForeignKey('Target', to_field='bad')
+            foreign_key = models.ForeignKey('Target', models.CASCADE, to_field='bad')
 
         field = Model._meta.get_field('foreign_key')
         errors = field.check()
@@ -380,7 +467,7 @@ class RelativeFieldTests(IsolatedModelsTestCase):
             bad = models.IntegerField()
 
         class Model(models.Model):
-            field = models.ForeignKey(Target, to_field='bad')
+            field = models.ForeignKey(Target, models.CASCADE, to_field='bad')
 
         field = Model._meta.get_field('field')
         errors = field.check()
@@ -405,6 +492,7 @@ class RelativeFieldTests(IsolatedModelsTestCase):
             person_city_id = models.IntegerField()
 
             person = models.ForeignObject(Person,
+                on_delete=models.CASCADE,
                 from_fields=['person_country_id', 'person_city_id'],
                 to_fields=['country_id', 'city_id'])
 
@@ -426,8 +514,7 @@ class RelativeFieldTests(IsolatedModelsTestCase):
             pass
 
         class Model(models.Model):
-            foreign_key = models.ForeignKey('Person',
-                on_delete=models.SET_NULL)
+            foreign_key = models.ForeignKey('Person', models.SET_NULL)
 
         field = Model._meta.get_field('foreign_key')
         errors = field.check()
@@ -446,8 +533,7 @@ class RelativeFieldTests(IsolatedModelsTestCase):
             pass
 
         class Model(models.Model):
-            foreign_key = models.ForeignKey('Person',
-                on_delete=models.SET_DEFAULT)
+            foreign_key = models.ForeignKey('Person', models.SET_DEFAULT)
 
         field = Model._meta.get_field('foreign_key')
         errors = field.check()
@@ -487,8 +573,10 @@ class RelativeFieldTests(IsolatedModelsTestCase):
 
         class Model(models.Model):
             explicit_fk = models.ForeignKey(SwappableModel,
+                models.CASCADE,
                 related_name='explicit_fk')
             implicit_fk = models.ForeignKey('invalid_models_tests.SwappableModel',
+                models.CASCADE,
                 related_name='implicit_fk')
             explicit_m2m = models.ManyToManyField(SwappableModel,
                 related_name='explicit_m2m')
@@ -519,8 +607,10 @@ class RelativeFieldTests(IsolatedModelsTestCase):
 
         class Model(models.Model):
             explicit_fk = models.ForeignKey(SwappedModel,
+                models.CASCADE,
                 related_name='explicit_fk')
             implicit_fk = models.ForeignKey('invalid_models_tests.SwappedModel',
+                models.CASCADE,
                 related_name='implicit_fk')
             explicit_m2m = models.ManyToManyField(SwappedModel,
                 related_name='explicit_m2m')
@@ -572,7 +662,7 @@ class RelativeFieldTests(IsolatedModelsTestCase):
 
         for invalid_related_name in invalid_related_names:
             Child = type(str('Child_%s') % str(invalid_related_name), (models.Model,), {
-                'parent': models.ForeignKey('Parent', related_name=invalid_related_name),
+                'parent': models.ForeignKey('Parent', models.CASCADE, related_name=invalid_related_name),
                 '__module__': Parent.__module__,
             })
 
@@ -613,7 +703,7 @@ class RelativeFieldTests(IsolatedModelsTestCase):
 
         for related_name in related_names:
             Child = type(str('Child_%s') % str(related_name), (models.Model,), {
-                'parent': models.ForeignKey('Parent', related_name=related_name),
+                'parent': models.ForeignKey('Parent', models.CASCADE, related_name=related_name),
                 '__module__': Parent.__module__,
             })
 
@@ -626,17 +716,17 @@ class AccessorClashTests(IsolatedModelsTestCase):
     def test_fk_to_integer(self):
         self._test_accessor_clash(
             target=models.IntegerField(),
-            relative=models.ForeignKey('Target'))
+            relative=models.ForeignKey('Target', models.CASCADE))
 
     def test_fk_to_fk(self):
         self._test_accessor_clash(
-            target=models.ForeignKey('Another'),
-            relative=models.ForeignKey('Target'))
+            target=models.ForeignKey('Another', models.CASCADE),
+            relative=models.ForeignKey('Target', models.CASCADE))
 
     def test_fk_to_m2m(self):
         self._test_accessor_clash(
             target=models.ManyToManyField('Another'),
-            relative=models.ForeignKey('Target'))
+            relative=models.ForeignKey('Target', models.CASCADE))
 
     def test_m2m_to_integer(self):
         self._test_accessor_clash(
@@ -645,7 +735,7 @@ class AccessorClashTests(IsolatedModelsTestCase):
 
     def test_m2m_to_fk(self):
         self._test_accessor_clash(
-            target=models.ForeignKey('Another'),
+            target=models.ForeignKey('Another', models.CASCADE),
             relative=models.ManyToManyField('Target'))
 
     def test_m2m_to_m2m(self):
@@ -681,7 +771,7 @@ class AccessorClashTests(IsolatedModelsTestCase):
             pass
 
         class Model(models.Model):
-            foreign = models.ForeignKey(Target)
+            foreign = models.ForeignKey(Target, models.CASCADE)
             m2m = models.ManyToManyField(Target)
 
         errors = Model.check()
@@ -738,17 +828,17 @@ class ReverseQueryNameClashTests(IsolatedModelsTestCase):
     def test_fk_to_integer(self):
         self._test_reverse_query_name_clash(
             target=models.IntegerField(),
-            relative=models.ForeignKey('Target'))
+            relative=models.ForeignKey('Target', models.CASCADE))
 
     def test_fk_to_fk(self):
         self._test_reverse_query_name_clash(
-            target=models.ForeignKey('Another'),
-            relative=models.ForeignKey('Target'))
+            target=models.ForeignKey('Another', models.CASCADE),
+            relative=models.ForeignKey('Target', models.CASCADE))
 
     def test_fk_to_m2m(self):
         self._test_reverse_query_name_clash(
             target=models.ManyToManyField('Another'),
-            relative=models.ForeignKey('Target'))
+            relative=models.ForeignKey('Target', models.CASCADE))
 
     def test_m2m_to_integer(self):
         self._test_reverse_query_name_clash(
@@ -757,7 +847,7 @@ class ReverseQueryNameClashTests(IsolatedModelsTestCase):
 
     def test_m2m_to_fk(self):
         self._test_reverse_query_name_clash(
-            target=models.ForeignKey('Another'),
+            target=models.ForeignKey('Another', models.CASCADE),
             relative=models.ManyToManyField('Target'))
 
     def test_m2m_to_m2m(self):
@@ -794,17 +884,17 @@ class ExplicitRelatedNameClashTests(IsolatedModelsTestCase):
     def test_fk_to_integer(self):
         self._test_explicit_related_name_clash(
             target=models.IntegerField(),
-            relative=models.ForeignKey('Target', related_name='clash'))
+            relative=models.ForeignKey('Target', models.CASCADE, related_name='clash'))
 
     def test_fk_to_fk(self):
         self._test_explicit_related_name_clash(
-            target=models.ForeignKey('Another'),
-            relative=models.ForeignKey('Target', related_name='clash'))
+            target=models.ForeignKey('Another', models.CASCADE),
+            relative=models.ForeignKey('Target', models.CASCADE, related_name='clash'))
 
     def test_fk_to_m2m(self):
         self._test_explicit_related_name_clash(
             target=models.ManyToManyField('Another'),
-            relative=models.ForeignKey('Target', related_name='clash'))
+            relative=models.ForeignKey('Target', models.CASCADE, related_name='clash'))
 
     def test_m2m_to_integer(self):
         self._test_explicit_related_name_clash(
@@ -813,7 +903,7 @@ class ExplicitRelatedNameClashTests(IsolatedModelsTestCase):
 
     def test_m2m_to_fk(self):
         self._test_explicit_related_name_clash(
-            target=models.ForeignKey('Another'),
+            target=models.ForeignKey('Another', models.CASCADE),
             relative=models.ManyToManyField('Target', related_name='clash'))
 
     def test_m2m_to_m2m(self):
@@ -859,18 +949,21 @@ class ExplicitRelatedQueryNameClashTests(IsolatedModelsTestCase):
         self._test_explicit_related_query_name_clash(
             target=models.IntegerField(),
             relative=models.ForeignKey('Target',
+                models.CASCADE,
                 related_query_name='clash'))
 
     def test_fk_to_fk(self):
         self._test_explicit_related_query_name_clash(
-            target=models.ForeignKey('Another'),
+            target=models.ForeignKey('Another', models.CASCADE),
             relative=models.ForeignKey('Target',
+                models.CASCADE,
                 related_query_name='clash'))
 
     def test_fk_to_m2m(self):
         self._test_explicit_related_query_name_clash(
             target=models.ManyToManyField('Another'),
             relative=models.ForeignKey('Target',
+                models.CASCADE,
                 related_query_name='clash'))
 
     def test_m2m_to_integer(self):
@@ -881,7 +974,7 @@ class ExplicitRelatedQueryNameClashTests(IsolatedModelsTestCase):
 
     def test_m2m_to_fk(self):
         self._test_explicit_related_query_name_clash(
-            target=models.ForeignKey('Another'),
+            target=models.ForeignKey('Another', models.CASCADE),
             relative=models.ManyToManyField('Target',
                 related_query_name='clash'))
 
@@ -1013,7 +1106,7 @@ class SelfReferentialFKClashTests(IsolatedModelsTestCase):
 
     def test_accessor_clash(self):
         class Model(models.Model):
-            model_set = models.ForeignKey("Model")
+            model_set = models.ForeignKey("Model", models.CASCADE)
 
         errors = Model.check()
         expected = [
@@ -1030,7 +1123,7 @@ class SelfReferentialFKClashTests(IsolatedModelsTestCase):
 
     def test_reverse_query_name_clash(self):
         class Model(models.Model):
-            model = models.ForeignKey("Model")
+            model = models.ForeignKey("Model", models.CASCADE)
 
         errors = Model.check()
         expected = [
@@ -1048,7 +1141,7 @@ class SelfReferentialFKClashTests(IsolatedModelsTestCase):
     def test_clash_under_explicit_related_name(self):
         class Model(models.Model):
             clash = models.CharField(max_length=10)
-            foreign = models.ForeignKey("Model", related_name='clash')
+            foreign = models.ForeignKey("Model", models.CASCADE, related_name='clash')
 
         errors = Model.check()
         expected = [
@@ -1087,8 +1180,8 @@ class ComplexClashTests(IsolatedModelsTestCase):
         class Model(models.Model):
             src_safe = models.CharField(max_length=10)
 
-            foreign_1 = models.ForeignKey(Target, related_name='id')
-            foreign_2 = models.ForeignKey(Target, related_name='src_safe')
+            foreign_1 = models.ForeignKey(Target, models.CASCADE, related_name='id')
+            foreign_2 = models.ForeignKey(Target, models.CASCADE, related_name='src_safe')
 
             m2m_1 = models.ManyToManyField(Target, related_name='id')
             m2m_2 = models.ManyToManyField(Target, related_name='src_safe')
@@ -1211,9 +1304,9 @@ class M2mThroughFieldsTests(IsolatedModelsTestCase):
             invitees = models.ManyToManyField(Fan, through='Invitation', through_fields=('invitee', 'event'))
 
         class Invitation(models.Model):
-            event = models.ForeignKey(Event)
-            invitee = models.ForeignKey(Fan)
-            inviter = models.ForeignKey(Fan, related_name='+')
+            event = models.ForeignKey(Event, models.CASCADE)
+            invitee = models.ForeignKey(Fan, models.CASCADE)
+            inviter = models.ForeignKey(Fan, models.CASCADE, related_name='+')
 
         field = Event._meta.get_field('invitees')
         errors = field.check(from_model=Event)
@@ -1243,9 +1336,9 @@ class M2mThroughFieldsTests(IsolatedModelsTestCase):
             invitees = models.ManyToManyField(Fan, through='Invitation', through_fields=('invalid_field_1', 'invalid_field_2'))
 
         class Invitation(models.Model):
-            event = models.ForeignKey(Event)
-            invitee = models.ForeignKey(Fan)
-            inviter = models.ForeignKey(Fan, related_name='+')
+            event = models.ForeignKey(Event, models.CASCADE)
+            invitee = models.ForeignKey(Fan, models.CASCADE)
+            inviter = models.ForeignKey(Fan, models.CASCADE, related_name='+')
 
         field = Event._meta.get_field('invitees')
         errors = field.check(from_model=Event)
@@ -1275,9 +1368,9 @@ class M2mThroughFieldsTests(IsolatedModelsTestCase):
             invitees = models.ManyToManyField(Fan, through='Invitation', through_fields=(None, 'invitee'))
 
         class Invitation(models.Model):
-            event = models.ForeignKey(Event)
-            invitee = models.ForeignKey(Fan)
-            inviter = models.ForeignKey(Fan, related_name='+')
+            event = models.ForeignKey(Event, models.CASCADE)
+            invitee = models.ForeignKey(Fan, models.CASCADE)
+            inviter = models.ForeignKey(Fan, models.CASCADE, related_name='+')
 
         field = Event._meta.get_field('invitees')
         errors = field.check(from_model=Event)

+ 3 - 3
tests/known_related_objects/models.py

@@ -17,10 +17,10 @@ class Organiser(models.Model):
 
 class Pool(models.Model):
     name = models.CharField(max_length=30)
-    tournament = models.ForeignKey(Tournament)
-    organiser = models.ForeignKey(Organiser)
+    tournament = models.ForeignKey(Tournament, models.CASCADE)
+    organiser = models.ForeignKey(Organiser, models.CASCADE)
 
 
 class PoolStyle(models.Model):
     name = models.CharField(max_length=30)
-    pool = models.OneToOneField(Pool)
+    pool = models.OneToOneField(Pool, models.CASCADE)

+ 2 - 2
tests/lookup/models.py

@@ -30,7 +30,7 @@ class Author(models.Model):
 class Article(models.Model):
     headline = models.CharField(max_length=100)
     pub_date = models.DateTimeField()
-    author = models.ForeignKey(Author, blank=True, null=True)
+    author = models.ForeignKey(Author, models.SET_NULL, blank=True, null=True)
 
     class Meta:
         ordering = ('-pub_date', 'headline')
@@ -58,7 +58,7 @@ class Season(models.Model):
 
 @python_2_unicode_compatible
 class Game(models.Model):
-    season = models.ForeignKey(Season, related_name='games')
+    season = models.ForeignKey(Season, models.CASCADE, related_name='games')
     home = models.CharField(max_length=100)
     away = models.CharField(max_length=100)
 

+ 1 - 1
tests/m2m_and_m2o/models.py

@@ -18,7 +18,7 @@ class User(models.Model):
 class Issue(models.Model):
     num = models.IntegerField()
     cc = models.ManyToManyField(User, blank=True, related_name='test_issue_cc')
-    client = models.ForeignKey(User, related_name='test_issue_client')
+    client = models.ForeignKey(User, models.CASCADE, related_name='test_issue_client')
 
     def __str__(self):
         return six.text_type(self.num)

+ 2 - 2
tests/m2m_intermediary/models.py

@@ -35,8 +35,8 @@ class Article(models.Model):
 
 @python_2_unicode_compatible
 class Writer(models.Model):
-    reporter = models.ForeignKey(Reporter)
-    article = models.ForeignKey(Article)
+    reporter = models.ForeignKey(Reporter, models.CASCADE)
+    article = models.ForeignKey(Article, models.CASCADE)
     position = models.CharField(max_length=100)
 
     def __str__(self):

+ 22 - 17
tests/m2m_through/models.py

@@ -32,8 +32,8 @@ class Group(models.Model):
 
 @python_2_unicode_compatible
 class Membership(models.Model):
-    person = models.ForeignKey(Person)
-    group = models.ForeignKey(Group)
+    person = models.ForeignKey(Person, models.CASCADE)
+    group = models.ForeignKey(Group, models.CASCADE)
     date_joined = models.DateTimeField(default=datetime.now)
     invite_reason = models.CharField(max_length=64, null=True)
 
@@ -46,9 +46,14 @@ class Membership(models.Model):
 
 @python_2_unicode_compatible
 class CustomMembership(models.Model):
-    person = models.ForeignKey(Person, db_column="custom_person_column", related_name="custom_person_related_name")
-    group = models.ForeignKey(Group)
-    weird_fk = models.ForeignKey(Membership, null=True)
+    person = models.ForeignKey(
+        Person,
+        models.CASCADE,
+        db_column="custom_person_column",
+        related_name="custom_person_related_name",
+    )
+    group = models.ForeignKey(Group, models.CASCADE)
+    weird_fk = models.ForeignKey(Membership, models.SET_NULL, null=True)
     date_joined = models.DateTimeField(default=datetime.now)
 
     def __str__(self):
@@ -59,8 +64,8 @@ class CustomMembership(models.Model):
 
 
 class TestNoDefaultsOrNulls(models.Model):
-    person = models.ForeignKey(Person)
-    group = models.ForeignKey(Group)
+    person = models.ForeignKey(Person, models.CASCADE)
+    group = models.ForeignKey(Group, models.CASCADE)
     nodefaultnonull = models.CharField(max_length=5)
 
 
@@ -74,8 +79,8 @@ class PersonSelfRefM2M(models.Model):
 
 
 class Friendship(models.Model):
-    first = models.ForeignKey(PersonSelfRefM2M, related_name="rel_from_set")
-    second = models.ForeignKey(PersonSelfRefM2M, related_name="rel_to_set")
+    first = models.ForeignKey(PersonSelfRefM2M, models.CASCADE, related_name="rel_from_set")
+    second = models.ForeignKey(PersonSelfRefM2M, models.CASCADE, related_name="rel_to_set")
     date_friended = models.DateTimeField()
 
 
@@ -90,10 +95,10 @@ class Event(models.Model):
 
 
 class Invitation(models.Model):
-    event = models.ForeignKey(Event, related_name='invitations')
+    event = models.ForeignKey(Event, models.CASCADE, related_name='invitations')
     # field order is deliberately inverted. the target field is "invitee".
-    inviter = models.ForeignKey(Person, related_name='invitations_sent')
-    invitee = models.ForeignKey(Person, related_name='invitations')
+    inviter = models.ForeignKey(Person, models.CASCADE, related_name='invitations_sent')
+    invitee = models.ForeignKey(Person, models.CASCADE, related_name='invitations')
 
 
 @python_2_unicode_compatible
@@ -110,9 +115,9 @@ class Employee(models.Model):
 
 class Relationship(models.Model):
     # field order is deliberately inverted.
-    another = models.ForeignKey(Employee, related_name="rel_another_set", null=True)
-    target = models.ForeignKey(Employee, related_name="rel_target_set")
-    source = models.ForeignKey(Employee, related_name="rel_source_set")
+    another = models.ForeignKey(Employee, models.SET_NULL, related_name="rel_another_set", null=True)
+    target = models.ForeignKey(Employee, models.CASCADE, related_name="rel_target_set")
+    source = models.ForeignKey(Employee, models.CASCADE, related_name="rel_source_set")
 
 
 class Ingredient(models.Model):
@@ -133,5 +138,5 @@ class Recipe(models.Model):
 
 
 class RecipeIngredient(models.Model):
-    ingredient = models.ForeignKey(Ingredient, to_field='iname')
-    recipe = models.ForeignKey(Recipe, to_field='rname')
+    ingredient = models.ForeignKey(Ingredient, models.CASCADE, to_field='iname')
+    recipe = models.ForeignKey(Recipe, models.CASCADE, to_field='rname')

+ 8 - 8
tests/m2m_through_regress/models.py

@@ -8,8 +8,8 @@ from django.utils.encoding import python_2_unicode_compatible
 # Forward declared intermediate model
 @python_2_unicode_compatible
 class Membership(models.Model):
-    person = models.ForeignKey('Person')
-    group = models.ForeignKey('Group')
+    person = models.ForeignKey('Person', models.CASCADE)
+    group = models.ForeignKey('Group', models.CASCADE)
     price = models.IntegerField(default=100)
 
     def __str__(self):
@@ -20,8 +20,8 @@ class Membership(models.Model):
 @python_2_unicode_compatible
 class UserMembership(models.Model):
     id = models.AutoField(db_column='usermembership_id', primary_key=True)
-    user = models.ForeignKey(User)
-    group = models.ForeignKey('Group')
+    user = models.ForeignKey(User, models.CASCADE)
+    group = models.ForeignKey('Group', models.CASCADE)
     price = models.IntegerField(default=100)
 
     def __str__(self):
@@ -53,8 +53,8 @@ class A(models.Model):
 
 
 class ThroughBase(models.Model):
-    a = models.ForeignKey(A)
-    b = models.ForeignKey('B')
+    a = models.ForeignKey(A, models.CASCADE)
+    b = models.ForeignKey('B', models.CASCADE)
 
 
 class Through(ThroughBase):
@@ -89,8 +89,8 @@ class Driver(models.Model):
 
 @python_2_unicode_compatible
 class CarDriver(models.Model):
-    car = models.ForeignKey('Car', to_field='make')
-    driver = models.ForeignKey('Driver', to_field='name')
+    car = models.ForeignKey('Car', models.CASCADE, to_field='make')
+    driver = models.ForeignKey('Driver', models.CASCADE, to_field='name')
 
     def __str__(self):
         return "pk=%s car=%s driver=%s" % (str(self.pk), self.car, self.driver)

+ 4 - 4
tests/m2o_recursive/models.py

@@ -2,7 +2,7 @@
 Relating an object to itself, many-to-one
 
 To define a many-to-one relationship between a model and itself, use
-``ForeignKey('self')``.
+``ForeignKey('self', ...)``.
 
 In this example, a ``Category`` is related to itself. That is, each
 ``Category`` has a parent ``Category``.
@@ -17,7 +17,7 @@ from django.utils.encoding import python_2_unicode_compatible
 @python_2_unicode_compatible
 class Category(models.Model):
     name = models.CharField(max_length=20)
-    parent = models.ForeignKey('self', blank=True, null=True, related_name='child_set')
+    parent = models.ForeignKey('self', models.SET_NULL, blank=True, null=True, related_name='child_set')
 
     def __str__(self):
         return self.name
@@ -26,8 +26,8 @@ class Category(models.Model):
 @python_2_unicode_compatible
 class Person(models.Model):
     full_name = models.CharField(max_length=20)
-    mother = models.ForeignKey('self', null=True, related_name='mothers_child_set')
-    father = models.ForeignKey('self', null=True, related_name='fathers_child_set')
+    mother = models.ForeignKey('self', models.SET_NULL, null=True, related_name='mothers_child_set')
+    father = models.ForeignKey('self', models.SET_NULL, null=True, related_name='fathers_child_set')
 
     def __str__(self):
         return self.full_name

+ 2 - 2
tests/managers_regress/models.py

@@ -137,11 +137,11 @@ class RelatedModel(models.Model):
 
 @python_2_unicode_compatible
 class RelationModel(models.Model):
-    fk = models.ForeignKey(RelatedModel, related_name='test_fk')
+    fk = models.ForeignKey(RelatedModel, models.CASCADE, related_name='test_fk')
 
     m2m = models.ManyToManyField(RelatedModel, related_name='test_m2m')
 
-    gfk_ctype = models.ForeignKey(ContentType, null=True)
+    gfk_ctype = models.ForeignKey(ContentType, models.SET_NULL, null=True)
     gfk_id = models.IntegerField(null=True)
     gfk = GenericForeignKey(ct_field='gfk_ctype', fk_field='gfk_id')
 

+ 10 - 10
tests/many_to_one/models.py

@@ -23,7 +23,7 @@ class Reporter(models.Model):
 class Article(models.Model):
     headline = models.CharField(max_length=100)
     pub_date = models.DateField()
-    reporter = models.ForeignKey(Reporter)
+    reporter = models.ForeignKey(Reporter, models.CASCADE)
 
     def __str__(self):
         return self.headline
@@ -40,27 +40,27 @@ class First(models.Model):
 
 
 class Second(models.Model):
-    first = models.ForeignKey(First, related_name='the_first')
+    first = models.ForeignKey(First, models.CASCADE, related_name='the_first')
 
 
 # Protect against repetition of #1839, #2415 and #2536.
 class Third(models.Model):
     name = models.CharField(max_length=20)
-    third = models.ForeignKey('self', null=True, related_name='child_set')
+    third = models.ForeignKey('self', models.SET_NULL, null=True, related_name='child_set')
 
 
 class Parent(models.Model):
     name = models.CharField(max_length=20, unique=True)
-    bestchild = models.ForeignKey('Child', null=True, related_name='favored_by')
+    bestchild = models.ForeignKey('Child', models.SET_NULL, null=True, related_name='favored_by')
 
 
 class Child(models.Model):
     name = models.CharField(max_length=20)
-    parent = models.ForeignKey(Parent)
+    parent = models.ForeignKey(Parent, models.CASCADE)
 
 
 class ToFieldChild(models.Model):
-    parent = models.ForeignKey(Parent, to_field='name')
+    parent = models.ForeignKey(Parent, models.CASCADE, to_field='name')
 
 
 # Multiple paths to the same model (#7110, #7125)
@@ -73,13 +73,13 @@ class Category(models.Model):
 
 
 class Record(models.Model):
-    category = models.ForeignKey(Category)
+    category = models.ForeignKey(Category, models.CASCADE)
 
 
 @python_2_unicode_compatible
 class Relation(models.Model):
-    left = models.ForeignKey(Record, related_name='left_set')
-    right = models.ForeignKey(Record, related_name='right_set')
+    left = models.ForeignKey(Record, models.CASCADE, related_name='left_set')
+    right = models.ForeignKey(Record, models.CASCADE, related_name='right_set')
 
     def __str__(self):
         return "%s - %s" % (self.left.category.name, self.right.category.name)
@@ -97,4 +97,4 @@ class School(models.Model):
 
 
 class Student(models.Model):
-    school = models.ForeignKey(School)
+    school = models.ForeignKey(School, models.CASCADE)

+ 2 - 2
tests/many_to_one/tests.py

@@ -173,7 +173,7 @@ class ManyToOneTests(TestCase):
             name = models.CharField(max_length=50)
 
         class BandMember(models.Model):
-            band = UnsavedForeignKey(Band)
+            band = UnsavedForeignKey(Band, models.CASCADE)
             first_name = models.CharField(max_length=50)
             last_name = models.CharField(max_length=50)
 
@@ -609,7 +609,7 @@ class ManyToOneTests(TestCase):
     def test_fk_instantiation_outside_model(self):
         # Regression for #12190 -- Should be able to instantiate a FK outside
         # of a model, and interrogate its related field.
-        cat = models.ForeignKey(Category)
+        cat = models.ForeignKey(Category, models.CASCADE)
         self.assertEqual('id', cat.remote_field.get_related_field().name)
 
     def test_relation_unsaved(self):

Some files were not shown because too many files changed in this diff