feat: rename custom field
This commit is contained in:
parent
21881e6652
commit
0d5f28d569
6 changed files with 121 additions and 1 deletions
|
|
@ -25,6 +25,7 @@ frappe.ui.form.on("Custom Field", {
|
|||
frm.toggle_enable("dt", frm.doc.__islocal);
|
||||
frm.trigger("dt");
|
||||
frm.toggle_reqd("label", !frm.doc.fieldname);
|
||||
frm.trigger("add_rename_field");
|
||||
|
||||
if (frm.doc.is_system_generated) {
|
||||
frm.dashboard.add_comment(
|
||||
|
|
@ -110,6 +111,29 @@ frappe.ui.form.on("Custom Field", {
|
|||
frm.fields_dict["options_help"].disp_area.innerHTML = "";
|
||||
}
|
||||
},
|
||||
add_rename_field(frm) {
|
||||
frm.add_custom_button(__("Rename Fieldname"), () => {
|
||||
frappe.prompt(
|
||||
{
|
||||
fieldtype: "Data",
|
||||
label: __("Fieldname"),
|
||||
fieldname: "fieldname",
|
||||
reqd: 1,
|
||||
},
|
||||
function (data) {
|
||||
frappe.call({
|
||||
method: "frappe.custom.doctype.custom_field.custom_field.rename_fieldname",
|
||||
args: {
|
||||
custom_field: frm.doc.name,
|
||||
fieldname: data.fieldname,
|
||||
},
|
||||
});
|
||||
},
|
||||
__("Rename Fieldname"),
|
||||
__("Rename")
|
||||
);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
frappe.utils.has_special_chars = function (t) {
|
||||
|
|
|
|||
|
|
@ -340,3 +340,52 @@ def create_custom_fields(custom_fields: dict, ignore_validate=False, update=True
|
|||
|
||||
finally:
|
||||
frappe.flags.in_create_custom_fields = False
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def rename_fieldname(custom_field: str, fieldname: str):
|
||||
frappe.only_for("System Manager")
|
||||
|
||||
field: CustomField = frappe.get_doc("Custom Field", custom_field)
|
||||
parent_doctype = field.dt
|
||||
old_fieldname = field.fieldname
|
||||
field.fieldname = fieldname
|
||||
field.set_fieldname()
|
||||
new_fieldname = field.fieldname
|
||||
|
||||
if field.is_system_generated:
|
||||
frappe.throw(_("System Generated Fields can not be renamed"))
|
||||
if frappe.db.has_column(parent_doctype, fieldname):
|
||||
frappe.throw(_("Can not rename as fieldname {0} is already present on DocType."))
|
||||
if old_fieldname == new_fieldname:
|
||||
frappe.msgprint(_("Old and new fieldnames are same."), alert=True)
|
||||
return
|
||||
|
||||
frappe.db.rename_column(parent_doctype, old_fieldname, new_fieldname)
|
||||
|
||||
# Update in DB after alter column is successful, alter column will implicitly commit, so it's
|
||||
# best to commit change on field too to avoid any possible mismatch between two.
|
||||
field.db_set("fieldname", field.fieldname, notify=True)
|
||||
_update_fieldname_references(field, old_fieldname, new_fieldname)
|
||||
|
||||
frappe.db.commit()
|
||||
frappe.clear_cache()
|
||||
|
||||
|
||||
def _update_fieldname_references(
|
||||
field: CustomField, old_fieldname: str, new_fieldname: str
|
||||
) -> None:
|
||||
# Passwords are stored in auth table, so column name needs to be updated there.
|
||||
if field.fieldtype == "Password":
|
||||
Auth = frappe.qb.Table("__Auth")
|
||||
frappe.qb.update(Auth).set(Auth.fieldname, new_fieldname).where(
|
||||
(Auth.doctype == field.dt) & (Auth.fieldname == old_fieldname)
|
||||
).run()
|
||||
|
||||
# Update ordering reference.
|
||||
frappe.db.set_value(
|
||||
"Custom Field",
|
||||
{"insert_after": old_fieldname, "dt": field.dt},
|
||||
"insert_after",
|
||||
new_fieldname,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,11 @@
|
|||
# License: MIT. See LICENSE
|
||||
|
||||
import frappe
|
||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||
from frappe.custom.doctype.custom_field.custom_field import (
|
||||
create_custom_field,
|
||||
create_custom_fields,
|
||||
rename_fieldname,
|
||||
)
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
|
||||
test_records = frappe.get_test_records("Custom Field")
|
||||
|
|
@ -81,3 +85,23 @@ class TestCustomField(FrappeTestCase):
|
|||
# undo changes commited by DDL
|
||||
# nosemgrep
|
||||
frappe.db.commit()
|
||||
|
||||
def test_custom_field_renaming(self):
|
||||
def gen_fieldname():
|
||||
return "test_" + frappe.generate_hash()
|
||||
|
||||
field = create_custom_field("ToDo", {"label": gen_fieldname()}, is_system_generated=False)
|
||||
old = field.fieldname
|
||||
new = gen_fieldname()
|
||||
data = frappe.generate_hash()
|
||||
doc = frappe.get_doc({"doctype": "ToDo", old: data, "description": "Something"}).insert()
|
||||
|
||||
rename_fieldname(field.name, new)
|
||||
field.reload()
|
||||
self.assertEqual(field.fieldname, new)
|
||||
|
||||
doc = frappe.get_doc("ToDo", doc.name) # doc.reload doesn't clear old fields.
|
||||
self.assertEqual(doc.get(new), data)
|
||||
self.assertFalse(doc.get(old))
|
||||
|
||||
field.delete()
|
||||
|
|
|
|||
|
|
@ -1285,6 +1285,9 @@ class Database:
|
|||
"""Get estimated max row size of any table in bytes."""
|
||||
raise NotImplementedError
|
||||
|
||||
def rename_column(self, doctype: str, old_column_name: str, new_column_name: str):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@contextmanager
|
||||
def savepoint(catch: type | tuple[type, ...] = Exception):
|
||||
|
|
|
|||
|
|
@ -254,6 +254,20 @@ class MariaDBDatabase(MariaDBConnectionUtil, MariaDBExceptionUtil, Database):
|
|||
null_constraint = "NOT NULL" if not nullable else ""
|
||||
return self.sql_ddl(f"ALTER TABLE `{table_name}` MODIFY `{column}` {type} {null_constraint}")
|
||||
|
||||
def rename_column(self, doctype: str, old_column_name, new_column_name):
|
||||
current_data_type = self.get_column_type(doctype, old_column_name)
|
||||
|
||||
table_name = get_table_name(doctype)
|
||||
|
||||
frappe.db.sql_ddl(
|
||||
f"""ALTER TABLE `{table_name}`
|
||||
CHANGE COLUMN `{old_column_name}`
|
||||
`{new_column_name}`
|
||||
{current_data_type}"""
|
||||
# ^ Mariadb requires passing current data type again even if there's no change
|
||||
# This requirement is gone from v10.5
|
||||
)
|
||||
|
||||
def create_auth_table(self):
|
||||
self.sql_ddl(
|
||||
"""create table if not exists `__Auth` (
|
||||
|
|
|
|||
|
|
@ -264,6 +264,12 @@ class PostgresDatabase(PostgresExceptionUtil, Database):
|
|||
ALTER COLUMN "{column}" {null_constraint}"""
|
||||
)
|
||||
|
||||
def rename_column(self, doctype: str, old_column_name: str, new_column_name: str):
|
||||
table_name = get_table_name(doctype)
|
||||
frappe.db.sql_ddl(
|
||||
f"ALTER TABLE `{table_name}` RENAME COLUMN `{old_column_name}` TO `{new_column_name}`"
|
||||
)
|
||||
|
||||
def create_auth_table(self):
|
||||
self.sql_ddl(
|
||||
"""create table if not exists "__Auth" (
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue