From 32c0d823e5316aa7d616a69996919b62748368cc Mon Sep 17 00:00:00 2001
From: Simon Charette <charette.s@gmail.com>
Date: Sat, 10 Sep 2016 19:20:16 -0400
Subject: [PATCH] Used a database feature to prevent the jsonb test model from
 being migrated.

Thanks Tim for the review.
---
 django/db/backends/postgresql/features.py     |  4 +++
 .../migrations/0002_create_test_models.py     | 14 +---------
 tests/postgres_tests/models.py                | 17 +++++-------
 tests/postgres_tests/test_json.py             | 26 +++++--------------
 4 files changed, 19 insertions(+), 42 deletions(-)

diff --git a/django/db/backends/postgresql/features.py b/django/db/backends/postgresql/features.py
index a9e1c77480..6fd6af9088 100644
--- a/django/db/backends/postgresql/features.py
+++ b/django/db/backends/postgresql/features.py
@@ -36,3 +36,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
     @cached_property
     def has_select_for_update_skip_locked(self):
         return self.connection.pg_version >= 90500
+
+    @cached_property
+    def has_jsonb_datatype(self):
+        return self.connection.pg_version >= 90400
diff --git a/tests/postgres_tests/migrations/0002_create_test_models.py b/tests/postgres_tests/migrations/0002_create_test_models.py
index 974aae1ec8..161f883d3f 100644
--- a/tests/postgres_tests/migrations/0002_create_test_models.py
+++ b/tests/postgres_tests/migrations/0002_create_test_models.py
@@ -216,9 +216,6 @@ class Migration(migrations.Migration):
             },
             bases=(models.Model,),
         ),
-    ]
-
-    pg_94_operations = [
         migrations.CreateModel(
             name='JSONModel',
             fields=[
@@ -227,17 +224,8 @@ class Migration(migrations.Migration):
                 ('field_custom', JSONField(null=True, blank=True, encoder=DjangoJSONEncoder)),
             ],
             options={
+                'required_db_features': {'has_jsonb_datatype'},
             },
             bases=(models.Model,),
         ),
     ]
-
-    def apply(self, project_state, schema_editor, collect_sql=False):
-        try:
-            PG_VERSION = schema_editor.connection.pg_version
-        except AttributeError:
-            pass  # We are probably not on PostgreSQL
-        else:
-            if PG_VERSION >= 90400:
-                self.operations = self.operations + self.pg_94_operations
-        return super(Migration, self).apply(project_state, schema_editor, collect_sql)
diff --git a/tests/postgres_tests/models.py b/tests/postgres_tests/models.py
index 52dbc0335c..8913e60869 100644
--- a/tests/postgres_tests/models.py
+++ b/tests/postgres_tests/models.py
@@ -1,5 +1,5 @@
 from django.core.serializers.json import DjangoJSONEncoder
-from django.db import connection, models
+from django.db import models
 
 from .fields import (
     ArrayField, BigIntegerRangeField, DateRangeField, DateTimeRangeField,
@@ -129,15 +129,12 @@ class RangeLookupsModel(PostgreSQLModel):
     date = models.DateField(blank=True, null=True)
 
 
-# Only create this model for postgres >= 9.4
-if connection.vendor == 'postgresql' and connection.pg_version >= 90400:
-    class JSONModel(models.Model):
-        field = JSONField(blank=True, null=True)
-        field_custom = JSONField(blank=True, null=True, encoder=DjangoJSONEncoder)
-else:
-    # create an object with this name so we don't have failing imports
-    class JSONModel(object):
-        pass
+class JSONModel(models.Model):
+    field = JSONField(blank=True, null=True)
+    field_custom = JSONField(blank=True, null=True, encoder=DjangoJSONEncoder)
+
+    class Meta:
+        required_db_features = ['has_jsonb_datatype']
 
 
 class ArrayFieldSubclass(ArrayField):
diff --git a/tests/postgres_tests/test_json.py b/tests/postgres_tests/test_json.py
index aeaae0de34..f306284b7f 100644
--- a/tests/postgres_tests/test_json.py
+++ b/tests/postgres_tests/test_json.py
@@ -1,15 +1,13 @@
 from __future__ import unicode_literals
 
 import datetime
-import unittest
 import uuid
 from decimal import Decimal
 
 from django.core import exceptions, serializers
 from django.core.serializers.json import DjangoJSONEncoder
-from django.db import connection
 from django.forms import CharField, Form, widgets
-from django.test import TestCase
+from django.test import skipUnlessDBFeature
 from django.utils.html import escape
 
 from . import PostgreSQLTestCase
@@ -22,18 +20,8 @@ except ImportError:
     pass
 
 
-def skipUnlessPG94(test):
-    try:
-        PG_VERSION = connection.pg_version
-    except AttributeError:
-        PG_VERSION = 0
-    if PG_VERSION < 90400:
-        return unittest.skip('PostgreSQL >= 9.4 required')(test)
-    return test
-
-
-@skipUnlessPG94
-class TestSaveLoad(TestCase):
+@skipUnlessDBFeature('has_jsonb_datatype')
+class TestSaveLoad(PostgreSQLTestCase):
     def test_null(self):
         instance = JSONModel()
         instance.save()
@@ -106,8 +94,8 @@ class TestSaveLoad(TestCase):
         self.assertEqual(loaded.field_custom, obj_after)
 
 
-@skipUnlessPG94
-class TestQuerying(TestCase):
+@skipUnlessDBFeature('has_jsonb_datatype')
+class TestQuerying(PostgreSQLTestCase):
     @classmethod
     def setUpTestData(cls):
         cls.objs = [
@@ -250,8 +238,8 @@ class TestQuerying(TestCase):
         )
 
 
-@skipUnlessPG94
-class TestSerialization(TestCase):
+@skipUnlessDBFeature('has_jsonb_datatype')
+class TestSerialization(PostgreSQLTestCase):
     test_data = (
         '[{"fields": {"field": {"a": "b", "c": null}, "field_custom": null}, '
         '"model": "postgres_tests.jsonmodel", "pk": null}]'