Modifying MySQL data from within Python

This article is part of Robert Sheldon's continuing series on Learning MySQL. To see all of the items in the series, click here.

In the previous article in this series, I introduced you to how to access MySQL data from within a Python script. The article described how to use the MySQL Connector to establish a connection with a database and then retrieve data through that connection. In this article, I continue the discussion by demonstrating how to insert, update, and delete data in a MySQL database, again working with Python and the MySQL Connector.

The process of modifying MySQL data from within Python is, in many respects, similar to querying data. You must define a connection object that links to the database and define a cursor object based on that connection. You can then use the cursor object to execute your SQL statement. If you are not familiar with these concepts, I recommend that you read my previous article before launching into this one.

Although I focus specifically on Python and the MySQL Connector in this article (and in the last one), the process of connecting to a MySQL database and querying data is similar in most object-oriented programming environments, at least in the broader sense.

Each combination of programming language and database connector is unique, and you must understand how they work in your specific environment to use them effectively. That said, seeing how to interface with MySQL from within Python can still provide you with a conceptual understanding of what database access looks like in a programming language.

Note: The examples in this article are based on a specific MySQL and Python setup. The last section of the article—“Appendix: Preparing your Python and MySQL environment”—provides information about how I set up my system and includes a SQL script for creating the database and tables on which the examples are based.

Adding data to a MySQL database from within Python

The steps you take to insert data in a MySQL database are similar to those you follow to update or delete data. In each case, you generally take the following steps:

  1. Import the mysql.connector module or specific its components.
  2. Define a connection object that establishes a connection to the target database.
  3. Use the connection object to invoke the cursor method and create a cursor object.
  4. Define a SQL statement and save it to a variable.
  5. Use the cursor object to invoke the execute method, passing in the variable as an argument.
  6. Commit your changes to the database.
  7. Close the cursor and the connection.

These steps are, of course, a simplification of the process, but they should provide you with an idea of what you’re trying to achieve. You can see these steps in the following Python script, which runs an INSERT statement against the manufacturers table in the travel database:

If you read the previous article, many of these elements should look familiar to you. The script starts by importing the connect and Error methods from the MySQL Connector module (mysql.connector). Next, the script instantiates a connection object and assigns it to the conn variable and then instantiates a cursor object and assigns it to the cursor variable. This is followed by a try block, where you define and run your SQL statement.

Note: I do want to remind you again that putting passwords, especially the root password, in a script that you store somewhere is not best security practice. A discussion of security is far beyond the scope of this article. It’s up to you to take the steps necessary to secure your application from the wide range of potential threats that can put your data at risk.

One thing you might notice that’s different in this script from the examples you saw in the previous article is that the connection and cursor objects are defined prior to the try block, rather than within it. This lets you access the objects outside of the try block, which I’ll explain shortly.

In the try block, I first defined the INSERT statement and assigned it to the add_manufacturer variable. I then used the cursor object to call the execute method, passing in the variable in as an argument. Because it contains the INSERT statement, the method will execute the statement when you run the script.

Next, I used the connection object to invoke the commit method. You must specially commit your changes to the database because by default, the MySQL Connector turns off MySQL’s autocommit feature, so your changes won’t be implemented until you commit them. (Note: you can control this with autocommit property on the connection object if you wish.)

I also used the connection object to invoke the rollback method, which I included in the except block. Because I defined the connection object outside of the try block, I can use the object in other blocks to invoke methods such as rollback. If a data-related error occurs, the transaction will be rolled back, undoing any changes that might have been made within the try block.

Something else I’ve included that wasn’t in the previous article is a finally block. A finally block is often used with a try block to run statements that should be executed whether or not an error occurs. In this way, you can ensure that the cursor and connection get closed even if there is a MySQL exception.

When you run the Python script the first time, it should insert the row in the manufacturers table with no problem. However, if you try to run the script a second time, MySQL will return a duplicate key error because the manufacturer_id value of 101 already exists. The manufacturer_id column is the table’s primary key, so a duplicate key error would be expected. In fact, this is an easy way to verify whether your script’s MySQL error handing is working as it should.

In the previous article, I also demonstrated how to use the %s marker as a placeholder within your SELECT statements. The marker makes it possible to create more dynamic SELECT statements based on user input. You can also use %s markers in your INSERT statements (as well as UPDATE and DELETE statements). The following Python script includes an INSERT statement that contains nine %s markers in the VALUES clause:

After defining the INSERT statement, I created a tuple that contains the values that will be assigned to the markers, in the order they need to be inserted. I then assigned the tuple to the plane_values variable. (A tuple is an immutable collection of objects.) In the real world, you’ll likely want to capture the marker values through user interaction or other means, rather than hard-coding them in this way, but this approach should be enough to demonstrate how the markers work.

After defining my SQL statement and marker values, I used the cursor object to call the execute method and run the INSERT statement. I also included a second argument that specifies the plane_values variable. As a result, the %s markers in the INSERT statement will be replaced with the tuple values during statement execution. When you run the script the first time, the row should be added to the airplanes table with no problem.

In some cases, you’ll want to add multiple rows to a table in a single operation. You can do this in Python by making a few adjustments to the code, as shown in the following script:

The INSERT statement in this script is the same one as in the previous example. However, the tuple assigned to the plane_values variable has been replaced by a list that includes four tuples containing the %s values for the INSERT statement. Each tuple corresponds to one row of data that will be inserted into the airplanes table.

Another difference in this example from the previous one is that the cursor object is now used to invoke the executemany method rather than the execute method. The executemany method makes it easy to use a single INSERT statement to add multiple rows to a table, while still taking advantage of the %s markers. When you run this Python script, it should now add the four rows to the airplanes table.

You can, of course, define an INSERT statement that includes all the rows, without using the %s markers, but then you lose the advantage of the markers and their potential for creating dynamic queries. Even so, it’s good to know that you can create a single statement if you want to go this route.

You can also define multiple SQL statements within your Python code. For example, the following Python script adds a row to the manufacturers table and then adds five rows to the airplanes table for that manufacturer:

The script first defines the INSERT statement for the manufacturers table, adds a tuple for the statement’s %s values, and then invokes the execute method to run the statement. Next, the script defines the INSERT statement for the airplanes table, adds a list of tuples for the %s values, and then invokes the executemany method to run the second statement. After running the two statements, the script commits the transaction.

Updating data in a MySQL database from within Python

The process of updating MySQL data from within Python works much the same as inserting data, except that you define an UPDATE statement instead of an INSERT statement. For example, the following Python script modifies a row in the airplanes table:

You should recognize most of the elements in this script. The UPDATE statement changes the plane value for the row that has a plane_id value of 1010. This in itself should be fairly straightforward, but take particular note of the id_value variable and its assignment.

The UPDATE statement includes only one %s marker, so you need to define only one value outside the statement. In this case, however, the assigned value, 1010, is followed by a comma and enclosed in parentheses. This is because the execute method will accept only a list, tuple, or dictionary for the second argument, not a simple string or integer. To accommodate this requirement, I created a tuple by adding the trailing comma and then enclosing the value and comma in parentheses. Now I can then use the id_value variable as the second argument of the execute method.

When modifying MySQL data, you can create more complex SQL statements than what I showed you in the previous script. For example, the following UPDATE statement includes a subquery in its WHERE clause:

The subquery retrieves the manufacturer_id value for the manufacturer specified in the manufacturer variable. The manufacturer_id value is then used to determine which rows in the airplanes table to update. (I covered subqueries in an earlier article in this series, so refer to that if you have any questions about how they work.)

In the previous two examples, the UPDATE statements included only one %s marker, but you can include multiple markers in your statements, as in the following Python script:

The UPDATE statement contains two %s markers in the SET clause and one in the WHERE clause. The values for these markers are in a tuple that I assigned to the plane_values variable. You can use as many %s markers as needed in whatever clauses you deem necessary. Just remember to specify their values in the correct order so they match the statement.

After I ran the UPDATE statement and called the commit method, I defined a SELECT statement to retrieve the newly updated data from the airplanes table. I did this as a way to verify that the data had been correctly modified.

When I called the execute method to run the SELECT statement, I passed in the value from the plane_values tuple, specifying 2 as the value’s index number. (Tuples use a 0-based index.) I also added a comma after the index number and enclosed the entire value in parentheses so it would be treated as its own tuple, as required by the execute method.

I then used the cursor object to run the fetchone method, which retrieves the current row from the query results. Next, I saved the row of values—returned as a single tuple—to the results variable.

I followed this with a series of print statements that display information about the update. The first print statement calls the rowcount property on the cursor object. The property shows the number of rows that were affected by the UPDATE statement. This is followed by four more print statements, one for each value in the row returned by the SELECT statement. Each print statement uses the tuple’s index to specify which value to include. The returned information should look similar to the following:

The original wingspan value was 261.65, and the original plane_length value was 238.62. When you add 5 and 8 to these values, respectively, you get the new totals returned by the script. You can also add logic to your script to capture the old values before updating them. In that way, you can return both the old and new values at the end of your script and pass those onto your application.

Deleting data in a MySQL database from within Python

The process of deleting MySQL data from within Python is much the same as inserting or updating it. For example, the following Python script deletes a row from the airplanes table:

At this point, you should be familiar with all the elements in this script. I defined a DELETE statement and assigned it to the delete_plane variable. I then assigned a value to the plane_id variable, which will be used for the %s marker. Next, I executed the statement, specifying the two variables, and added a print statement to display the number of rows that were deleted.

Overall, there’s nothing really new about this script except for the use of a DELETE statement rather than INSERT or UPDATE. I simply wanted to show you what deleting data might look like. You can make your DELETE statements as complex as necessary and use more %s markers where needed, as in the next example:

In this script, I’ve used a different form of the %s markers. When I defined the marker values, I created a dictionary rather than a tuple or list. A dictionary makes it possible to assign a label to each value. You can then reference that label within your SQL statement. To do so, insert the label name, enclosed in parentheses, between the % and the s, as in %(mfc_id)s. You can reference any value in the dictionary, without regard to their order.

Getting started with Python and MySQL data modifications

In this article and the previous one, I’ve tried to provide you with a foundation for using the MySQL Connector in your Python scripts. There is, of course, much more to the connector and MySQL that what I’ve shown you here. There’s also a lot more to writing data-driven scripts and applications. But the information I’ve provided should at least help you better understand how to connect to a MySQL database from within Python and manipulate the data. You might also find this information useful when working with other programming languages, at least from a conceptual vantage point. Data is at the heart of most applications, and the more insight you have into how the pieces fit together, the greater you’ll be able to appreciate what it takes to make these applications work.

Appendix: Preparing your MySQL and Python environment

For the examples in this article, I used a Mac computer that was set up with the following components:

  • MySQL 8.0
  • Python 3.10
  • PyCharm Community Edition IDE
  • MySQL Connector/Python 8.0 module

You can find information about installing the module in the MySQL documentation. On my system, I used pip to add the module. Pip is a package installer for Python that makes it very easy to deploy a module.

The Python examples in this article connect to the travel database on a local MySQL instance. The database contains the manufacturers table and airplanes table, which is defined with a foreign key that references the manufacturers table. This is the same database and tables you saw in previous articles in this series, except that you don’t need to insert any data.

If you plan to try out these examples, start by running the following script against your MySQL instance:

The script will create the travel database and add the manufacturers and airplanes tables in the proper order to accommodate the foreign key defined in the airplanes table. If you already created the database and tables for previous articles, I recommend that you re-create them now to ensure that your key values line up correctly with the examples.