diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index d2d8d8a621..ac40bce2f3 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -15,7 +15,7 @@ from frappe.model.document import Document from frappe.custom.doctype.property_setter.property_setter import make_property_setter from frappe.desk.notifications import delete_notification_count_for from frappe.modules import make_boilerplate -from frappe.model.db_schema import validate_column_name, validate_column_length +from frappe.model.db_schema import validate_column_name, validate_column_length, type_map import frappe.website.render # imports - third-party imports @@ -205,6 +205,8 @@ class DocType(Document): def on_update(self): """Update database schema, make controller templates if `custom` is not set and clear cache.""" from frappe.model.db_schema import updatedb + self.rename_custom_fields() + updatedb(self.name, self) self.change_modified_of_parent() @@ -236,6 +238,28 @@ class DocType(Document): if self.name in frappe.local.meta_cache: del frappe.local.meta_cache[self.name] + def rename_custom_fields(self): + if not (frappe.db.table_exists(self.name) and frappe.db.table_exists("Custom Field")): + return + fields = [d.fieldname for d in self.fields if d.fieldtype in type_map] + custom_field_conflict = frappe.db.sql('''select name, fieldname from + `tabCustom Field` + where + fieldname in ({0})'''.format(', '.join(['%s'] * len(fields))), fields, as_dict=True) + + for custom_field in custom_field_conflict: + if frappe.db.has_column(self.name, custom_field.fieldname): + frappe.db.commit() + column_type = frappe.db.get_column_type(self.name, custom_field.fieldname) + + if not frappe.db.has_column(self.name, custom_field.fieldname + '_custom'): + frappe.db.sql('alter table `tab{0}` change `{1}` `{1}_custom` {2}'.format(self.name, + custom_field.fieldname, column_type)) + + # rename in custom field + frappe.db.set_value('Custom Field', custom_field.name, 'fieldname', custom_field.fieldname + '_custom') + + def sync_global_search(self): '''If global search settings are changed, rebuild search properties for this table''' global_search_fields_before_update = [d.fieldname for d in diff --git a/frappe/database.py b/frappe/database.py index 990ddf37be..bc0697ab9e 100644 --- a/frappe/database.py +++ b/frappe/database.py @@ -776,9 +776,9 @@ class Database: """Return true of field exists.""" return self.sql("select name from tabDocField where fieldname=%s and parent=%s", (dt, fn)) - def table_exists(self, tablename): - """Returns True if table exists.""" - return ("tab" + tablename) in self.get_tables() + def table_exists(self, doctype): + """Returns True if table for given doctype exists.""" + return ("tab" + doctype) in self.get_tables() def get_tables(self): return [d[0] for d in self.sql("show tables")] @@ -842,6 +842,10 @@ class Database: """Returns True if column exists in database.""" return column in self.get_table_columns(doctype) + def get_column_type(self, doctype, column): + return frappe.db.sql('''SELECT column_type FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'tab{0}' AND COLUMN_NAME = "{1}"'''.format(doctype, column))[0][0] + def add_index(self, doctype, fields, index_name=None): """Creates an index with given fields if not already created. Index name will be `fieldname1_fieldname2_index`""" diff --git a/frappe/desk/doctype/desktop_icon/desktop_icon.py b/frappe/desk/doctype/desktop_icon/desktop_icon.py index 6d5e2d6ab5..1874e0725c 100644 --- a/frappe/desk/doctype/desktop_icon/desktop_icon.py +++ b/frappe/desk/doctype/desktop_icon/desktop_icon.py @@ -352,7 +352,10 @@ def sync_from_app(app): m['_doctype'] = m.pop('doctype') desktop_icon.update(m) - desktop_icon.save() + try: + desktop_icon.save() + except frappe.exceptions.UniqueValidationError: + pass return modules_list diff --git a/frappe/utils/help.py b/frappe/utils/help.py index 45971820c0..c76e977dd8 100644 --- a/frappe/utils/help.py +++ b/frappe/utils/help.py @@ -15,6 +15,8 @@ from bs4 import BeautifulSoup import jinja2.exceptions from six import text_type +import io + def sync(): # make table print('Syncing help database...') @@ -55,7 +57,7 @@ class HelpDatabase(object): self.global_help_setup = frappe.conf.get('global_help_setup') if self.global_help_setup: bench_name = os.path.basename(os.path.abspath(frappe.get_app_path('frappe')).split('/apps/')[0]) - self.help_db_name = hashlib.sha224(bench_name).hexdigest()[:15] + self.help_db_name = hashlib.sha224(bench_name.encode('utf-8')).hexdigest()[:15] def make_database(self): '''make database for global help setup''' @@ -135,9 +137,9 @@ class HelpDatabase(object): for fname in files: if fname.rsplit('.', 1)[-1] in ('md', 'html'): fpath = os.path.join(basepath, fname) - with open(fpath, 'r') as f: + with io.open(fpath, 'r', encoding = 'utf-8') as f: try: - content = frappe.render_template(text_type(f.read(), 'utf-8'), + content = frappe.render_template(f.read(), {'docs_base_url': '/assets/{app}_docs'.format(app=app)}) relpath = self.get_out_path(fpath) @@ -235,7 +237,7 @@ class HelpDatabase(object): # files not in index.txt for f in os.listdir(path): - if not os.path.isdir(os.path.join(path, f)): + if not os.path.isdir(os.path.join(path, f)) and len(f.rsplit('.', 1)) == 2: name, extn = f.rsplit('.', 1) if name not in files \ and name != 'index' and extn in ('md', 'html'):