Repository: mathesar-foundation/mathesar

Issue:
Links do not work after adding check constraint to a table
## Description

It appears that tables with any check constraint (e.g. on an unrelated column) breaks the display of all links on that table.

The foreign key constraint is held by the underlying database, and the links are shown in the link panel on the sidebar. However, in the grid the cells just show the raw value rather than a dropdown.

Another clue: clicking table > advanced > constraints results shows the error 'Unable to fetch table constraints When making an XHR request, the server responded with an error, but the response body was not valid JSON.' Under the hood this corresponds with a 500 request to `/api/db/v0/tables/22/constraints/?limit=500`

<details>
<summary>
500 response body
</summary>

```

<!doctype html>
<html lang="en">
<head>
  <title>Server Error (500)</title>
</head>
<body>
  <h1>Server Error (500)</h1><p></p>
</body>
</html>
```

</details>

## To Reproduce
<!-- How can we recreate this bug? Please try to provide a Minimal, Complete, and Verifiable (http://stackoverflow.com/help/mcve) example if code-related. -->

Steps:
1. Add a link column to a table in Mathesar OR in another tool, add a foreign key constraint and refresh the schema in Mathesar
2. [at this point, if you view the table you should see the link as a dropdown]
3. In another tool, add a constraint to any column in the table
4. View the table in Mathesar: The links continue to display fine
5. Refresh the schema in Mathesar
6. View the table in Mathesar

Expected: The links display as before
Actual: The raw value of the links (e.g. the number for an integer id column) are shown

7. In another tool, remove the constraint
8. View the table in Mathesar: all the data will disappear. Adding a record also seems broken from this point, but under the hood the record is actually added.
9. Refresh the schema in Mathesar
10. View the table in Mathesar: all the data is present and link works correctly again

Video:

This is all mock data. Ignore bit where I try to add person_id manually at the beginning, this is force of habit 😅. Also, I add a record in the external tool but this is not necessary for reproduction (this is just meant to show the check constraint appears to work fine in other tools, and doesn't stop manipulating data there, so I don't think this is a problem with the other tool).

https://github.com/centerofci/mathesar/assets/4953590/044e5e4f-ddbc-4d77-8032-c08375ff9055

## Environment

 - OS: Host: macOS, but mathesar 0.1.2 is running inside a [podman](https://podman.io/) container. I don't think this is relevant though.
 - Browser: Google Chrome
 - Browser Version: 115.0.5790.114 (Official Build) (arm64)
 - Other info: Using postgres:14 container.


Discussion:
@mathemancer tagging you here since you're going to be working on better compatibility with existing DBs.
This appears to be because we're throwing an error when any constraint exists on the table that Mathesar doesn't understand, breaking the constraints endpoint. Thanks for reporting this @domdomegg !

Resolving patch:
diff --git a/mathesar/api/serializers/constraints.py b/mathesar/api/serializers/constraints.py
--- a/mathesar/api/serializers/constraints.py
+++ b/mathesar/api/serializers/constraints.py
@@ -139,6 +139,10 @@ class Meta:
         'foreignkey': ForeignKeyConstraintSerializer,
         'primary': BaseConstraintSerializer,
         'unique': BaseConstraintSerializer,
+        # Even though 'check' & 'exclude' constraints are currently unsupported it's added here
+        # so that the app doesn't break in case these constraints are already present.
+        'check': BaseConstraintSerializer,
+        'exclude': BaseConstraintSerializer
     }
 
     def get_mapping_field(self, data):
@@ -165,7 +169,7 @@ def run_validation(self, data):
                     field='referent_table'
                 )
         constraint_type = data.get('type', None)
-        if constraint_type not in self.serializers_mapping.keys():
+        if constraint_type not in ('foreignkey', 'primary', 'unique'):
             raise UnsupportedConstraintAPIException(constraint_type=constraint_type)
         columns = data.get('columns', None)
         if columns == []:


Test patch:
diff --git a/mathesar/tests/conftest.py b/mathesar/tests/conftest.py
--- a/mathesar/tests/conftest.py
+++ b/mathesar/tests/conftest.py
@@ -99,13 +99,14 @@ def create_dj_db(request):
     def _create_and_add(db_name):
         create_db(db_name)
         add_db_to_dj_settings(db_name)
+        credentials = settings.DATABASES.get(db_name)
         database_model = Database.current_objects.create(
             name=db_name,
             db_name=db_name,
-            username='mathesar',
-            password='mathesar',
-            host='mathesar_dev_db',
-            port=5432
+            username=credentials['USER'],
+            password=credentials['PASSWORD'],
+            host=credentials['HOST'],
+            port=credentials['PORT']
         )
         return database_model
     yield _create_and_add
@@ -128,13 +129,14 @@ def test_db_model(request, test_db_name, django_db_blocker):
 
     add_db_to_dj_settings(test_db_name)
     with django_db_blocker.unblock():
+        credentials = settings.DATABASES.get(test_db_name)
         database_model = Database.current_objects.create(
             name=test_db_name,
             db_name=test_db_name,
-            username='mathesar',
-            password='mathesar',
-            host='mathesar_dev_db',
-            port=5432
+            username=credentials['USER'],
+            password=credentials['PASSWORD'],
+            host=credentials['HOST'],
+            port=credentials['PORT']
         )
     yield database_model
     database_model.delete()