OnlyDean
OnlyDean

Reputation: 1039

Python Abstract Method Default Logic, super() method

I am coming from the Java world and I am having a hard time understanding the super() method as it relates to abstract methods.

I have an abstract class with an abstract method. This class' subclasses are responsible for querying my MySQL database and returning formatted reports to me. In half of the subclasses, they use the exact same logic to run the report. In the other half, each needs its own logic. Therefore I want to make run_report an abstract method.

class Report:
    __metaclass__ = ABCMeta

    def __init__(self):
        self.user = 'root'
        self.passwd = ''
        self.host = '127.0.0.0.1'
        self.port = '80'
        self.db = 'animals'        
        self.conn = mysql.connector.connect(user=self.user, password=self.passd, db=self.db, host=self.host, port=self.port)
        self.cursor = self.conn.cursor()

    @staticmethod
    def construct_query(query, external_data):
        finalQuery = query % external_data
        return finalQuery

    @abstractmethod
    def run_report(self, query):
        self.cursor.execute(self, query)
        results = self.cursor.fetchall()
        return data

Here is an example of an implementation of a class that uses the abstract method as is:

class SheepReport(Report):

    query = "SELECT COUNT(*) FROM sheep WHERE alive = 1;"

    def run_report(query):
        super(Report, self).run_report(query)

This syntax seems arcane. My code is too incomplete to test run at the moment, and I'm trying to nip errors in the bud. Am I understanding abstract methods correctly?

By contrast, here is a class that overrides run_report:

 class CowReport(Report):

    query = "SELECT height, weight FROM cows WHERE name IN (%s);"

    def run_report(self):
        finalQuery = self.construct_query(query, "'Hilda', 'Gary'");
        self.cursor.execute(self, finalQuery)
        results = self.cursor.fetchall()
        return data

Am I on the right track? All the examples of super() and inheritance I've seen are going over my head, I think I need someone to dumb it down for me.

Upvotes: 1

Views: 3610

Answers (1)

rantanplan
rantanplan

Reputation: 7450

First of all, I sincerely hope this is just for learning purposes and not actual production code. Passing unsanitized user input into a query string equals to a full blown SQL injection attack.

With that out of the way...

Abstract classes are usually for defining interfaces, rather than concrete functionality. Nevertherless for what you're trying to do, an abstract class with only query defined as an abstract property, would simplify considerably your code.

class Report:
    __metaclass__ = ABCMeta

    def __init__(self):
        self.user = 'root'
        self.passwd = ''
        self.host = '127.0.0.0.1'
        self.port = '80'
        self.db = 'animals'
        self.conn = mysql.connector.connect(user=self.user, password=self.passd, db=self.db, 
                                            host=self.host, port=self.port)
        self.cursor = self.conn.cursor()

    @abstractproperty
    def query(self):
        return NotImplemented

    def construct_query(self, **external_data):
        finalQuery = self.query % external_data
        return finalQuery

    def run_report(self, **external_data):
        self.cursor.execute(self, self.construct_query(**external_data))
        results = self.cursor.fetchall()
        return results

With the above class, then you only need to have a subclass like the below:

class SheepReport(Report):

    @property
    def query(self):
        return "SELECT COUNT(*) FROM sheep WHERE alive = 1;"

Don't mind the unconditional self.query % external_data. It will work with either template strings or plain strings with empty dictionary data.

Upvotes: 1

Related Questions