devxlogo

Create a Web-Based CRUD Automation Framework with Python

Create a Web-Based CRUD Automation Framework with Python

uppose you have a constantly growing database that currently contains 75 tables. Like any other database, a significant percentage of the tables carry metadata (i.e., the kind of data that appears as checkboxes or dropdown lists in your web form). Whenever you add a new table, you have to implement database access layer classes that look roughly like this:

class genre:
create(params)read(params)update(params)delete(params)

You then expose this class as an XML-RPC service so that client code written in any language can invoke it.

Now imagine the data access layer getting created auto-magically whenever a new table is added, without you needing to do a thing. This article explains how to implement just such a function using a CRUD (Create, Read, Update, Delete) automation framework that handles insert and simple queries.

Inside the Framework

The sample framework uses two open source, Python-based technologies: Twisted (asynchronous networking framework with built-in protocol implementations) and SQLAlchemy (SQL toolkit and object relational mapper). Twisted allows you to focus on application logic and not worry about protocol implementation intricacies. SQLAlchemy allows you to map a table as a Python class and an instance of class maps to a record. That way you can write maintainable code easily without worrying about the underlying RDBMS. The CRUD automation framework leverages Twisted’s own database API as well as SQLAlchemy’s database reflection API to iterate through all the tables in a database.

Click here to download the source code for the framework, which consists of the following:

  • Two core modules, dbinterface and crud-service
  • A config.py file, which consists of configuration parameters that you can modify to talk to your database
  • A test database (test.db), which has one table, Genre, that consists of a list of genres that classify a video (e.g., Comedy, Sports, News, etc.).
  • A dbcreate.py file, which you can use to create test databases of your own
  • A crud-client.py file, a sample client that demonstrates how easy it is to create a record and perform queries (see below)

The following listing is the code for crud-client.py:

s = xmlrpclib.Server('http://127.0.0.1:9080')#create a Genre record and set it to activerecord = { 'name' : 'Comedy', 'active' : True}s.create('genre', record)#Fetch all the records from genre table that are in active statusconditions = {'active' : True}for result in s.read('genre', conditions):    print result
Author’s Note: The framework was tested with the following technologies:

  • Python 2.5.x
  • SQLAlchemy 0.4.7
  • SQLite 3.6.14.2
  • pysqlite
  • Twisted 8.2.0 for python 2.5

How the Framework Works

This section breaks down the components of the CRUD automation framework.

The dbinterface module has two classes, basetable and dbinterface. The basetable class has two key methods, pack and unpack. The pack method populates the table class instance with an input dictionary consisting of column names and values. The unpack method converts the table class instance back to a dictionary with column names as keys and column values as values.

Here is the code for dbinterface.py:

class basetable(object):    #map the record dictionary to table instance variables    def pack(self, record):        for column in record:            self.__dict__[column] = record[column]        #return the dictionary representation of the table instance    def unpack(self):        record = {}        for name in self.__dict__:            if name[0] == "_": continue #exclude non column keys            value = self.__dict__[name]            try:                record[name] = str(value)            except:                record[name] = repr(value)        return record

The dbinterface class maps all the tables of the database to table classes that inherit this base table. Here is the dbinterface init method for this mapping:

#Initialize DB and create a hashmap of table name and associated ORM mapper class        metadata = MetaData(db)        #retrieve database table information dynamically        metadata.reflect()        for table_name in metadata.tables:            #create a class that inherits basetable class and maps the class to table            table_class = type(str(table_name),(basetable,),{})            mapper(table_class, Table(table_name, metadata, autoload=True))            self.table_map[table_name] = table_class

The dbinterface also provides two methods, read and create, and you can easily implement update and delete methods as well. The read method shown below maps the resultset into an array of dictionaries:

#fetch all the records from tables that have conditions specified    def read(self, table_name, **keywords):        session = None        records = []        try:            table_class = self.table_map[table_name]            session = self.Session()            query = session.query(table_class)            resultset = query.filter_by(**keywords).all()            for record in resultset:                records.append(record.unpack())        except:            print traceback.format_exc()        finally:            if session:                session.close()        return records

The create method shown below creates a new record. The input is the dictionary with column name and column value as a key-value pair:

#create a record    def create(self, table_name, record):        session = None        status = False        try:            #lookup the corresponding table class and create an instance            table_instance = self.table_map[table_name]()            table_instance.pack(record)            session = self.Session()            session.save(table_instance)            session.commit()            status = True        except:            print traceback.format_exc()        finally:            if session:                session.close()        return status

The dbinterface class is wrapped around an XML-RPC class. (XML-RPC is simpler to use than SOAP.)

The crud-service class is very straightforward. It initializes db interfaces and sets up loggers. The configuration for the service is stored in config.py:

#Configuration Parameters#db url of your database. for simplicity, sqlite is useddb_url = 'sqlite:///test.db'#log directory is created in the same directory program is runninglog_dir = 'log'#log file namelog_file = 'crud.log'#the ip address of the serverhost = '127.0.0.1'#listening portport = 9080

The DB URL to access is sqlite:///test.db. If you have a PostgreSQL DB, the URL would look like postgres://user:password@host:port/dbname.

The crudservice proxies the requests to the dbinterface class. Here is the code for crudservice.py:

class crudservice(xmlrpc.XMLRPC):    def __init__(self, dbi):        self.dbi = dbi         xmlrpc.XMLRPC.__init__(self)    def xmlrpc_create(self, table_name, record):        return self.dbi.create(table_name, record)        def xmlrpc_read(self, table_name, conditions):        return self.dbi.read(table_name, **conditions)

To start the framework, run the command “python crud-service.py.” Whenever a new table is added, all you have to do is restart the service.

You can easily extend the framework to handle complex queries involving joins across tables. You also could easily replace XML-RPC with REST or SOAP.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist