CRUD для списка контактов, используя PyMySQL


Я из Java-фон, Изучаем Python. Пожалуйста, просмотрите мой код и руководство меня правильно изучать Python.

Это пример кода для выполнения всех операций CRUD контакта приложение.

import pymysql
from beautifultable import BeautifulTable


class Contact:

    def __init__(self, name, email, mobile, cid=None):
        self.cid = cid
        self.name = name
        self.email = email
        self.mobile = mobile


class DbUtil:

    @staticmethod
    def get_connection():

        try:
            con = pymysql.connect("localhost", "root", "password", "counselor_db")
            return con
        except pymysql.DatabaseError as error:
            print("While connecting with database :", error)

    @staticmethod
    def close_connection(conn, cursor):

        try:
            if conn:
                conn.close()
            if cursor:
                cursor.close()
        except pymysql.DatabaseError as error:
            print("While closing connection ", error)


class ContactService:

    def add_contact(self, new_contact):
        try:
            conn = DbUtil.get_connection()
            cursor = conn.cursor()
            cursor.execute("insert into contact(name,email,mobile) values(%s,%s,%s)", (new_contact.name
                                                                                       ,new_contact.email
                                                                                       ,new_contact.mobile))
            conn.commit()
            self.view_contacts()
        except pymysql.DatabaseError as error:
            print("While adding contact to DB :", error)
        finally:
            DbUtil.close_connection(conn, cursor)

    def get_all_records(self):

        try:
            conn = DbUtil.get_connection()
            cursor = conn.cursor()
            cursor.execute("select cid,name,email,mobile from contact")
            rows = cursor.fetchall()
            lst = self.get_list_data(rows)
            return lst
        except pymysql.DatabaseError as error:
            print("While geting data from DB :", error)
        finally:
            DbUtil.close_connection(conn, cursor)

    def search_all_records(self, search_str):

        try:
            conn = DbUtil.get_connection()
            cursor = conn.cursor()
            cursor.execute("select cid,name,email,mobile from contact where name like %s ", ('%' + search_str + '%'))

            rows = cursor.fetchall()
            lst = self.get_list_data(rows);
            return lst
        except pymysql.DatabaseError as error:
            print("While geting data from DB :", error)
        finally:
            DbUtil.close_connection(conn, cursor)

    def get_list_data(self, rows):

        lst = []
        for row in rows:
            cid = row[0]
            name = row[1]
            email = row[2]
            mobile = row[3]
            cont = Contact(name, email, mobile, cid)
            lst.append(cont)
        return lst

    def view_contacts(self):
        lst = self.get_all_records()
        table = BeautifulTable()
        table.column_headers = ["Cid", "Name", "Email", "Mobile"]
        for li in lst:
            table.append_row([li.id, li.name, li.email, li.mobile])
        print(table)

    def view_contact(self, con):

        table = BeautifulTable()
        table.column_headers = ["Cid", "Name", "Email", "Mobile"]
        table.append_row([con.cid, con.name, con.email, con.mobile])
        print(table)

    def get_contact(self, cid):
        try:

            conn = DbUtil.get_connection()
            cursor = conn.cursor()
            cursor.execute("select cid,name,email,mobile from contact where cid = %s",cid)
            row = cursor.fetchone()
            cid = row[0]
            name = row[1]
            email = row[2]
            mobile = row[3]
            cont = Contact(name, email, mobile, cid)
            self.view_contact(cont)
        except pymysql.DatabaseError as error:
            print("While geting data from DB :", error)
        finally:
            DbUtil.close_connection(conn, cursor)

    def update_contact(self, contact):

        try:

            conn = DbUtil.get_connection()
            cursor = conn.cursor()
            cursor.execute("update contact set name = %s ,email = %s, mobile = %s where cid = %s", (contact.name,
            contact.email, contact.mobile, contact.cid))
            conn.commit()

        except pymysql.DatabaseError as error:
            print("While updating data of DB :", error)
            conn.rollback()
        finally:
            DbUtil.close_connection(conn, cursor)

    def delete_contact(self, cid):

        try:

            conn = DbUtil.get_connection()
            cursor = conn.cursor()
            cursor.execute("delete from contact where cid = %s ",cid)
            conn.commit()

        except pymysql.DatabaseError as error:
            print("While deleting data from DB :", error)

        finally:
            DbUtil.close_connection(conn, cursor)

    def search_contact(self, search_str):
        lst = self.search_all_records(search_str)
        table = BeautifulTable()
        table.column_headers = ["Cid", "Name", "Email", "Mobile"]
        for li in lst:
            table.append_row([li.cid, li.name, li.email, li.mobile])
        print(table)


983
7
задан 20 февраля 2018 в 12:02 Источник Поделиться
Комментарии
2 ответа

Вы могли бы сделать вашу жизнь немного легче, если Contact имел тот же порядок, что rowили наоборот. Тогда вы могли бы просто использовать кортеж распаковка создать новый Contact:

cont = Contact(*row)

К счастью, это довольно легко, просто измените ваши запросы:

cursor.execute("select name,email,mobile,cid from contact ...")
cont = Contact(*cursor.fetchone())

В Contact сам класс также является более сложным, чем это необходимо. Поскольку вы только когда-нибудь понадобится, она действует как объект, который содержит данные (и вы не должны изменить существующую Contactпо крайней мере, не в код, который вы показали), вы можете просто использовать collections.namedtuple:

from collections import namedtuple

Contact = namedtuple("Contact", ["name", "email", "mobile", "cid"])

Делая Contact а namedtuple следует также позволяет использовать его в таком виде, поскольку она теперь ведет себя точно так же, как tuple:

cursor.execute("update contact set name = %s ,email = %s, mobile = %s where cid = %s", contact)

Ваш get_list_data метод также может быть значительно упрощена с помощью этого и список осмысления:

def get_list_data(self, rows):
return [Contact(*row) for row in rows]

4
ответ дан 20 февраля 2018 в 05:02 Источник Поделиться

Есть некоторые элементы, которые больше привлекают мое внимание:

Безопасности

Если ваше приложение будет развернуто, то это не очень хорошо вы ввести учетные данные, как в обычной в код. Существует широкая литература по этому вопросу, и я давайте вы погуглите об этом.

Обработка исключений

Вы должны улучшить способ Вы имеете дело с исключениями.
Например, в get_connection() Если возникает исключение, то return заявление не будет выполняться. Если быть точным, то он будет выполнен, но после except заявление будет сделано, и вы получите None в качестве возвращаемого значения. Как видите, это приносит путаницу.

Хорошей практикой является держать try блок как свет и аккуратно, как можно, запустив только код, который на самом деле подвержен поднять исключение, не более того. Что-нибудь еще, вы должны делегировать его на следующие блоки.

Поэтому я предлагаю вам переписать этот метод следующим образом:

@staticmethod
def get_connection():
try:
con = pymysql.connect("localhost", "root", "password", "counselor_db")
except pymysql.DatabaseError as error:
print("While connecting with database :", error)
raise
else:
return con

Таким образом, try блок делает то, что он должен делать: запустить код, который склонен к бедам. Обратите внимание на return оператор выполняется только если все было в порядке.

Не забывайте, оставшиеся исключения

Другая проблема с этой функцией заключается в том, что вы имеете дело с DatabaseError исключение только, но вы проигнорировали многие другие. Но у вас есть два варианта решить эту проблему: либо вы ловите их по одному, или делать то, что следует:

@staticmethod
def get_connection():
try:
con = pymysql.connect("localhost", "root", "password", "counselor_db")
except MySQLError as error:
print('Exception number: {}, value {!r}'.format(error.args[0], error))
raise
else:
return con

Не смешивайте EAFP с LBYL

В повседневной жизни, это легче спросить разрешения, чем прощение. В Python, это наоборот:


Легче просить прощения, чем разрешения. Это общее питона
стиль программирования предполагает наличие действительных ключей или атрибутов и
ловит исключения, если предположение доказывает ложные. Это чистый и быстрый
стиль характеризуется наличием многих попробовать и за исключением
заявления. Техника контрастирует со стилем LBYL общими для многих
другие языки, такие как C.

Это означает, что он делает Сенс не смешивать try и if заявления как вы делали в close_connection(). Так что это будет лучше:

@staticmethod
def close_connection(conn, cursor):
try:
conn.close()
cursor.close()
except pymysql.DatabaseError as error:
print("While closing connection ", error)
raise

Помимо того, здесь опять же, функция не попадает общении с другими исключениями, как раньше, было бы лучше try блок занимается только один блок кода, который склонен к бедам. В этом случае, как cursor.close() и conn.close() может вызвать исключение. Так что я бы предпочел рассматривать их отдельно в два разных try блоки:

@staticmethod
def close_connection(conn, cursor):
try:
conn.close()
except MySQLError as error:
print("While closing connection ...")
print('Exception number: {}, value {!r}'.format(error.args[0], error))
raise
try:
cursor.close()
except MySQLError as error:
print("While closing the cursor ...")
print('Exception number: {}, value {!r}'.format(error.args[0], error))
raise

Вы можете даже создать 2 разных функции для этой цели, в случае, если вы очень дотошны с модульного тестирования.

Элегантность

Не критично, но все-таки важно: вы случайно оставить пустые строки в коде одну и ту же функцию. Вот несколько случаев, где можно оставить пустые строки:


Объемный функции верхнего уровня и определения классов двумя пустыми
линии.

Определения методов внутри класса окружены одной пустой
линии.

Лишние пустые строки могут использоваться (ограниченно) для отдельных групп
связанные с этим функции. Пустые строки могут быть опущены между кучей
связанные одной линии (например, набор муляжей реализации).

Используйте пустые строки в функциях, скупо, указать на логические разделы.

Питон принимает контроля-я (т. е. ^л) используются в качестве
пробелов; множество инструментов обработать эти символы в качестве разделителей страниц, так
вы можете использовать их на отдельных страницах соответствующих разделов файла.
Обратите внимание, некоторые редакторы и веб-просмотрщики код может не распознать
контроль-Л как форма подачи и покажу еще один символ на своем месте.

1
ответ дан 27 февраля 2018 в 09:02 Источник Поделиться