[Python 05] SQLite 연동 - 입력, 삭제, 수정

Review

테이블에 데이터 입력

INSERT INTO topics (title, body) VALUES ('SQLite', 'SQLite is ...');

테이블에서 데이터 조회

SELECT * FROM topics;


입력한 데이터가 잘 출력되는 것을 볼 수 있다.

SQLite를 Python에서 연동

import sqlite3
conn = sqlite3.connect('db.sqlite3')
cursor = conn.cursor()

Python에는 SQLite가 내장되어 있기 때문에 따로 설치하지 않아도 바로 import 해서 사용할 수 있다.
sqlite.connect() 함수로 DB 파일을 열고, cursor를 설정해준다.

Python에서 SQLite 쿼리 실행

cursor.execute('SELECT * FROM topics;')
topics = cursor.fetchall()

cursor.execute() 함수로 쿼리를 실행할 수 있다. 쿼리가 실행된 결과의 가장 앞에 cursor가 위치하고, cursor.fetchall() 함수로 해당 내용을 전부 가져올 수 있다.
이렇게 가져온 정보를 print해보자.

print(topics)

구문을 python 파일에 추가하고, 터미널에서 방금 작성한 python 파일을 실행한다.

topics에 DB 내용이 잘 들어왔다.

topics 테이블을 읽어오는 read.py

import sqlite3
conn = sqlite3.connect('db.sqlite3')
cursor = conn.cursor()
cursor.execute('SELECT * FROM topics;')
topics = cursor.fetchall()
for topic in topics:
  print(topic[0], topic[1])
conn.close()


출력이 잘 된다.

topics 테이블에 입력하는 create.py

import sqlite3
conn = sqlite3.connect('db.sqlite3')
cursor = conn.cursor()

title = input('title : ')
body = input('body : ')

cursor.execute('INSERT INTO topics (title, body) VALUES (?, ?);', (title, body))
conn.commit()
conn.close()

import read

마지막에 import read 해주어서 입력 후 바로 테이블 내용을 출력하게 해주었다.

server.py에 SQLite 연동

각 함수마다 sqlite connection을 생성한다. conn.close()로 닫아주는 것을 잊지 말자. template() 함수 내에 다음과 같이 작성했다.

  conn = sqlite3.connect('db.sqlite3')
  cursor = conn.cursor()
  cursor.execute('SELECT * FROM topics')
  topics = cursor.fetchall()
  conn.close()

tuple로 정보를 받아오게 되고, dictionary에 해당했던 문법 대신 tuple에 맞도록 출력 부분을 topic[0], topic[1] 이런 식으로 넣어준다.

  for topic in topics:
    liTags += f'<li><a href="/read/{topic[0]}/">{topic[1]}</a></li>'

read 함수에서는 해당하는 번호의 게시물 내용을 가져오도록 작성했다.

def read(num):
  conn = sqlite3.connect('db.sqlite3')
  cursor = conn.cursor()
  cursor.execute('SELECT * FROM topics WHERE id=?', (num,))
  topic = cursor.fetchone()
  conn.close()
  content = f'<h2>{topic[1]}</h2>{topic[2]}'
  return template(content, num)

(num,) 이라고 쉼표를 붙여준 것은 (num)으로 넘길 시 그냥 숫자로 넘어가고 (num,)으로 넘기면 튜플로 넘어가기 때문이다.

첫 화면

게시물을 클릭했을 때

Insert 함수 작성

기존 insert는 전역 변수 리스트인 topics에 항목을 추가하는 방식이었고, 이제 SQLite DB를 연결했으니 DB에 저장하는 방식으로 변경해보았다.

@app.route("/create_process/", methods=['POST'])
def create_process():
  title = request.form['title']
  body = request.form['body']
  conn = sqlite3.connect('db.sqlite3')
  cursor = conn.cursor()
  cursor.execute('INSERT INTO topics (title, body) VALUES (?,?)',(title, body))
  nextId = cursor.lastrowid
  conn.commit()
  conn.close()
  return redirect(f'/read/{nextId}')

처음에는 SELECT 함수로 가장 마지막 id값을 가져오는 것으로 코딩했는데, cursor.lastrowid라고 마지막 row의 id값을 가져오는 매우 훌륭한 내장 변수가 존재하고 있었다.


입력이 잘 된 것을 확인할 수 있다.

Delete 함수 작성

@app.route('/delete/<int:num>/')
def delete(num):
  conn = sqlite3.connect('db.sqlite3')
  cursor = conn.cursor()
  cursor.execute('DELETE FROM topics WHERE id=?', (num,))
  conn.commit()
  conn.close()
  return template(f'<h2>Delete Succeeded</h2>Content number {num} deleted.')

Delete 함수도 구현했다.

삭제 시연


HBase 글이 삭제된 것을 확인할 수 있다. 잘 작동된다.
(디자인이 바뀐 것은 부트스트랩을 사용해 버튼 속성을 변경해주었고 갑자기 CSS 복습을 해서...)

이렇게 2주 간의 웹 입문 교육이 마무리되었다.

Challenge

Modify 기능 구현해보기

수업이 끝나고 게시물 수정 기능도 구현해보았다. Create와 Delete를 적절히 섞으면 되니 어렵지는 않다.

@app.route('/modify/<int:num>/')
def modify(num):
  conn = sqlite3.connect('db.sqlite3')
  cursor = conn.cursor()
  cursor.execute('SELECT * FROM topics WHERE id=?', (num,))
  topic = cursor.fetchone()
  conn.close()
  content = f'''
  <h2>Modify</h2>let\'s modify!<br>
  <form action="/modify_process/{num}/" method="POST">
    <p><input type="text" name="title" value="{topic[1]}"></p>
    <p><textarea name="body">{topic[2]}</textarea></p>
    <p><input type="submit" value="수정"></p>
  </form>
  '''
  return template(content)

@app.route("/modify_process/<int:num>/", methods=['POST'])
def modify_process(num):
  title = request.form['title']
  body = request.form['body']
  conn = sqlite3.connect('db.sqlite3')
  cursor = conn.cursor()
  cursor.execute('UPDATE topics SET title = ?, body = ? WHERE id=?',(title, body, num))
  conn.commit()
  conn.close()
  return redirect(f'/read/{num}')

modify()와 modify_process()를 작성했다.
modify()는 수정하는 화면을 보여주는데 create()와 유사하나 이미 작성된 내용을 입력 창에 나타나게 만들었다.
modify_process()도 create_process()와 유사한데, 매개변수로 num일 넘겨받아 id 값에 해당하는 row를 UPDATE 하도록 구현했다.

DB2 게시물에서 수정 버튼을 눌러보면,

위와 같이 수정 화면이 나타난다.
내용을 변경한 후 수정 버튼을 눌러보면,



내용이 변경된 것을 확인할 수 있다.

? Question

tuple로 받아온 정보를 테이블 구조에 따라 dictionary처럼 구성할 수도 있을까?

데이터를 받아서 id, title, body에 접근할 때 topic[0], topic[1] 이렇게 인덱스 값으로 접근하는 것은 위험하다. dictionary로 변환하는 기능이 필요해 보인다.

! Answer

row_factory로 가능하다.

conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute('select * from topics')

result = cursor.fetchall()

하면 dictionary로 구성된 list로 반환이 된다고 한다.

Comment

오늘로 이고잉 강사님의 강의가 끝났다. 2주 동안 나름 정이 든 것 같은데 마지막이라니 아쉬운 마음이 들었다. 오랜만에 웹을 만져봤는데 변한 것도 많고 (javaScript의 let, const나, Flask 등...) 내가 참 오랫동안 손을 놓고 있었구나 깨닫고 겸손해지는 시간이었다. 어쨌든 내 손으로 그럴싸한 웹사이트 하나 만들어내서 뿌듯하다. 게시물 등록 삭제 수정이 되는 홈페이지라니! github에는 HTML, CSS, JavaScript 등 정적인 페이지만 호스팅 가능해서 Flask로 만든 동적인 웹은 어디에 올리면 좋을지 고민이다. Glitch는 월 8달러를 내야 24시간 서비스할 수 있다는데, 조금 괜찮은 걸 만들면 이용해보아도 좋을 것 같다.

Link

Glitch 코드
Glitch Live Site(24시간 호스팅 안 됨)

좋은 웹페이지 즐겨찾기