AttributeError After Attempt To Make New DocType Via Script With Data From Foreign Database: 'NoneType' object has no attribute 'replace'

So, I’m working on a script that maps SQL data to DocTypes, which it creates if they don’t exist yet and updates if they do.

The strange error above occurs after the interpreter reaches the line in the code that creates a new DocType using the SQL data, which is stored in a dictionary. The bottom of the traceback shows the culprit: the String replace() method being run:

File “/home/vongol/frappe-bench/apps/frappe/frappe/”, line 692, in scrub
return txt.replace(’ ‘,’‘).replace(’-', '').lower()

Here’s the whole traceback and the code:

Traceback (most recent call last):
  File "/usr/lib/python2.7/", line 174, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/usr/lib/python2.7/", line 72, in _run_code
    exec code in run_globals
  File "/home/vongol/frappe-bench/apps/frappe/frappe/utils/", line 94, in <module>
  File "/home/vongol/frappe-bench/apps/frappe/frappe/utils/", line 18, in main
  File "/home/vongol/frappe-bench/env/local/lib/python2.7/site-packages/click/", line 722, in __call__
    return self.main(*args, **kwargs)
  File "/home/vongol/frappe-bench/env/local/lib/python2.7/site-packages/click/", line 697, in main
    rv = self.invoke(ctx)
  File "/home/vongol/frappe-bench/env/local/lib/python2.7/site-packages/click/", line 1066, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/vongol/frappe-bench/env/local/lib/python2.7/site-packages/click/", line 1066, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/vongol/frappe-bench/env/local/lib/python2.7/site-packages/click/", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/vongol/frappe-bench/env/local/lib/python2.7/site-packages/click/", line 535, in invoke
    return callback(*args, **kwargs)
  File "/home/vongol/frappe-bench/env/local/lib/python2.7/site-packages/click/", line 17, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/home/vongol/frappe-bench/apps/frappe/frappe/commands/", line 24, in _func
    ret = f(frappe._dict(ctx.obj), *args, **kwargs)
  File "/home/vongol/frappe-bench/apps/frappe/frappe/commands/", line 113, in execute
    ret = frappe.get_attr(method)(*args, **kwargs)
  File "/home/vongol/frappe-bench/apps/sql2doctype/sql2doctype/sql2doctype/", line 51, in sync
  File "/home/vongol/frappe-bench/apps/sql2doctype/sql2doctype/sql2doctype/", line 17, in add_doc
    doc = frappe.get_doc(fields, fields['LAST_NAME'])
  File "/home/vongol/frappe-bench/apps/frappe/frappe/", line 622, in get_doc
    return frappe.model.document.get_doc(arg1, arg2)
  File "/home/vongol/frappe-bench/apps/frappe/frappe/model/", line 49, in get_doc
    controller = get_controller(doctype)
  File "/home/vongol/frappe-bench/apps/frappe/frappe/model/", line 35, in get_controller
    module = load_doctype_module(doctype, module_name)
  File "/home/vongol/frappe-bench/apps/frappe/frappe/modules/", line 179, in load_doctype_module
    module_name = get_module_name(doctype, module, prefix, suffix)
  File "/home/vongol/frappe-bench/apps/frappe/frappe/modules/", line 193, in get_module_name
    doctype = scrub(doctype),
  File "/home/vongol/frappe-bench/apps/frappe/frappe/modules/", line 133, in scrub
    return frappe.scrub(txt)
  File "/home/vongol/frappe-bench/apps/frappe/frappe/", line 692, in scrub
    return txt.replace(' ','_').replace('-', '_').lower()
AttributeError: 'NoneType' object has no attribute 'replace'

import frappe
from frappe.model.document import Document
import pyodbc


### Initialize connection and cursor ###

cnxn = pyodbc.connect('DRIVER={ODBC Driver 13 For SQL Server}; SERVER=localhost; DATABASE=testdb; UID=SA; PWD=xyzzy209!')
cursor = cnxn.cursor()

### Query functions ###

# Add a DocType by passing a dictionary to the get_doc function.
def add_doc(fields):
    doc = frappe.get_doc(fields, fields['LAST_NAME'])

# "Synchronize" function. Maps SQL data in table to DocTypes, adding new DocTypes and/or upgrading them when applicable.
def sync(query):
    #query = 'SELECT * FROM \"%s\"' % table
    #cursor.execute(query, table)

    doctypes = []
    records = []
    columns = [column[0] for column in cursor.description]

    # Iterate through each row and pass a dictionary containing its column names as keys and respective values to add_doc.
    # Use id field to designate each SQL table and ease searching and updating.
    for row in cursor.fetchall():
        count = 0
        fields = dict(zip(columns, row))

        if doctypes is not None:
            for doctype in doctypes:

                if fields["ID"] == doctype["ID"] and fields != doctype:
                        for field in fields:

                            doc_field = getattr(doctype, field)
                            db_field = fields[field]

                            if doc_field != db_field:
        count += 1
        print("Added %d doc" % count)

You should create custom doctypes (custom should be 1) or your doctypes should have controllers (py files)

1 Like

OK. So, how do I go about doing this via scripting? Shouldn’t controllers be automatically generated upon creation of DocTypes? Is “custom” an attribute that can be changed via an argument? Can I do all of this in the script?