Skip to content

Resolve "Prevent fields with duplicate names in the same table"

Closes #508 (closed)

This MR adds the frontend and backend validations which prevents users creating fields with duplicate names, blank names or fields called "id" or "order". It also contains a reversible migration which fixes any existing duplicate/blank/reserved field names.

Testing Notes

  • When editing field names in a table:
    • cannot name a field 'id' or 'order' and you get an appropriate instant error message when you enter it.
    • cannot add a duplicate field name and the error message is also instant and different from the above stating it is a duplicate
  • Can't import a table via CSV,XML,JSON,Paste with duplicate names (though for JSON due to the parser it overwrites duplicate field names already)
  • I checked all templates don't have any duplicate field names (one did which I fixed).
  • To test the migration:
    • I checked out develop
    • ran a custom management command in the patch below which created 10000 users with a table each full of duplicates and reserved names (./baserow make_tables 10000)
    • checked out this branch and ran ./baserow migrate to check how long it might take in a worst case prod migration
    • logged in as one of the users and confirmed the migration worked as expected
    • rolled back to the previous version ./baserow migrate database 0031
    • checked out develop
    • logged in as one of the users and confirmed the old field names are back
    • there is also a new migration unit test testing various cases in baserow/contrib/database/migrations/test_database_migrations.py
Index: backend/src/baserow/contrib/database/management/commands/make_tables.py
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/backend/src/baserow/contrib/database/management/commands/make_tables.py b/backend/src/baserow/contrib/database/management/commands/make_tables.py
new file mode 100644
--- /dev/null	(date 1625134441979)
+++ b/backend/src/baserow/contrib/database/management/commands/make_tables.py	(date 1625134441979)
@@ -0,0 +1,89 @@
+from django.core.management.base import BaseCommand
+
+from baserow.contrib.database.table.handler import TableHandler
+from baserow.core.handler import CoreHandler
+from baserow.core.user.handler import UserHandler
+
+
+class Command(BaseCommand):
+    help = "Fills the database with many users, databases and tables."
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            "limit",
+            type=int,
+            help="Amount of users to be inserted each with a few "
+            "databases and tables.",
+        )
+
+    def handle(self, *args, **options):
+        limit = options["limit"]
+        user_handler = UserHandler()
+        core_handler = CoreHandler()
+        table_handler = TableHandler()
+        self.stdout.write(
+            self.style.NOTICE(
+                f"{limit} users are going to be inserted " f"with a table each."
+            )
+        )
+        for i in range(limit):
+            self.stdout.write(self.style.NOTICE(f"Inserting user {i}"))
+            user = user_handler.create_user(
+                f"User {i}",
+                f"user_{i}@example.com",
+                f"user_" f"{i}",
+            )
+            group_user = core_handler.create_group(user, name=f"user {i} group")
+            database = core_handler.create_application(
+                user, group_user.group, "database", name=f"user {i} database"
+            )
+            headers = [
+                "a",
+                "a",
+                "a",
+                "id",
+                "order",
+                "Order",
+                "non_dupe",
+                "b",
+                "b_2",
+                "test",
+                "Id",
+            ]
+            table_handler.create_table(
+                user,
+                database,
+                data=[headers, list(range(len(headers)))],
+                first_row_header=True,
+                name=f"User {i} table",
+            )
+            headers = [
+                "a",
+                "b",
+                "test",
+                "id",
+                "id",
+                "id",
+                "id_2",
+                "id_3",
+                "id_4",
+                "c",
+                "c",
+                "d",
+                "d",
+                "d",
+                "d",
+                "d_2",
+                "d_3",
+                "d_4",
+                "d_5",
+            ]
+            table_handler.create_table(
+                user,
+                database,
+                data=[headers, list(range(len(headers)))],
+                first_row_header=True,
+                name=f"User {i} second table",
+            )
+
+        self.stdout.write(self.style.SUCCESS(f"{limit} users have been inserted."))
Edited by Nigel Gott

Merge request reports