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