diff --git a/django/apps/registry.py b/django/apps/registry.py
index 57bdc3edb2..fe53d965de 100644
--- a/django/apps/registry.py
+++ b/django/apps/registry.py
@@ -208,9 +208,17 @@ class Apps(object):
         model_name = model._meta.model_name
         app_models = self.all_models[app_label]
         if model_name in app_models:
-            raise RuntimeError(
-                "Conflicting '%s' models in application '%s': %s and %s." %
-                (model_name, app_label, app_models[model_name], model))
+            if (model.__name__ == app_models[model_name].__name__ and
+                    model.__module__ == app_models[model_name].__module__):
+                warnings.warn(
+                    "Model '%s.%s' was already registered. "
+                    "Reloading models is not advised as it can lead to inconsistencies, "
+                    "most notably with related models." % (model_name, app_label),
+                    RuntimeWarning, stacklevel=2)
+            else:
+                raise RuntimeError(
+                    "Conflicting '%s' models in application '%s': %s and %s." %
+                    (model_name, app_label, app_models[model_name], model))
         app_models[model_name] = model
         self.clear_cache()
 
diff --git a/docs/releases/1.7.2.txt b/docs/releases/1.7.2.txt
index 32500045f0..c25aafba1d 100644
--- a/docs/releases/1.7.2.txt
+++ b/docs/releases/1.7.2.txt
@@ -14,3 +14,7 @@ Bugfixes
 
 * Fixed a migration crash when adding an explicit ``id`` field to a model on
   SQLite (:ticket:`23702`).
+
+* Warn for duplicate models when a module is reloaded. Previously a
+RuntimeError was raised every time two models clashed in the app registry.
+(:ticket:`23621`).
diff --git a/tests/apps/tests.py b/tests/apps/tests.py
index 513ed32039..1a308efb6a 100644
--- a/tests/apps/tests.py
+++ b/tests/apps/tests.py
@@ -3,6 +3,7 @@ from __future__ import unicode_literals
 import os
 import sys
 from unittest import skipUnless
+import warnings
 
 from django.apps import apps, AppConfig
 from django.apps.registry import Apps
@@ -208,6 +209,44 @@ class AppsTests(TestCase):
             apps.get_model("apps", "SouthPonies")
         self.assertEqual(new_apps.get_model("apps", "SouthPonies"), temp_model)
 
+    def test_model_clash(self):
+        """
+        Test for behavior when two models clash in the app registry.
+        """
+        new_apps = Apps(["apps"])
+        meta_contents = {
+            'app_label': "apps",
+            'apps': new_apps,
+        }
+
+        body = {}
+        body['Meta'] = type(str("Meta"), tuple(), meta_contents)
+        body['__module__'] = TotallyNormal.__module__
+        type(str("SouthPonies"), (models.Model,), body)
+
+        # When __name__ and __module__ match we assume the module
+        # was reloaded and issue a warning. This use-case is
+        # useful for REPL. Refs #23621.
+        body = {}
+        body['Meta'] = type(str("Meta"), tuple(), meta_contents)
+        body['__module__'] = TotallyNormal.__module__
+        with warnings.catch_warnings(record=True) as w:
+            type(str("SouthPonies"), (models.Model,), body)
+            self.assertEqual(len(w), 1)
+            self.assertTrue(issubclass(w[-1].category, RuntimeWarning))
+            self.assertEqual(str(w[-1].message),
+                 "Model 'southponies.apps' was already registered. "
+                 "Reloading models is not advised as it can lead to inconsistencies, "
+                 "most notably with related models.")
+
+        # If it doesn't appear to be a reloaded module then we expect
+        # a RuntimeError.
+        body = {}
+        body['Meta'] = type(str("Meta"), tuple(), meta_contents)
+        body['__module__'] = TotallyNormal.__module__ + '.whatever'
+        with six.assertRaisesRegex(self, RuntimeError,
+                "Conflicting 'southponies' models in application 'apps':.*"):
+            type(str("SouthPonies"), (models.Model,), body)
 
 class Stub(object):
     def __init__(self, **kwargs):