본문 바로가기
인터페이스 개발/Python

Python - 파일[예제] - Database Application

by cooluk 2020. 7. 29.

Chap.14 파일[예제] - Database Application

 

파일[예제] 

Database Application

 

>> pip install mysqlclient

 

현재 sqldb.tbladdr 상황

 

 

1단계(베이스)

뼈대 만들기

# DB+App.py

import sys
from myapp import Application, MenuItem


class DBApp(Application):  # 상속
    def __init__(self):
        super().__init__()

    def create_menu(self, menu):
        menu.add(MenuItem("목록", self.print_list))
        menu.add(MenuItem("추가", self.add))
        menu.add(MenuItem("수정", self.update))
        menu.add(MenuItem("삭제", self.remove))
        menu.add(MenuItem("종료", self.exit))

    def print_list(self):
        print("목록보기...")

    def add(self):
        print("추가")

    def update(self):
        print("수정")

    def remove(self):
        print("삭제")

    def exit(self):
        sys.exit(0)

if __name__ == '__main__':  # 단독 실행 여부 확인인
    app = DBApp()
    app.run()  # 부모 Class에 정의되어 있다.

결과

[메뉴] 0:목록  1:추가  2:수정  3:삭제  4:종료  
선택] 1
추가
[메뉴] 0:목록  1:추가  2:수정  3:삭제  4:종료  
선택] 2
수정
[메뉴] 0:목록  1:추가  2:수정  3:삭제  4:종료  
선택] 3
삭제
[메뉴] 0:목록  1:추가  2:수정  3:삭제  4:종료  
선택] 0
목록보기...
[메뉴] 0:목록  1:추가  2:수정  3:삭제  4:종료  
선택] 0
목록보기...
[메뉴] 0:목록  1:추가  2:수정  3:삭제  4:종료  
선택] 4

 

 

2단계(연결)

Connection & Close 어디서 해주어야 하나.

# DB+App.py

import sys
from myapp import Application, MenuItem
import MySQLdb

class DBApp(Application):  # 상속
    def __init__(self):
        super().__init__()
        self.db = MySQLdb.connect(db="sqldb", host="localhost",
                                  user="root", passwd="1234", charset='utf8')
        self.cursor = self.db.cursor()
# self.db가 아니라 db 로 했으면 지역변수 db를 쓰겠다는 뜻이므로 인스턴스 변수로 관리해야한다.

    def create_menu(self, menu):
        menu.add(MenuItem("목록", self.print_list))
        menu.add(MenuItem("추가", self.add))
        menu.add(MenuItem("수정", self.update))
        menu.add(MenuItem("삭제", self.remove))
        menu.add(MenuItem("종료", self.exit))

    def print_list(self):
        print("목록보기...")

    def add(self):
        print("추가")

    def update(self):
        print("수정")

    def remove(self):
        print("삭제")

    def exit(self):  # exit 할 때 커서와 db 닫아준다.
        answer = input("종료하시겠습니까?([y]/n) :")  # y 또는 Y 입력 받으면 종료
        if answer in ["y", "Y", ""]:  # "" : Enter만 입력받아도 종료 (디폴트 설정)
            self.cursor.close()
            self.db.close()
            sys.exit(0)
        else: app.run()


if __name__ == '__main__':
    app = DBApp()
    app.run()

결과

[메뉴] 0:목록  1:추가  2:수정  3:삭제  4:종료  
선택] 4
종료하시겠습니까?([y]/n) Y

 

 

3단계(목록)

목록보기 - print_list() ★★★

# DB+App.py

import sys
from myapp import Application, MenuItem
import MySQLdb

class DBApp(Application):
    def __init__(self):
        super().__init__()
        self.db = MySQLdb.connect(db="sqldb", host="localhost",
                                  user="root", passwd="1234", charset='utf8')
        self.cursor = self.db.cursor()

    def create_menu(self, menu):
        menu.add(MenuItem("목록", self.print_list))
        menu.add(MenuItem("추가", self.add))
        menu.add(MenuItem("수정", self.update))
        menu.add(MenuItem("삭제", self.remove))
        menu.add(MenuItem("종료", self.exit))

    def print_list(self):
        try:
            sql = "SELECT count(*) from tblAddr"  # 컬럼의 NULL 값을 신경 안쓴다.
            self.cursor.execute(sql)              # 결과가 있고 그 결과는 항상 한개이다.
            row = self.cursor.fetchone()          # 집계함수의 특성상 값이 항상 보장된다. (if 검사 필요 X)
            total = row[0]  # 전체 데이터 건수의 올바른 방법 (필터링할 때도 유지)

            sql = "SELECT * FROM tblAddr"
            self.cursor.execute(sql)  # 아직은 사용안하지만 영향받은 행의 수 리턴됨
            rows = self.cursor.fetchall()

            print("="*50)
            print("No  이름          전화번호    주소")
            print("-"*50)
            for ix, row in enumerate(rows, 1):  # No을 위해 enumerate 이용
                print(f"{ix} : {row[0]:10} {row[1]:10} {row[2]} ")  # ★★★ : 뒤는 폭을 의미
            print("=" * 50)
            print(f"(총 {total} 건)")
        except Exception as e:
            print(e)

    def add(self):
        print("추가")

    def update(self):
        print("수정")

    def remove(self):
        print("삭제")

    def exit(self):
        answer = input("종료하시겠습니까?([y]/n) ")
        if answer in ["y", "Y", ""]:
            self.cursor.close()
            self.db.close()
            sys.exit(0)


if __name__ == '__main__':
    app = DBApp()
    app.run()

결과

[메뉴] 0:목록  1:추가  2:수정  3:삭제  4:종료  
선택] 0
==================================================
No  이름          전화번호    주소
--------------------------------------------------
1 : 김상형        123-4567   오산 
2 : 한경은        555-1004   수원 
3 : 한주완        444-1092   대전 
4 : 홍길동        1111-1111  군포 
==================================================
(총 4 건)
[메뉴] 0:목록  1:추가  2:수정  3:삭제  4:종료  
선택] 

 

 

4단계(추가)

추가 - add()

# DB+App.py

import sys
from myapp import Application, MenuItem
import MySQLdb

class DBApp(Application):
    def __init__(self):
        super().__init__()
        self.db = MySQLdb.connect(db="sqldb", host="localhost",
                                  user="root", passwd="1234", charset='utf8')
        self.cursor = self.db.cursor()

    def create_menu(self, menu):
        menu.add(MenuItem("목록", self.print_list))
        menu.add(MenuItem("추가", self.add))
        menu.add(MenuItem("수정", self.update))
        menu.add(MenuItem("삭제", self.remove))
        menu.add(MenuItem("종료", self.exit))

    def exit(self):
        answer = input("종료하시겠습니까?([y]/n) ")
        if answer in ["y", "Y", ""]:
            self.cursor.close()
            self.db.close()
            sys.exit(0)

    def print_list(self):
        try:
            sql = "SELECT count(*) from tblAddr"  
            self.cursor.execute(sql)   
            row = self.cursor.fetchone()         
            total = row[0]  

            sql = "SELECT * FROM tblAddr"
            self.cursor.execute(sql) 
            rows = self.cursor.fetchall()

            print("="*50)
            print("No  이름          전화번호    주소")
            print("-"*50)
            for ix, row in enumerate(rows, 1):
                print(f"{ix} : {row[0]:10} {row[1]:10} {row[2]} ") 
            print("=" * 50)
            print(f"(총 {total} 건)")
        except Exception as e:
            print(e)

    def add(self):
        try:
            name  = input("이름   : ")
            phone = input("전화번호: ")
            addr  = input("주소   : ")
            sql = "INSERT INTO tblAddr VALUES(%s, %s, %s)"
            self.cursor.execute(sql, (name, phone, addr))
            self.db.commit()  # 실제 데이터베이스 반영
            # insert, update, delete 때는 필요하다!
            print("추가 완료")
        except Exception as e:
            print(e)

    def remove(self):
        print("삭제")

    def update(self):
        print("수정")

if __name__ == '__main__':
    app = DBApp()
    app.run()

결과

[메뉴] 0:목록  1:검색  2:추가  3:수정  4:삭제  5:종료  
선택] 2
이름   : 고길동
전화번호: 123-1234
주소   : 서울
추가 완료
[메뉴] 0:목록  1:검색  2:추가  3:수정  4:삭제  5:종료  
선택] 

 

 

5단계(삭제)

삭제 - remove()

# DB+App.py

import sys
from myapp import Application, MenuItem
import MySQLdb

class DBApp(Application):
    def __init__(self):
        super().__init__()
        self.db = MySQLdb.connect(db="sqldb", host="localhost",
                                  user="root", passwd="1234", charset='utf8')
        self.cursor = self.db.cursor()

    def create_menu(self, menu):
        menu.add(MenuItem("목록", self.print_list))
        menu.add(MenuItem("추가", self.add))
        menu.add(MenuItem("수정", self.update))
        menu.add(MenuItem("삭제", self.remove))
        menu.add(MenuItem("종료", self.exit))

    def exit(self):
        answer = input("종료하시겠습니까?([y]/n) ")
        if answer in ["y", "Y", ""]:
            self.cursor.close()
            self.db.close()
            sys.exit(0)

    def print_list(self):
        try:
            sql = "SELECT count(*) from tblAddr"  
            self.cursor.execute(sql)            
            row = self.cursor.fetchone()        
            total = row[0]  

            sql = "SELECT * FROM tblAddr"
            self.cursor.execute(sql) 
            rows = self.cursor.fetchall()

            print("="*50)
            print("No  이름          전화번호    주소")
            print("-"*50)
            for ix, row in enumerate(rows, 1): 
                print(f"{ix} : {row[0]:10} {row[1]:10} {row[2]} ") 
            print("=" * 50)
            print(f"(총 {total} 건)")
        except Exception as e:
            print(e)

    def add(self):
        try:
            name  = input("이름   : ")
            phone = input("전화번호: ")
            addr  = input("주소   : ")
            sql = "INSERT INTO tblAddr VALUES(%s, %s, %s)"
            self.cursor.execute(sql, (name, phone, addr))
            self.db.commit()  
            print("추가 완료")
        except Exception as e:
            print(e)

    def remove(self):
        try:
            name  = input("이름: ")
            sql = "DELETE FROM tblAddr WHERE name = %s"
            self.cursor.execute(sql, (name,))
            self.db.commit()  # ☆
            print("삭제 완료")
        except Exception as e:
            print(e)

    def update(self):
        print("수정")

if __name__ == '__main__':
    app = DBApp()
    app.run()

결과

[메뉴] 0:목록  1:검색  2:추가  3:수정  4:삭제  5:종료  
선택] 4
이름: 고길동
삭제 완료
[메뉴] 0:목록  1:검색  2:추가  3:수정  4:삭제  5:종료  
선택] 

 

 

6단계(수정)

수정 - update()

1. 이름 입력 받아서 데이터 있는지 확인하기

2. 기존의 데이터 보여주고, 그냥 넘어가면 수정하지 않게

# DB+App.py

import sys
from myapp import Application, MenuItem
import MySQLdb

class DBApp(Application):
    def __init__(self):
        super().__init__()
        self.db = MySQLdb.connect(db="sqldb", host="localhost",
                                  user="root", passwd="1234", charset='utf8')
        self.cursor = self.db.cursor()

    def create_menu(self, menu):
        menu.add(MenuItem("목록", self.print_list))
        menu.add(MenuItem("추가", self.add))
        menu.add(MenuItem("수정", self.update))
        menu.add(MenuItem("삭제", self.remove))
        menu.add(MenuItem("종료", self.exit))

    def exit(self):
        answer = input("종료하시겠습니까?([y]/n) ")
        if answer in ["y", "Y", ""]:
            self.cursor.close()
            self.db.close()
            sys.exit(0)

    def print_list(self):
        try:
            sql = "SELECT count(*) from tblAddr" 
            self.cursor.execute(sql)             
            row = self.cursor.fetchone()         
            total = row[0] 

            sql = "SELECT * FROM tblAddr"
            self.cursor.execute(sql)
            rows = self.cursor.fetchall()

            print("="*50)
            print("No  이름          전화번호    주소")
            print("-"*50)
            for ix, row in enumerate(rows, 1):
                print(f"{ix} : {row[0]:10} {row[1]:10} {row[2]} ") 
            print("=" * 50)
            print(f"(총 {total} 건)")
        except Exception as e:
            print(e)

    def add(self):
        try:
            name  = input("이름   : ")
            phone = input("전화번호: ")
            addr  = input("주소   : ")
            sql = "INSERT INTO tblAddr VALUES(%s, %s, %s)"
            self.cursor.execute(sql, (name, phone, addr))
            self.db.commit()  # 실제 데이터베이스 반영
            # insert, update, delete 때는 필요하다!
            print("추가 완료")
        except Exception as e:
            print(e)

    def remove(self):
        try:
            name  = input("이름: ")
            sql = "DELETE FROM tblAddr WHERE name = %s"
            self.cursor.execute(sql, (name,))
            self.db.commit() 
            print("삭제 완료")
        except Exception as e:
            print(e)

    def update(self):
        try:
            name  = input("이름: ")
            sql = "SELECT * FROM tblAddr WHERE name = %s"
            self.cursor.execute(sql, (name,))
            data = self.cursor.fetchone()
            if not data:  # 데이터가 없다면
                print(f"{name} 데이터가 없습니다.")
                return  # 리턴

            phone = input(f"전화번호({data[1]}): ")
            if not phone:  # 비어있는 문자열 받았을 때
                phone = data[1]
            addr = input(f"주소({data[2]}): ")
            if not addr:  # 비어있는 문자열 받았을 때
                addr = data[2]

            sql = """
            UPDATE tblAddr SET
                phone = %s,
                addr = %s
            WHERE name = %s
            """
            
            self.cursor.execute(sql, (phone, addr, name))  # 순서 신경쓰자
            self.db.commit()
            print("수정 완료")

        except Exception as e:
            print(e)

if __name__ == '__main__':
    app = DBApp()
    app.run()

결과

[메뉴] 0:목록  1:검색  2:추가  3:수정  4:삭제  5:종료  
선택] 3
이름: 홍길동 
전화번호(1111-1111): 123-1234
주소(군포): 
수정 완료
[메뉴] 0:목록  1:검색  2:추가  3:수정  4:삭제  5:종료  
선택] 0
==================================================
No   이름        전화번호      주소         
--------------------------------------------------
1 : 김상형        123-4567   오산 
2 : 한경은        555-1004   수원 
3 : 한주완        444-1092   대전 
4 : 홍길동        123-1234   군포 
==================================================
(총 4 건)
[메뉴] 0:목록  1:검색  2:추가  3:수정  4:삭제  5:종료  
선택] 

 

 

 

7단계(검색) - 메뉴 추가

# DB+App.py

import sys
from myapp import Application, MenuItem
import MySQLdb


class DBApp(Application):
    def __init__(self):
        super().__init__()
        self.db = MySQLdb.connect(db="sqldb", host="localhost",
                                  user="root", passwd="1234", charset='utf8')
        self.cursor = self.db.cursor()

    def create_menu(self, menu):
        menu.add(MenuItem("목록", self.print_list))
        menu.add(MenuItem("검색", self.search))  # 검색 메뉴 추가
        menu.add(MenuItem("추가", self.add))
        menu.add(MenuItem("수정", self.update))
        menu.add(MenuItem("삭제", self.remove))
        menu.add(MenuItem("종료", self.exit))

    def exit(self):
        answer = input("종료하시겠습니까?([y]/n) ")
        if answer in ["y", "Y", ""]:
            self.cursor.close()
            self.db.close()
            sys.exit(0)

    def print_list(self):
        try:
            sql = "SELECT count(*) from tblAddr"  
            self.cursor.execute(sql)  
            row = self.cursor.fetchone()  
            total = row[0]  

            sql = "SELECT * FROM tblAddr"
            self.cursor.execute(sql) 
            rows = self.cursor.fetchall()

            print("=" * 50)
            print("No  이름          전화번호    주소")
            print("-" * 50)
            for ix, row in enumerate(rows, 1):  
                print(f"{ix} : {row[0]:10} {row[1]:10} {row[2]} ")  
            print("=" * 50)
            print(f"(총 {total} 건)")
        except Exception as e:
            print(e)

    def add(self):
        try:
            name = input("이름   : ")
            phone = input("전화번호: ")
            addr = input("주소   : ")
            sql = "INSERT INTO tblAddr VALUES(%s, %s, %s)"
            self.cursor.execute(sql, (name, phone, addr))
            self.db.commit()  
            print("추가 완료")
        except Exception as e:
            print(e)

    def remove(self):
        try:
            name = input("이름: ")
            sql = "DELETE FROM tblAddr WHERE name = %s"
            self.cursor.execute(sql, (name,))
            self.db.commit() 
            print("삭제 완료")
        except Exception as e:
            print(e)

    def update(self):
        try:
            name = input("이름: ")
            sql = "SELECT * FROM tblAddr WHERE name = %s"
            self.cursor.execute(sql, (name,))
            data = self.cursor.fetchone()
            if not data: 
                print(f"{name} 데이터가 없습니다.")
                return 

            phone = input(f"전화번호({data[1]}): ")
            if not phone:  
                phone = data[1]
            addr = input(f"주소({data[2]}): ")
            if not addr:  
                addr = data[2]

            sql = """
            UPDATE tblAddr SET
                phone = %s,
                addr = %s
            WHERE name = %s
            """

            self.cursor.execute(sql, (phone, addr, name))  
            self.db.commit()
            print("수정 완료")

        except Exception as e:
            print(e)

    def search(self):
        try:
            name = input("이름   : ")
            sql = f"SELECT count(*) from tblAddr WHERE name LIKE '%{name}%'"
            self.cursor.execute(sql)
            row = self.cursor.fetchone()
            total = row[0]

            sql = f"SELECT * from tblAddr WHERE name LIKE '%{name}%'"
            self.cursor.execute(sql)
            rows = self.cursor.fetchall()

            print("=" * 50)
            header = ("이름", "전화번호", "주소")
            print(f"No   {header[0]:10}{header[1]:10}{header[2]:10} ")
            print("-" * 50)

            for ix, row in enumerate(rows, 1):  # No을 위해 enumerate 이용
                print(f"{ix} : {row[0]:10} {row[1]:10} {row[2]} ")  # ★★★ : 뒤는 폭을 의미
            print("=" * 50)
            print(f"(총 {total} 건)")

        except Exception as e:
            print(e)


if __name__ == '__main__':
    app = DBApp()
    app.run()

결과

[메뉴] 0:목록  1:검색  2:추가  3:수정  4:삭제  5:종료  
선택] 1
이름   : 
==================================================
No   이름        전화번호      주소         
--------------------------------------------------
1 : 한경은        555-1004   수원 
2 : 한주완        444-1092   대전 
==================================================
(총 2 건)
[메뉴] 0:목록  1:검색  2:추가  3:수정  4:삭제  5:종료  
선택] 

 

 

 

 

method 화(1)

# addr_ui.py

def print_list(total, rows):
    print("=" * 50)
    header = ("이름", "전화번호", "주소")
    print(f"No   {header[0]:10}{header[1]:10}{header[2]:10} ")
    print("-" * 50)
    for ix, row in enumerate(rows, 1):  # No을 위해 enumerate 이용
        print(f"{ix} : {row[0]:10} {row[1]:10} {row[2]} ")  # ★★★ : 뒤는 폭을 의미
    print("=" * 50)
    print(f"(총 {total} 건)")
# addr_repository.py

class AddressRepositrory:

    def __init__(self, db):        # App은 db객체만 다루게 하기 위해
        self.cursor = db.cursor()  # SQL 실행을 위함

    def close(self):
        self.cursor.close()

    def get_total(self):  # 데이터 건수 함수
        sql = "SELECT count(*) FROM tblAddr"
        self.cursor.execute(sql)
        row = self.cursor.fetchone()
        return row[0]

    def get_list(self):
        sql = "SELECT * FROM tblAddr"
        self.cursor.execute(sql)
        rows = self.cursor.fetchall()
        return rows

# DB+App.py

import sys
from myapp import Application, MenuItem
import MySQLdb
from addr_repository import AddressRepositrory  # 새로 만든 모듈 import
from addr_ui import *  # 새로 만든 모듈 import

class DBApp(Application):
    def __init__(self):
        super().__init__()
        self.db = MySQLdb.connect(db="sqldb", host="localhost",
                                  user="root", passwd="1234", charset='utf8')
        # self.cursor = self.db.cursor()  # 이제 필요없다.
        self.repo = AddressRepositrory(self.db)

    def create_menu(self, menu):
        menu.add(MenuItem("목록", self.print_list))
        menu.add(MenuItem("검색", self.search))  # 검색 메뉴 추가
        menu.add(MenuItem("추가", self.add))
        menu.add(MenuItem("수정", self.update))
        menu.add(MenuItem("삭제", self.remove))
        menu.add(MenuItem("종료", self.exit))

    def exit(self):
        answer = input("종료하시겠습니까?([y]/n) ")
        if answer in ["y", "Y", ""]:
            # self.cursor.close()
            self.repo.close()
            self.db.close()
            sys.exit(0)

    def print_list(self):
        try:
            # sql = "SELECT count(*) from tblAddr"  # 컬럼의 NULL 값을 신경 안쓴다.
            # self.cursor.execute(sql)              # 결과가 있고 그 결과는 항상 한개이다.
            # row = self.cursor.fetchone()          # 집계함수의 특성상 값이 항상 보장된다. (if 검사 필요 X)
            # total = row[0]  # 전체 데이터 건수의 올바른 방법 (필터링할 때도 유지)

            total = self.repo.get_total()  # 이렇게 한줄로 처리가능

            # sql = "SELECT * FROM tblAddr"
            # self.cursor.execute(sql)  # 아직은 사용안하지만 영향받은 행의 수 리턴됨
            # rows = self.cursor.fetchall()

            rows = self.repo.get_list()

            # print("="*50)
            # print("No  이름          전화번호    주소")
            # print("-"*50)
            # for ix, row in enumerate(rows, 1):  # No을 위해 enumerate 이용
            #     print(f"{ix} : {row[0]:10} {row[1]:10} {row[2]} ")  # ★★★ : 뒤는 폭을 의미
            # print("=" * 50)
            # print(f"(총 {total} 건)")

            print_list(total, rows)

        except Exception as e:
            print(e)

    def add(self):
        try:
            name  = input("이름   : ")
            phone = input("전화번호: ")
            addr  = input("주소   : ")
            sql = "INSERT INTO tblAddr VALUES(%s, %s, %s)"
            self.cursor.execute(sql, (name, phone, addr))
            self.db.commit()  # 실제 데이터베이스 반영
            # insert, update, delete 때는 필요하다!
            print("추가 완료")
        except Exception as e:
            print(e)

    def remove(self):
        try:
            name  = input("이름: ")
            sql = "DELETE FROM tblAddr WHERE name = %s"
            self.cursor.execute(sql, (name,))
            self.db.commit()  # ☆
            print("삭제 완료")
        except Exception as e:
            print(e)

    def update(self):
        try:
            name  = input("이름: ")
            sql = "SELECT * FROM tblAddr WHERE name = %s"
            self.cursor.execute(sql, (name,))
            data = self.cursor.fetchone()
            if not data:  # 데이터가 없다면
                print(f"{name} 데이터가 없습니다.")
                return  # 리턴

            phone = input(f"전화번호({data[1]}): ")
            if not phone:  # 비어있는 문자열 받았을 때
                phone = data[1]
            addr = input(f"주소({data[2]}): ")
            if not addr:  # 비어있는 문자열 받았을 때
                addr = data[2]

            sql = """
            UPDATE tblAddr SET
                phone = %s,
                addr = %s
            WHERE name = %s
            """

            self.cursor.execute(sql, (phone, addr, name))  # 순서 신경쓰자
            self.db.commit()
            print("수정 완료")

        except Exception as e:
            print(e)


    def search(self):
        try:
            name = input("이름   : ")
            sql = f"SELECT count(*) from tblAddr WHERE name LIKE '%{name}%'"
            self.cursor.execute(sql)
            row = self.cursor.fetchone()
            total = row[0]

            sql = f"SELECT * from tblAddr WHERE name LIKE '%{name}%'"
            self.cursor.execute(sql)
            rows = self.cursor.fetchall()

            print("=" * 50)
            header = ("이름", "전화번호", "주소")
            print(f"No   {header[0]:10}{header[1]:10}{header[2]:10} ")
            print("-" * 50)

        except Exception as e:
            print(e)

if __name__ == '__main__':
    app = DBApp()
    app.run()

 

method 화(2)

# addr_ui.py

def print_list(total, rows):
    print("=" * 50)
    header = ("이름", "전화번호", "주소")
    print(f"No   {header[0]:10}{header[1]:10}{header[2]:10} ")
    print("-" * 50)
    for ix, row in enumerate(rows, 1):  # No을 위해 enumerate 이용
        print(f"{ix} : {row[0]:10} {row[1]:10} {row[2]} ")  # ★★★ : 뒤는 폭을 의미
    print("=" * 50)
    print(f"(총 {total} 건)")

# addr_repository.py

class AddressRepositrory:

    def __init__(self, db):        # App은 db객체만 다루게 하기 위해
        self.cursor = db.cursor()  # SQL 실행을 위함

    def close(self):
        self.cursor.close()

    def get_total(self, where=''):  # 디폴드는 where절 없는거
        sql = "SELECT count(*) FROM tblAddr " + where  # 주의! 스페이스
        self.cursor.execute(sql)
        row = self.cursor.fetchone()
        return row[0]

    def get_list(self, where=''):
        sql = "SELECT * FROM tblAddr " + where
        self.cursor.execute(sql)
        rows = self.cursor.fetchall()
        return rows

# DB+App.py

import sys
from myapp import Application, MenuItem
import MySQLdb
from addr_repository import AddressRepositrory  # 새로 만든 모듈 import
from addr_ui import *  # 새로 만든 모듈 import

class DBApp(Application):
    def __init__(self):
        super().__init__()
        self.db = MySQLdb.connect(db="sqldb", host="localhost",
                                  user="root", passwd="1234", charset='utf8')
        # self.cursor = self.db.cursor()  # 이제 필요없다.
        self.repo = AddressRepositrory(self.db)

    def create_menu(self, menu):
        menu.add(MenuItem("목록", self.print_list))
        menu.add(MenuItem("검색", self.search))  # 검색 메뉴 추가
        menu.add(MenuItem("추가", self.add))
        menu.add(MenuItem("수정", self.update))
        menu.add(MenuItem("삭제", self.remove))
        menu.add(MenuItem("종료", self.exit))

    def exit(self):
        answer = input("종료하시겠습니까?([y]/n) ")
        if answer in ["y", "Y", ""]:
            # self.cursor.close()
            self.repo.close()
            self.db.close()
            sys.exit(0)

    def print_list(self):
        try:
            # sql = "SELECT count(*) from tblAddr"  # 컬럼의 NULL 값을 신경 안쓴다.
            # self.cursor.execute(sql)              # 결과가 있고 그 결과는 항상 한개이다.
            # row = self.cursor.fetchone()          # 집계함수의 특성상 값이 항상 보장된다. (if 검사 필요 X)
            # total = row[0]  # 전체 데이터 건수의 올바른 방법 (필터링할 때도 유지)

            total = self.repo.get_total()  # 이렇게 한줄로 처리가능

            # sql = "SELECT * FROM tblAddr"
            # self.cursor.execute(sql)  # 아직은 사용안하지만 영향받은 행의 수 리턴됨
            # rows = self.cursor.fetchall()

            rows = self.repo.get_list()

            # print("="*50)
            # print("No  이름          전화번호    주소")
            # print("-"*50)
            # for ix, row in enumerate(rows, 1):  # No을 위해 enumerate 이용
            #     print(f"{ix} : {row[0]:10} {row[1]:10} {row[2]} ")  # ★★★ : 뒤는 폭을 의미
            # print("=" * 50)
            # print(f"(총 {total} 건)")

            print_list(total, rows)

        except Exception as e:
            print(e)

    def add(self):
        try:
            name  = input("이름   : ")
            phone = input("전화번호: ")
            addr  = input("주소   : ")
            sql = "INSERT INTO tblAddr VALUES(%s, %s, %s)"
            self.cursor.execute(sql, (name, phone, addr))
            self.db.commit()  # 실제 데이터베이스 반영
            # insert, update, delete 때는 필요하다!
            print("추가 완료")
        except Exception as e:
            print(e)

    def remove(self):
        try:
            name  = input("이름: ")
            sql = "DELETE FROM tblAddr WHERE name = %s"
            self.cursor.execute(sql, (name,))
            self.db.commit()  # ☆
            print("삭제 완료")
        except Exception as e:
            print(e)

    def update(self):
        try:
            name  = input("이름: ")
            sql = "SELECT * FROM tblAddr WHERE name = %s"
            self.cursor.execute(sql, (name,))
            data = self.cursor.fetchone()
            if not data:  # 데이터가 없다면
                print(f"{name} 데이터가 없습니다.")
                return  # 리턴

            phone = input(f"전화번호({data[1]}): ")
            if not phone:  # 비어있는 문자열 받았을 때
                phone = data[1]
            addr = input(f"주소({data[2]}): ")
            if not addr:  # 비어있는 문자열 받았을 때
                addr = data[2]

            sql = """
            UPDATE tblAddr SET
                phone = %s,
                addr = %s
            WHERE name = %s
            """

            self.cursor.execute(sql, (phone, addr, name))  # 순서 신경쓰자
            self.db.commit()
            print("수정 완료")

        except Exception as e:
            print(e)


    def search(self):
        try:
            name = input("이름   : ")

            # sql = f"SELECT count(*) from tblAddr WHERE name LIKE '%{name}%'"
            # self.cursor.execute(sql)
            # row = self.cursor.fetchone()
            # total = row[0]
            #
            # sql = f"SELECT * from tblAddr WHERE name LIKE '%{name}%'"
            # self.cursor.execute(sql)
            # rows = self.cursor.fetchall()
            #
            # print("=" * 50)
            # header = ("이름", "전화번호", "주소")
            # print(f"No   {header[0]:10}{header[1]:10}{header[2]:10} ")
            # print("-" * 50)
            # for ix, row in enumerate(rows, 1):  # No을 위해 enumerate 이용
            #     print(f"{ix} : {row[0]:10} {row[1]:10} {row[2]} ")  # ★★★ : 뒤는 폭을 의미
            # print("=" * 50)
            # print(f"(총 {total} 건)")

            where = f"WHERE name LIKE '%{name}%'"
            total = self.repo.get_total(where)
            rows = self.repo.get_list(where)
            print_list(total, rows)

        except Exception as e:
            print(e)

if __name__ == '__main__':
    app = DBApp()
    app.run()

 

method 화(3)

# addr_ui.py

def print_list(total, rows):
    print("=" * 50)
    header = ("이름", "전화번호", "주소")
    print(f"No   {header[0]:10}{header[1]:10}{header[2]:10} ")
    print("-" * 50)
    for ix, row in enumerate(rows, 1):  # No을 위해 enumerate 이용
        print(f"{ix} : {row[0]:10} {row[1]:10} {row[2]} ")  # ★★★ : 뒤는 폭을 의미
    print("=" * 50)
    print(f"(총 {total} 건)")

def input_addr_info():
    name  = input("이름   : ")
    phone = input("전화번호: ")
    addr  = input("주소   : ")
    return name, phone, addr  # 튜플로 리턴된다.

def input_now_addr(data):
    phone = input(f"전화번호({data[1]}): ")
    if not phone:
        phone = data[1]
    addr = input(f"주소({data[2]}): ")
    if not addr:
        addr = data[2]

    return data[0], phone, addr
# addr_repository.py

class AddressRepositrory:

    def __init__(self, db):        # App은 db객체만 다루게 하기 위해
        self.cursor = db.cursor()  # SQL 실행을 위함

    def close(self):
        self.cursor.close()

    def get_total(self, where=''):  # 디폴드는 where절 없는거
        sql = "SELECT count(*) FROM tblAddr " + where  # 주의! 스페이스
        self.cursor.execute(sql)
        row = self.cursor.fetchone()
        return row[0]

    def get_list(self, where=''):
        sql = "SELECT * FROM tblAddr " + where
        self.cursor.execute(sql)
        rows = self.cursor.fetchall()
        return rows

    def insert(self, data):
        sql = "INSERT INTO tblAddr VALUES(%s, %s, %s)"
        self.cursor.execute(sql, data)

    def remove(self, name):
        sql = "DELETE FROM tblAddr WHERE name = %s"
        self.cursor.execute(sql, (name,))

    def get_one(self, name):
        sql = "SELECT * FROM tblAddr WHERE name = %s"
        self.cursor.execute(sql, (name,))
        return self.cursor.fetchone()

    def update(self, data):
        sql = """
            UPDATE tblAddr
            SET
                phone = %s,
                addr = %s
            WHERE name = %s
        """
        self.cursor.execute(sql, (data[1], data[2], data[0]))
# DB+App.py

import sys
from myapp import Application, MenuItem
import MySQLdb
from addr_repository import AddressRepositrory  # 새로 만든 모듈 import
from addr_ui import *  # 새로 만든 모듈 import

class DBApp(Application):
    def __init__(self):
        super().__init__()
        self.db = MySQLdb.connect(db="sqldb", host="localhost",
                                  user="root", passwd="1234", charset='utf8')
        # self.cursor = self.db.cursor()  # 이제 필요없다.
        self.repo = AddressRepositrory(self.db)

    def create_menu(self, menu):
        menu.add(MenuItem("목록", self.print_list))
        menu.add(MenuItem("검색", self.search))  # 검색 메뉴 추가
        menu.add(MenuItem("추가", self.add))
        menu.add(MenuItem("수정", self.update))
        menu.add(MenuItem("삭제", self.remove))
        menu.add(MenuItem("종료", self.exit))

    def exit(self):
        answer = input("종료하시겠습니까?([y]/n) ")
        if answer in ["y", "Y", ""]:
            # self.cursor.close()
            self.repo.close()
            self.db.close()
            sys.exit(0)

    def print_list(self):
        try:
            # sql = "SELECT count(*) from tblAddr"  # 컬럼의 NULL 값을 신경 안쓴다.
            # self.cursor.execute(sql)              # 결과가 있고 그 결과는 항상 한개이다.
            # row = self.cursor.fetchone()          # 집계함수의 특성상 값이 항상 보장된다. (if 검사 필요 X)
            # total = row[0]  # 전체 데이터 건수의 올바른 방법 (필터링할 때도 유지)

            total = self.repo.get_total()  # 이렇게 한줄로 처리가능

            # sql = "SELECT * FROM tblAddr"
            # self.cursor.execute(sql)  # 아직은 사용안하지만 영향받은 행의 수 리턴됨
            # rows = self.cursor.fetchall()

            rows = self.repo.get_list()

            # print("="*50)
            # print("No  이름          전화번호    주소")
            # print("-"*50)
            # for ix, row in enumerate(rows, 1):  # No을 위해 enumerate 이용
            #     print(f"{ix} : {row[0]:10} {row[1]:10} {row[2]} ")  # ★★★ : 뒤는 폭을 의미
            # print("=" * 50)
            # print(f"(총 {total} 건)")

            print_list(total, rows)

        except Exception as e:
            print(e)

    def add(self):
        try:
            # name  = input("이름   : ")
            # phone = input("전화번호: ")
            # addr  = input("주소   : ")

            data = input_addr_info()

            # sql = "INSERT INTO tblAddr VALUES(%s, %s, %s)"
            # self.cursor.execute(sql, (name, phone, addr))
            # self.cursor.execute(sql, (name, phone, addr))

            self.repo.insert(data)

            self.db.commit()  # 실제 데이터베이스 반영
            # insert, update, delete 때는 필요하다!
            print("추가 완료")
        except Exception as e:
            print(e)

    def remove(self):
        try:
            name  = input("이름: ")
            # sql = "DELETE FROM tblAddr WHERE name = %s"
            # self.cursor.execute(sql, (name,))

            self.repo.remove(name)

            self.db.commit()  # ☆
            print("삭제 완료")
        except Exception as e:
            print(e)

    def update(self):
        try:
            name  = input("이름: ")
            # sql = "SELECT name FROM tblAddr WHERE name = %s"
            # self.cursor.execute(sql, (name,))
            # data = self.cursor.fetchone()

            data = self.repo.get_one(name)

            if not data:  # 데이터가 없다면
                print(f"{name} 데이터가 없습니다.")
                return  # 리턴

            # phone = input(f"전화번호({data[1]}): ")
            # if not phone:  # 비어있는 문자열 받았을 때
            #     phone = data[1]
            # addr = input(f"주소({data[2]}): ")
            # if not addr:  # 비어있는 문자열 받았을 때
            #     addr = data[2]
            #
            # sql = """
            # UPDATE tblAddr SET
            #     phone = %s,
            #     addr = %s
            # WHERE name = %s
            # """
            #
            # self.cursor.execute(sql, (phone, addr, name))  # 순서 신경쓰자

            data = input_new_addr(data)
            self.repo.update(data)

            self.db.commit()
            print("수정 완료")

        except Exception as e:
            print(e)


    def search(self):
        try:
            name = input("이름   : ")

            # sql = f"SELECT count(*) from tblAddr WHERE name LIKE '%{name}%'"
            # self.cursor.execute(sql)
            # row = self.cursor.fetchone()
            # total = row[0]
            #
            # sql = f"SELECT * from tblAddr WHERE name LIKE '%{name}%'"
            # self.cursor.execute(sql)
            # rows = self.cursor.fetchall()
            #
            # print("=" * 50)
            # header = ("이름", "전화번호", "주소")
            # print(f"No   {header[0]:10}{header[1]:10}{header[2]:10} ")
            # print("-" * 50)
            # for ix, row in enumerate(rows, 1):  # No을 위해 enumerate 이용
            #     print(f"{ix} : {row[0]:10} {row[1]:10} {row[2]} ")  # ★★★ : 뒤는 폭을 의미
            # print("=" * 50)
            # print(f"(총 {total} 건)")

            where = f"WHERE name LIKE '%{name}%'"
            total = self.repo.get_total(where)
            rows = self.repo.get_list(where)
            print_list(total, rows)

        except Exception as e:
            print(e)

if __name__ == '__main__':
    app = DBApp()
    app.run()

 

 

예외처리 - myapp.py

# myapp.py

class MenuItem:

    def __init__(self, title, action=None):
        self.title = title
        self.action = action

    def __str__(self):
        return f"<MenuItem {self.title}>"

    def __repr__(self):
        return f"<MenuItem {self.title}>"

    def run(self):
        self.action()


class Application:
    def __init__(self):
        self.menu = Menu()
        self.create_menu(self.menu)  

    def create_menu(self, menu): 
        pass  

    def run(self): 
        while True:
            try:  # 예외 처리 여기서!!!
                self.menu.print()
                sel = int(input("선택] "))
                self.menu.run(sel)
            except Exception as e:
                print(e)

class Menu:

    def __init__(self):
        self.menus = []

    def add(self, menu_item):
        self.menus.append(menu_item)

    def print(self):
        print("[메뉴]", end=' ')
        for i, menu in enumerate(self.menus):
            print(f"{i}:{menu.title}  ", end="")
        print()

    def run(self, select):
        if select >= len(self.menus):
            print("잘못된 메뉴 선택입니다")
            return
        self.menus[select].run()
# DB+App.py

import sys
from myapp import Application, MenuItem
import MySQLdb
from addr_repository import AddressRepositrory  
from addr_ui import * 

class DBApp(Application):
    def __init__(self):
        super().__init__()
        self.db = MySQLdb.connect(db="sqldb", host="localhost",
                                  user="root", passwd="1234", charset='utf8')
        self.repo = AddressRepositrory(self.db)

    def create_menu(self, menu):
        menu.add(MenuItem("목록", self.print_list))
        menu.add(MenuItem("검색", self.search))
        menu.add(MenuItem("추가", self.add))
        menu.add(MenuItem("수정", self.update))
        menu.add(MenuItem("삭제", self.remove))
        menu.add(MenuItem("종료", self.exit))

    def exit(self):
        answer = input("종료하시겠습니까?([y]/n) ")
        if answer in ["y", "Y", ""]:
            self.repo.close()
            self.db.close()
            sys.exit(0)

    def print_list(self):
        total = self.repo.get_total()
        rows = self.repo.get_list()
        print_list(total, rows)


    def add(self):
        data = input_addr_info()
        self.repo.insert(data)
        self.db.commit()
        print("추가 완료")

    def remove(self):
        name  = input("이름: ")
        self.repo.remove(name)
        self.db.commit()
        print("삭제 완료")


    def update(self):
        name  = input("이름: ")
        data = self.repo.get_one(name)
        if not data: 
            print(f"{name} 데이터가 없습니다.")
            return 

        data = input_now_addr(data)
        self.repo.update(data)
        self.db.commit()
        print("수정 완료")



    def search(self):
        name = input("이름   : ")
        where = f"WHERE name LIKE '%{name}%'"
        total = self.repo.get_total(where)
        rows = self.repo.get_list(where)
        print_list(total, rows)


if __name__ == '__main__':
    app = DBApp()
    app.run()

 

 

 

최종 (가독성)

addr_models.py 만들기

# addr_models.py

class Addr: 
    def __init__(self, name, phone, addr):
        self.name = name     # 테이블의 컬럼명과 같다.
        self.phone = phone   # 테이블의 컬럼명과 같다.
        self.addr = addr     # 테이블의 컬럼명과 같다.

    def __str__(self):
        return f"<Addr {self.name}/{self.phone}/{self.addr}>"

    def __repr__(self):
        return f"<Addr {self.name}>"


# addr_ui.py

from addr_models import Addr  # 임폴트

def print_list(total, rows):
    print("=" * 50)
    header = ("이름", "전화번호", "주소")
    print(f"No   {header[0]:10}{header[1]:10}{header[2]:10} ")
    print("-" * 50)
    for ix, row in enumerate(rows, 1):
        print(f"{ix} : {row.name:10} {row.phone:10} {row.addr:10} ")  # 인덱스 대신 필드 변수
    print("=" * 50)
    print(f"(총 {total} 건)")

def input_addr_info():
    name  = input("이름   : ")
    phone = input("전화번호: ")
    addr  = input("주소   : ")
    return Addr(name, phone, addr)  # 튜플 대신

def input_now_addr(data):
    phone = input(f"전화번호({data.phone}): ")  # 튜플 대신
    if not phone:
        phone = data.phone  # 튜플 대신
    addr = input(f"주소({data.addr}): ")  # 튜플 대신
    if not addr:
        addr = data.addr  # 튜플 대신

    return Addr(data.name, phone, addr)  # 튜플 대신
# addr_repository.py

from addr_models import Addr

class AddressRepositrory:

    def __init__(self, db):        # App은 db객체만 다루게 하기 위해
        self.cursor = db.cursor()  # SQL 실행을 위함

    def close(self):
        self.cursor.close()

    def get_total(self, where=''):  # 디폴드는 where절 없는거
        sql = "SELECT count(*) FROM tblAddr " + where  # 주의! 스페이스
        self.cursor.execute(sql)
        row = self.cursor.fetchone()
        return row[0]

    def get_list(self, where=''):
        sql = "SELECT * FROM tblAddr " + where
        self.cursor.execute(sql)
        rows = self.cursor.fetchall()
        return (Addr(*row) for row in rows)  # 변경


    def get_one(self, name):
        sql = "SELECT * FROM tblAddr WHERE name = %s"
        self.cursor.execute(sql, (name,))
        # return self.cursor.fetchone()
        row = self.cursor.fetchone()
        # addr = Addr(*row)  # 펼쳐준다.
        # return addr
        if row:  # 변경
            return Addr(*row)

    def insert(self, data):
        sql = "INSERT INTO tblAddr VALUES(%s, %s, %s)"
        self.cursor.execute(sql, (data.name, data.phone, data.addr))  # 변경

    def remove(self, name):
        sql = "DELETE FROM tblAddr WHERE name = %s"
        self.cursor.execute(sql, (name,))


    def update(self, data):
        sql = """
            UPDATE tblAddr
            SET
                phone = %s,
                addr = %s
            WHERE name = %s
        """
        self.cursor.execute(sql, (data.phone, data.addr, data.name))
# DB+App.py

import sys
from myapp import Application, MenuItem
import MySQLdb
from addr_repository import AddressRepositrory
from addr_ui import *

class DBApp(Application):
    def __init__(self):
        super().__init__()
        self.db = MySQLdb.connect(db="sqldb", host="localhost",
                                  user="root", passwd="1234", charset='utf8')
        self.repo = AddressRepositrory(self.db)

    def create_menu(self, menu):
        menu.add(MenuItem("목록", self.print_list))
        menu.add(MenuItem("검색", self.search))
        menu.add(MenuItem("추가", self.add))
        menu.add(MenuItem("수정", self.update))
        menu.add(MenuItem("삭제", self.remove))
        menu.add(MenuItem("종료", self.exit))

    def exit(self):
        answer = input("종료하시겠습니까?([y]/n) ")
        if answer in ["y", "Y", ""]:
            self.repo.close()
            self.db.close()
            sys.exit(0)

    def print_list(self):
        total = self.repo.get_total()
        rows = self.repo.get_list()
        print_list(total, rows)


    def add(self):
        data = input_addr_info()
        self.repo.insert(data)
        self.db.commit()
        print("추가 완료")

    def remove(self):
        name  = input("이름: ")
        self.repo.remove(name)
        self.db.commit()
        print("삭제 완료")


    def update(self):
        name  = input("이름: ")
        data = self.repo.get_one(name)
        if not data:
            print(f"{name} 데이터가 없습니다.")
            return

        data = input_now_addr(data)
        self.repo.update(data)
        self.db.commit()
        print("수정 완료")



    def search(self):
        name = input("이름   : ")
        where = f"WHERE name LIKE '%{name}%'"
        total = self.repo.get_total(where)
        rows = self.repo.get_list(where)
        print_list(total, rows)


if __name__ == '__main__':
    app = DBApp()
    app.run()

Application에서는 수정사항이 없다.
클래스와 테이블이 매핑된다.
인스턴스 하나가 표현하는게 테이블의 행에 해당한다.

댓글