[수업 3째주 9일차] Python-4

1. 학습내용

오늘은 데이터베이스를 터미널에 작성하고, 그걸 어제 작성했던 코드에 넣는 방법을 배웠다.
정확히는 리스트에 쓴 데이터를 가져와서 쓰는게 아닌, database(DB)에 넣은 자료를 가져와 쓰는 것을 배웠다.
일단 터미널을 열어서 'sqlite3'라고 입력해 프로그램을 불러온다.

그런다음 DB를 만들 파일명을 .open.파일명 이렇게 적어 파일을 생성한다.
아무표시도 안뜨면 파일이 생성된거다.
그리고 나서 데이터베이스는 표 형식으로 저장된 데이터들의 집합이므로, 위에서 적어준
topics에 해당되는 값들 처럼 표를 명령어로 입력해 적는다. 제목과 목록의 이름들만.

각각 id는 빈내용으로 넘길 수 없고, 다음행이 시작될때마다 자동으로 숫자가 늘어난다.
title도 텍스트만 적을 수 있고, 공란을 할 수 없다는것. body는 텍스트로만 적을수 있고
다른 조건은 쓰여지지 않았다.
그런다음 저렇게 쓰고 나서 제대로 생성이 되었는지 확인하려면

이렇게 적고 topics가 생성되면 제대로 만들어 진것을 알수 있다.
테이블의 구조를 보고싶다면 .schema topic이라고 치면 아까 쳤던 내용을 불러와서 확인 할 수 있다.

이제 테이블의 뼈대를 만들었으니까 안에 내용을 채워넣어보자. 내용은 아래와 같이 터미널에 입력하면 된다.

저렇게 내용이 생성되고 선택한 행의 내용을 보고 싶다면
SELCET * FROM topics; 라고 치고 enter를 누르면 아래와 같이 하나의 행의 내용을 보여준다.

만약 어떤 행을 지우고 싶다면, 행에 해당되는 아이디를 적고 아래와 같은 명령문을 쓰면 된다.

데이터를 다 썼다. 하면 .exit라고 하면 나갈수 있다.

여지껏 DB 파일의 목록을 보고 싶다면 ls -l이라고 치면 죽 목록이 뜬다.

그럼 저렇게 생성된 DB를 이제 파이썬으로 읽고, 쓰는 것을 만들어보자.
일단. read 부분이다. 일단 파이썬에는 기본적으로 SQlite가 내장되어 있기 때문에 import문으로 손쉽게 쓸수 있다.

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()

일단 위의 코드를 적기 위해 새로운 파일을 '파일명.py'라고 생성한다.
그런다음 아까 만든 DB파일을 연결하기 위해 'sqlite3.connect('db.sqlite3')'으로 적어주고
임의의 이름을 부여해 함수값으로 만든다.

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

이런식으로.
그런다음 자료를 꼭 집어 표시한다는 '커서를 댄다'는 의미로

cursor = conn.cursor() 

이런식으로 적는다.
그런다음 가리킨 자료를 가져온다는 뜻에서 fetch를 적는다. 뒤에 all이 붙는건, 데이터 전체를 가지고 온다는 말이다.

topics = cursor.fetchall()

그런다음 짠 코드가 맞는지 DB에 쓴 게 출력이 되게 하려면
print(topics)를 써서 저장한 다음에 터미널로 확인해 본다.

그럼 저렇게 아까 터미널에 입력해 놓았던 1행에 관한 자료가 뜬다.
그런데 저 []괄호 안에 ()까지 나오고 자료가 출력된다.
()까지 출력되어 자료가 표시되는데 자료만 출력되게 하려면 이렇게 하면 된다.

for topic in topics:
  print(topic[0], topic[1]) #자료의 순번은 0부터 시작하므로 아이디와 제목을 출력하기 위해 0과 1을 적음.


이렇게 뜬다.

끝에 DB를 계속 호출하면 시스템 과부화가 될수도 있기 때문에 close라고 끝맺어준다.

conn.close()

이번엔 파이썬으로 만들었던 DB에 데이터를 추가해 보기로 한다.

import sqlite3
conn = sqlite3.connect('db.sqlite3')
curor = conn.cursor()
title = input('title? ')
body = input('body? ')
curor.execute('INSERT INTO topics (title, body) VALUES(?, ?)', (title, body))
conn.commit()
conn.close()

import read #아까 만든 read파일을 불러오는 명령문.

마찬가지로 커서부분까지는 동일하다. 이제는 그 커서를 갖다 댄 부분에 우리는 자료를 새로 추가해야 한다. 그래서 title과 body를 입력받아야 하므로 input으로 입력창을 만들게 한다.

title = input('title? ')
body = input('body? ')

그런다음에 우리는 입력받은 제목과 본문 값을 DB에 넣어야 한다(topics)그럴려면 INSERT문을 써야 한다. EXECUTE에

topic의 표안에 값이 들어갈건데, 그 값은.
title과 body값에 들어갈거고 vlues값은 빈칸 중 첫번째, 두번째 값에 입력값이 들어갈거란 말이다.

curor.execute('INSERT INTO topics (title, body) VALUES(?, ?)', (title, body))

마지막으로 읽기에서 넣을 필요 없었던 구문을 하나 추가해야 하는데 바로 connect에 commit을 한다고 추가해야 한다. 이는 쓰기, 삭제 ,추가 에 해당한다.

conn.commit()

그럼 이제 터미널로 아래와 같이 추가로 써 넣을 수 있다.

데이터가 잘 들어왔는 지는 read로 확인 할 수 있다.

read입력어 없이 바로 추가된 자료가 추가되었는지 확인하려면,import read라고 마지막에 추가로 만든 파이썬 파일 제일 밑에 넣으면 된다.

이제는 추가까지 했으니 어제 입력한 파일로 돌아와서 아래의 list를 db로 바꾸는 작업을 해본다.

topics = [
  {"id":1,"title":"html","body":"html is ...."},
  {"id":2,"title":"css","body":"css is ...."},
  {"id":3,"title":"js","body":"js is ...."}
]

일단 상단에 import로 sqlite3라고 입력한다.
그런다음 본문 내용을 DB의 내용으로 수정하기 위해서, 본문이 들어가는 부분의 코드를 바꿔준다.

def template(content):
  liTags = ''
  for topic in topics:
    liTags = liTags + f'<li><a href="/read/{topic["id"]}/">{topic["title"]}</a></li>'

에서 liTags를 지우고 DB를 넣을거다.

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

그 다음 커서를 만들어 주고,

cs = conn.cursor()

자료를 선택해 가져온다는 구문을 쓴다.

cs.execute('SELECT * FROM topics')

그런다음 아까 읽기 파일처럼 커서에 댄 부분의 자료 전체를 가져온다는 뜻에서(그리고 sqlite에서 가져온 자료를 파이썬으로 옮겨올때 컨버팅 해준다는 의미이기도 함)

 topics = cs.fetchall()

를 써주고, liTags에서

    liTags = liTags + f'<li><a href="/read/{topic["id"]}/">{topic["title"]}</a></li>'

에서 topic['id']부분을 DB의 아이디 순번 0을 입력하고 제목인 순서 1을 입력한다.

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

그럼 이렇게 뜬다.

아까 터미널에서 추가했던 목록들로 바뀐것이다.

이제 저걸 누르면 제목에 관련된 내용으로 바뀌게 또 코드를 변경해야 한다.
위의 topics 내용이 아닌 DB의 내용이 뜨게 해야 되기 때문이다.
그럴려면 저걸 누를 때 바뀌는 라우터로 가서 명령문을 바꿔야한다.

@app.route("/read/<int:id>/")
def read(id):
  conn = sqlite3.connect('db.sqlite3')
  cs = conn.cursor()
  cs.execute('SELECT * FROM topics WHERE id=?', (id,))
  topic = cs.fetchone()
  conn.close()
  title = topic[1]
  body = topic[2]
  return template(f'<h2>{title}</h2>{body}', id)

마찬가지로 커서 부분 까지는 동일하게 쓰면 되고, execute부분에 해당되는 어떤 아이디의 내용을 갖고 올거냐 는 뜻에서 'WHERE'이라고 쓰고 id를 쓰면 된다.
id에 무슨 값이 오는지 모르기 때문에 ?을 쓰고, 그 값은 클릭하면 누르는 DB의 아이디의 값이란 뜻에서 (id,)라고 쓴다. 저 콤마 값을 안쓰면 마치 (1)만 입력했을때 1만 출력되듯 (1)값 전체가 출력되게 할려면 ,를 붙여야 한다. 변하는 값이 아니라는 뜻에서. 이걸 튜플이라고한다.

튜플(tuple)은 몇 가지 점을 제외하곤 리스트와 거의 비슷하며 리스트와 다른 점은 다음과 같다.

리스트는 [ ]으로 둘러싸지만 튜플은 ( )으로 둘러싼다.
리스트는 그 값의 생성, 삭제, 수정이 가능하지만 튜플은 그 값을 바꿀 수 없다.

어쨌든 저렇게 해당되는 아이디에 관한 행을 가져오는데, 우리는 하나의 행만 가져와야 한다.
(여럿 가져올 수 없음.)
이때는 cs.fetchone()이라고 one을 쓴다.

 topic = cs.fetchone()

다음엔 conn을 닫아주면 된다.
그리고, 이전에 적은 title과 body값을 DB에서 topics값을 어디서 가져오는 지 고쳐 적으면 된다.

  title = topic[1]
  body = topic[2]

그런다음 앞서 for문을 break값까지 지워준다.

'delete'버튼도 수정해야 한다. DB에서 자료를 가지고 오는 것으로 변경했으니, 거기에 있는 자료를 삭제하는 걸로 바꿔야 한다. 해당 되는 항목을 눌렀을때 삭제 버튼을 누르면 삭제가 되는 건
Delete 구문을 마찬가지로 execute괄호 안에 넣으면 된다. 해당 아이디가 들어갈때 해당되는 데이터를 삭제한다는 식으로.

@app.route('/delete/<int:id>/', methods = ['POST'])
def delete(id):
  conn = sqlite3.connect('db.sqlite3')
  cs = conn.cursor()
  cs.execute('DELETE FROM topics WHERE id=?', (id,))
  conn.commit()  
  conn.close()
  return redirect('/')

그리고 DELETE 문에 어디 아이디에서 라는 의미로 'WHERE'를 적어주면 된다.

2. 어려웠던 점 및 해결 방안

그럼 이제 어제 만든 폼에 내용을 적으면 추가되는 걸 DB에 추가되는 것으로 고치려 한다.
이걸 강사님께서는 아까 만든 DB데이터에 추가할 수 있는 걸로 고쳐보라고 하셨다.
그래서 이렇게 까지 밖에 못했다. 수업을 따라가느라 약간 이해를 덜했기 때문이였다.

@app.route('/create_process/', methods=['POST'])
def create_process():
  conn = sqlite3.connect('db.sqlite3')
  cs = conn.cursor()

근데 블로그 작성 중에 다시 보니 또 여기까진 이해를 했다.
추가할 값을 execute에 넣는거 까지 말이다.

cs.execute('INSERT INTO topics(title, body) VALUES(?,?)', ())

근데 다음부터는 잘 못하겠어서 강의를 다시 봤다.
그러니까 저 ?두개 부분에 각각 title과 body부분이 들어가야 하는데, 강사님께서는
메소드가 POST방식이면 자료를 받았을때 브라우저는 자료를 어떻게 요청 받을수 있냐고 하시면서
request라고 쓰면 된다고 하셨다.

title = request.form['title']

이렇게 하면 title 값을 가져올 수 있다고. body도 그렇게 쓰고, Values 뒤에 ()에 아까와 똑같이 title과 body를 지정해 주었으니 그대로 (title,body)이렇게 쓰면된다.

cs.execute('INSERT INTO topics(title, body) VALUES(?,?)', (title,body))

그런다음 commit을 적으면 된다. 이걸 적지 않으면 쓸수 있는 상태로 되지 쓰지는 못한다고.

@app.route('/create_process/', methods=['POST'])
def create_process():
  title = request.form['title']
  body = request.form['body']
  conn = sqlite3.connect('db.sqlite3')
  cs = conn.cursor()
  cs.execute('INSERT INTO topics(title, body) VALUES(?,?)', (title,body))
  conn.commit()  
  conn.close()

그런데 이렇게까지 쓰면 지금 추가하는 행의 아이디 값이 뭔지 알아 낼 수가 없다. 그렇기 때문에
selcet문으로 마지막행을 가져 올 수도 있지만 누가 또 추가로 적는다면 그게 마지막행이 될지 아님 그 전의 행이 될지 모르기 때문에, 아래와 같이 적는다고 하셨다.

  id = cs.lastrowid

커서에게 라스트 로우(그러니까 마지막줄의)아이디를 물어봐서 받아온다.
마지막에 nextID를 지우고, 위에도 지우고 저장하면 된다.

return redirect(f'/read/{id}/')

3. 학습소감

파이썬이 어렵기도 했지만 이번에는 신기하면서도 조금씩은 이해가 가는 느낌이였다.
여전히 데이타베이스의 개념이 내가 여지껏 배워온 도서관에서 쓰이는 거나 그냥 가볍게 알던 것과는 달라서 생소했지만 확실하게 컴퓨터 프로그램상에서 뭔지 알수 있었다.(조금..?)
오늘은 여지껏 가르쳐오셨던 강사님과의 마지막수업이라 아쉽고 아직 배워야 할 부분이 웹프로그램 관련해서 많다고 느꼈지만 그만큼 다음주의 수업도 기대가 되는 하루였다.

좋은 웹페이지 즐겨찾기