瀏覽代碼

Merge pull request #15906 from ankush/ignore_duplicates_db

feat(db/pg): ability to ignore pk collision
version-14
Ankush Menat 3 年之前
committed by GitHub
父節點
當前提交
c38d1f8a25
沒有發現已知的金鑰在資料庫的簽署中 GPG Key ID: 4AEE18F83AFDEB23
共有 4 個文件被更改,包括 37 次插入13 次删除
  1. +2
    -0
      frappe/database/database.py
  2. +24
    -8
      frappe/model/base_document.py
  3. +1
    -5
      frappe/model/document.py
  4. +10
    -0
      frappe/tests/test_db.py

+ 2
- 0
frappe/database/database.py 查看文件

@@ -177,6 +177,8 @@ class Database(object):
raise frappe.QueryTimeoutError(e)

elif frappe.conf.db_type == 'postgres':
# TODO: added temporarily
print(e)
raise

if ignore_ddl and (self.is_missing_column(e) or self.is_missing_table(e) or self.cant_drop_field_or_key(e)):


+ 24
- 8
frappe/model/base_document.py 查看文件

@@ -389,12 +389,24 @@ class BaseDocument(object):
fieldname = [df.fieldname for df in self.meta.get_table_fields() if df.options==doctype]
return fieldname[0] if fieldname else None

def db_insert(self):
"""INSERT the document (with valid columns) in the database."""
def db_insert(self, ignore_if_duplicate=False):
"""INSERT the document (with valid columns) in the database.

args:
ignore_if_duplicate: ignore primary key collision
at database level (postgres)
in python (mariadb)
"""
if not self.name:
# name will be set by document class in most cases
set_new_name(self)

conflict_handler = ""
# On postgres we can't implcitly ignore PK collision
# So instruct pg to ignore `name` field conflicts
if ignore_if_duplicate and frappe.db.db_type == "postgres":
conflict_handler = "on conflict (name) do nothing"

if not self.creation:
self.creation = self.modified = now()
self.created_by = self.modified_by = frappe.session.user
@@ -405,10 +417,11 @@ class BaseDocument(object):
columns = list(d)
try:
frappe.db.sql("""INSERT INTO `tab{doctype}` ({columns})
VALUES ({values})""".format(
doctype = self.doctype,
columns = ", ".join("`"+c+"`" for c in columns),
values = ", ".join(["%s"] * len(columns))
VALUES ({values}) {conflict_handler}""".format(
doctype=self.doctype,
columns=", ".join("`"+c+"`" for c in columns),
values=", ".join(["%s"] * len(columns)),
conflict_handler=conflict_handler
), list(d.values()))
except Exception as e:
if frappe.db.is_primary_key_violation(e):
@@ -421,8 +434,11 @@ class BaseDocument(object):
self.db_insert()
return

frappe.msgprint(_("{0} {1} already exists").format(self.doctype, frappe.bold(self.name)), title=_("Duplicate Name"), indicator="red")
raise frappe.DuplicateEntryError(self.doctype, self.name, e)
if not ignore_if_duplicate:
frappe.msgprint(_("{0} {1} already exists")
.format(self.doctype, frappe.bold(self.name)),
title=_("Duplicate Name"), indicator="red")
raise frappe.DuplicateEntryError(self.doctype, self.name, e)

elif frappe.db.is_unique_key_violation(e):
# unique constraint


+ 1
- 5
frappe/model/document.py 查看文件

@@ -249,11 +249,7 @@ class Document(BaseDocument):
if getattr(self.meta, "issingle", 0):
self.update_single(self.get_valid_dict())
else:
try:
self.db_insert()
except frappe.DuplicateEntryError as e:
if not ignore_if_duplicate:
raise e
self.db_insert(ignore_if_duplicate=ignore_if_duplicate)

# children
for d in self.get_all_children():


+ 10
- 0
frappe/tests/test_db.py 查看文件

@@ -291,6 +291,16 @@ class TestDB(unittest.TestCase):

frappe.db.MAX_WRITES_PER_TRANSACTION = Database.MAX_WRITES_PER_TRANSACTION

def test_pk_collision_ignoring(self):
# note has `name` generated from title
for _ in range(3):
frappe.get_doc(doctype="Note", title="duplicate name").insert(ignore_if_duplicate=True)

with savepoint():
self.assertRaises(frappe.DuplicateEntryError, frappe.get_doc(doctype="Note", title="duplicate name").insert)
# recover transaction to continue other tests
raise Exception


@run_only_if(db_type_is.MARIADB)
class TestDDLCommandsMaria(unittest.TestCase):


Loading…
取消
儲存