Repository: laymonage/django-jsonfield-backport (language: python)

Issue:
feature_classes and django-salesforce
[django-salesforce](https://github.com/django-salesforce/django-salesforce) is an ORM integration to the Salesforce API.
It gets set up like any other database source with it's backend identifying as [vendor `salesforce`](https://github.com/django-salesforce/django-salesforce/blob/8ca907ca6b5483f2bc7a5abac772df6d648f9def/salesforce/backend/base.py#L43).

This leads to a KeyError in https://github.com/laymonage/django-jsonfield-backport/blob/56679e1999cd7d20bca1a05e6a9d793830bc72e1/src/django_jsonfield_backport/features.py#L65
because the vendor cannot be found in the `feature_classes` map.

What would be the best way to handle unsupported/unknown database vendors so that I can offer a pull request?


Discussion / hints:
Hi, @lociii, thanks for the report and sorry for the late reply. I think we can change that line to something like:

```python
value = feature = getattr(feature_classes.get(connection.vendor), name, False)
```

That should let the value to be `False` as a fallback for database backends not included in `feature_classes`. Feel free to send in a PR if you'd like to. Otherwise, I'd commit the change myself.
On my way!

Fix patch:
diff --git a/src/django_jsonfield_backport/features.py b/src/django_jsonfield_backport/features.py
index f11fd45..720f29a 100644
--- a/src/django_jsonfield_backport/features.py
+++ b/src/django_jsonfield_backport/features.py
@@ -61,6 +61,10 @@ def supports_json_field(self):
 
 
 def extend_features(connection, **kwargs):
+    # Database is from an unknown vendor, we don't know about the features.
+    if connection.vendor not in feature_classes:
+        return
+
     for name in feature_names:
         value = feature = getattr(feature_classes[connection.vendor], name)
         if callable(feature):


Test patch:
diff --git a/tests/test_extend_features.py b/tests/test_extend_features.py
new file mode 100644
index 0000000..3cad2e6
--- /dev/null
+++ b/tests/test_extend_features.py
@@ -0,0 +1,84 @@
+from unittest import mock
+
+from django.db.backends.base.features import BaseDatabaseFeatures
+from django.test import SimpleTestCase
+
+from django_jsonfield_backport.features import extend_features
+
+FIELD_NAME = "this_field_should_never_exist"
+
+
+@mock.patch("django_jsonfield_backport.features.feature_names", [FIELD_NAME])
+class ExtendFeaturesTest(SimpleTestCase):
+    def test_static(self):
+        """
+        Test static field value to be added for known database vendor.
+        """
+
+        class DatabaseConnection:
+            vendor = "postgresql"
+            features = BaseDatabaseFeatures(connection=None)
+
+        connection = DatabaseConnection()
+
+        # Make sure the connection does not have the fake feature yet
+        self.assertIs(hasattr(connection.features, FIELD_NAME), False)
+
+        # Extend the feature set based on the connection vendor
+        with mock.patch(
+            "django_jsonfield_backport.features.PostgresFeatures.%s" % FIELD_NAME,
+            return_value=True,
+            new_callable=mock.PropertyMock,  # Mock a property instead of a method
+            create=True,
+        ):
+            extend_features(connection=connection)
+
+        # Make sure the fake feature has been set now
+        self.assertIs(hasattr(connection.features, FIELD_NAME), True)
+        self.assertIs(getattr(connection.features, FIELD_NAME), True)
+
+    def test_callable(self):
+        """
+        Test callable field value to be added for known database vendor.
+        """
+
+        class DatabaseConnection:
+            vendor = "postgresql"
+            features = BaseDatabaseFeatures(connection=None)
+
+        connection = DatabaseConnection()
+
+        # Make sure the connection does not have the fake feature yet
+        self.assertIs(hasattr(connection.features, FIELD_NAME), False)
+
+        # Extend the feature set based on the connection vendor
+        with mock.patch(
+            "django_jsonfield_backport.features.PostgresFeatures.%s" % FIELD_NAME,
+            return_value=True,  # MagicMock (default) is already a callable object
+            create=True,
+        ):
+            extend_features(connection=connection)
+
+        # Make sure the fake feature has been set now
+        self.assertIs(hasattr(connection.features, FIELD_NAME), True)
+        self.assertIs(getattr(connection.features, FIELD_NAME), True)
+
+    def test_unknown_vendor(self):
+        """
+        Test that nothing gets added for an unknown database vendor.
+        """
+
+        class DatabaseConnection:
+            vendor = "this_vendor_will_never_exist"
+            features = BaseDatabaseFeatures(connection=None)
+
+        connection = DatabaseConnection()
+
+        # Make sure the connection does not have the fake feature yet
+        self.assertIs(hasattr(connection.features, FIELD_NAME), False)
+
+        # Extend the feature set based on the connection vendor
+        extend_features(connection=connection)
+
+        # Make sure the fake feature is still not there
+        self.assertIs(hasattr(connection.features, FIELD_NAME), False)