简体   繁体   中英

Python webserver script won't submit to sqlite3 database

In the below script a simple webserver is created and there is a class that should sends GET data to a sqlite3 database.

import SimpleHTTPServer
import SocketServer
import sqlite3

PORT = 8000

Handler = SimpleHTTPServer.SimpleHTTPRequestHandler

httpd = SocketServer.TCPServer(("", PORT), Handler)

print "serving at port", PORT
httpd.serve_forever()


class DBLoggingHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
        super(DBLoggingHandler, self).__init__(*args, **kwargs)
        self.db = sqlite3.connect('/home/cron1admin/crazysean/crazysean.db')
    def do_GET(self):
        self.db.execute("INSERT INTO crazysean (command, vers, path) VALUES (?, ?, ?)",
                        (self.command, self.request_version, self.path))
        self.db.commit()
        return super(DBLoggingHandler, self).do_GET()

DBLoggingHandler()

The webserver portion of the script works properly. I am seeing GET data in my terminal output. However, nothing is being written to the crazysean table that I created in the db. Do you see anything wrong with my script or is it likely something else?


Code has been changed to reflect changes that I have made, but it is still not writing to the db.

UPDATED CODE:

import SimpleHTTPServer
import SocketServer
import sqlite3

PORT = 8000

class DBLoggingHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
        SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, *args, **kwargs)
        self.db = sqlite3.connect('/home/cron1admin/crazysean/crazysean.db')
    def do_GET(self):
        self.db.execute("INSERT INTO crazysean (command, vers, path) VALUES (?, ?, ?)",
                        (self.command, self.request_version, self.path))
        self.db.commit()
        return super(DBLoggingHandler, self).do_GET()

Handler = DBLoggingHandler

httpd = SocketServer.TCPServer(("", PORT), Handler)

print "serving at port", PORT
httpd.serve_forever()

You're missing the final magic words db.commit() :)

def do_GET(self):
        self.db.execute("INSERT INTO crazysean (command, vers, path) VALUES (?, ?, ?)",
                        (self.command, self.request_version, self.path))
        self.db.commit()
        return super(DBLoggingHandler, self).do_GET()

You have another error and this one is bigger. You're not actually calling your code, just calling SimpleHTTPRequestHandler .

Full example:

class DBLoggingHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
        SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(*args, **kwargs)
        self.db = sqlite3.connect('/home/cron1admin/crazysean/crazysean.db')
    def do_GET(self):
        self.db.execute("INSERT INTO crazysean (command, vers, path) VALUES (?, ?, ?)",
                        (self.command, self.request_version, self.path))
        self.db.commit()
        return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET()

Handler = DBLoggingHandler

httpd = SocketServer.TCPServer(("", PORT), Handler)

print "serving at port", PORT
httpd.serve_forever()

First you do this:

httpd.serve_forever()

And then you create the handler class and instantiate it.

So, your code doesn't do anything until after "forever". Which is way too long to wait for it.

What you want to do is pass your handler subclass to the server, in place of the base class. Where you have this:

Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
httpd = SocketServer.TCPServer(("", PORT), Handler)

… instead do this:

Handler = DBLoggingHandler
httpd = SocketServer.TCPServer(("", PORT), Handler)

(Or just get rid of the extra line and do httpd = SocketServer.TCPServer(("", PORT), DBLoggingHandler ).)

You will also have to move the definition of your class up to the top of the file.


But there's another problem.

Python used to have two different kinds of classes: new-style and classic . They fixed that years ago in 3.0, but you're still using 2.something.

Normally you can just always use the new-style classes and never worry about the old kind, but every once in a while you run into a class in the stdlib or a third-party library that's still made the old way, and you want to inherit from it. And that's exactly what happened here. So now, sadly, you have to learn about old-style classes.

Among the things old-style classes can't do is super . The fix for this is to explicitly call the unbound method of the base class. So, instead of this:

super(DBLoggingHandler, self).__init__(*args, **kwargs)

… you need this:

SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, *args, **kwargs)

And likewise for the other super call.

The way you know this is happening (other than by looking at the base class) is when you get an error message about classobj or instance . Those two types always mean a classic class is involved somewhere.


But wait, there's more.

Whatever idiot told you to put that self.db = … stuff in the __init__ method was an idiot. :)

SocketServer handlers are a weird kind of class. Each new request constructs a new instance, and the __init__ method calls handle , which doesn't return until it's time to destroy the whole instance. So, anything you put in the __init__ after calling the base class won't happen until it's too late.

The simplest thing to do here is probably to make db a global variable. All else being equal, globals are bad. But in this case, the alternatives seem to be either overriding functionality from three different classes, or monkeypatching it into the server instance and relying on undocumented details to reach it.


Also, you probably want to close the database somewhere. Killing the program without closing the database should never leave it corrupted, but it can mean you lose the last transaction if you time it just wrong.

Since serve_forever never returns except by you hitting ctrl-c, you can't just put it after that line; you need a with statement (or a finally , or an except BaseException , or an atexit ). I don't think sqlite3.Connection objects became context managers until a later version of Python, so you'll have to use the closing context manager.


So:

import contextlib
import SimpleHTTPServer
import SocketServer
import sqlite3

PORT = 8000

db = sqlite3.connect('/home/cron1admin/crazysean/crazysean.db')

class DBLoggingHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def do_GET(self):
        db.execute("INSERT INTO crazysean (command, vers, path) VALUES (?, ?, ?)",
                        (self.command, self.request_version, self.path))
        db.commit()
        return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)

Handler = DBLoggingHandler

httpd = SocketServer.TCPServer(("", PORT), Handler)

print "serving at port", PORT
with contextlib.closing(db):
    httpd.serve_forever()

Take a look at the example at the top of:

https://docs.python.org/2/library/sqlite3.html

You need to create a cursor, execute statements through the cursor, then commit the connection.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM