왕초보를 위한 에러로그

파이썬 플라스크-sqlalchemy를 이용한 투두리스트 만들기(A-Z) (4)sqlalchemy로 db만들고 연결하기

보오 2019. 12. 29. 17:10

여기서는 조금 중요한 단계이다. db를 만들고 그 db안에 투두리스트를 쓴 다음에 get으로 불러오거나, post로 수정하거나 delete로 삭제할 것이기 때문이다.

 

1. sqlalchemy로 테이블 만들기

기존 코드에 이렇게 추가해준다.

from flask import Flask, render_template, url_for
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)

class Todo(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.String(200), nullable=False)
    date_created = db.Column(db.DateTime, default=datetime.utcnow)

    def __repr__(self):
        return '<Task %r>' % self.id

@app.route('/')
def index():
    return render_template('index.html')

if __name__ == "__main__":
    app.run(debug=True)

나도 잘은 모르겠지만... 하나하나 간단히 짚고 넘어가자면

flask_sqlalchemy패키지에서 SQLALchemy라는 클래스(?)를 불러옴. datetime이라는 함수도 같이 쓸것이다.

 

자세히는 모르겠지만 SQLAlchemy를 사용하려면 저렇게 컨피규레이션을 해줘야하는 듯 하다.

마지막에 ///test.db를 통해 test라는 이름의 db를 만들고 실행문을 적는 것 같다.

 

그리고 이건 Todo라는 클래스를 만드는 것 같은데..

그냥 테이블을 만든다고 생각하자~~

각 테이블의 속성을 살펴보면

id는 integer(정수)이자 primary_key(기본키)이다. 하나밖에 없고 추가될 때마다 1씩 늘어나는.. 각 테이블의 데이트를 식별하기 위해 꼭 필요하다.

content는 200자 이내의 string(글자)이며 nullable=False는 공백을 허용안한다는 뜻 같다. 즉 필수적으로 써야한다는 것

date_created는 앞서 임포트해온 datetime함수를 통해 생성되는 입력 시간을 뜻한다. 디폴트로 datetime.utcnow로 되어있는데 db생성 당시의 컴퓨터의 시간을 UTC(협정세계시)형식으로 표현한 것 같다. (자세한 내용은 https://docs.microsoft.com/ko-kr/dotnet/api/system.datetime.utcnow?view=netframework-4.8 참고 바람)

 

마지막 __repr__(self) 함수를 통해 테이블에 있는 db를 리턴해주는 듯..

이해가 안되도 일단 그냥 넘어가야한다ㅎ

 

2. 파이썬 실행 후 db만들기

여기까지 썼으면 app.py를 잠시 종료한다.

그리고 env 가상환경으로 다시 들어가서(source 명령어 사용) 파이썬만 실행해준다.

- python3

- from app import db

이렇게 나오면 잘 되고 있는 것이므로 당황하지 않고 계속 진행한다.

- db.create_all()

여기까지 하면!!

test.db라는 파일이 만들어져있을 것이다.

이제부터 저 db안에 리스트가 만들어질 것이다..!

 

이제부터 진짜로 술술 할 수 있다.

다시 exit()를 타이핑하여 파이썬에서 나가주고..

 

3. index.html에 테이블 만들기, if/for구문 만들기

{% block body %}
<div class="content">
    <h1 style="text-align: center">Task Master</h1>
    {% if tasks|length < 1 %}
    <h4 style="text-align: center">There are no tasks. Create one below!</h4>
    {% else %}
    <table>
        <tr>
            <th>Task</th>
            <th>Added</th>
            <th>Actions</th>
        </tr>
        {% for task in tasks %}
            <tr>
                <td>{{ task.content }}</td>
                <td>{{ task.date_created.date() }}</td>
                <td>
                    <a href="/delete/{{task.id}}">Delete</a>
                    <br>
                    <a href="/update/{{task.id}}">Update</a>
                </td>
            </tr>
        {% endfor %}
    </table>
    {% endif %}

    <div class="form">
        <form action="/" method="POST">
            <input type="text" name="content" id="content">
            <input type="submit" value="Add Task">
        </form>
    </div>
</div>
{% endblock %}

index.html로 돌아와서 이렇게 코드를 만들어준다.

전체적인 구조를 보면

if절 안에 for문이 있는 형식이다. tasks는 이름을 임의로 쓴 것인데 app.py에서 이름으로 지정해줄 것이다.

 

- 플라스크로 if구문 만들기

{% if tasks | length < 1 %}

tasks에 db가 하나도 없을 때 보여줄 코드

{% else %}

그렇지 않은 경우의 코드

{% endif %}

if절 종료

 

- 플라스크로 for문 만들기

{% for task in tasks %}

for문 안에서 실행될 내용

(진자2 구문으로 데이터 가져오기)

{{ task.content }} 각 db의 content부분을 가져온다

{{ task.date_created.date() }} db가 추가되는 날짜를 입력한다

<a href="/delete/{{task.id}}">Delete</a>

<a href="/update/{{task.id}}">Update</a> task.id를 파라미터로 넘겨 app.py에서 받은 후 해당 데이터를 delete하거나 update해준다

{% endfor %}

 

만약 vue를 써봤다면.. 뷰 v-for문법이랑 매우 흡사함을 알 수 있다.

이렇게 했으면 app.py에서 각 메소드를 받아 데이터를 리턴해주는 구문을 작성해주자.

 

4. app.py에서 api 작성

일단 리퀘스트 모듈을 추가하여 api호출을 받을 준비를 해야한다.

다음과 같이 request, redirect 모듈을 추가

 

(1) post, get으로 불러오기

@app.route('/', methods=['POST', 'GET'])
def index():
    if request.method == 'POST':
        task_content = request.form['content']
        new_task = Todo(content=task_content)

        try:
            db.session.add(new_task)
            db.session.commit()
            return redirect('/')
        except:
            return 'There was an issue adding your task'

    else:
        tasks = Todo.query.order_by(Todo.date_created).all()
        return render_template('index.html', tasks=tasks)

try에는 실행할 코드이고

except에 예외처리를 해준다

 

(2) delete로 호출했을 때

@app.route('/delete/<int:id>')
def delete(id):
    task_to_delete = Todo.query.get_or_404(id)

    try:
        db.session.delete(task_to_delete)
        db.session.commit()
        return redirect('/')
    except:
        return 'There was a problem deleting that task'

해당 아이디를 가진 db를 삭제해주는 함수

 

(3) update해줄 경우

@app.route('/update/<int:id>', methods=['GET', 'POST'])
def update(id):
    task = Todo.query.get_or_404(id)

    if request.method == 'POST':
        task.content = request.form['content']

        try:
            db.session.commit()
            return redirect('/')
        except:
            return 'There was an issue updating your task'

    else:
        return render_template('update.html', task=task)

 기존 db에서 일치하는 id값을 찾아 덮어씌워준다.

이때 update.html을 리턴하는데 코드는 다음과 같다.

 

5. update.html 작성

\todoApp\templates\update.html

{% extends 'base.html' %}

{% block head %}
<title>Task Master</title>
{% endblock %}

{% block body %}
<div class="content">
    <h1 style="text-align: center">Update Task</h1>

    <div class="form">
        <form action="/update/{{task.id}}" method="POST">
            <input type="text" name="content" id="content" value="{{task.content}}">
            <input type="submit" value="Update">
        </form>
    </div>
</div>
{% endblock %}

업데이트시에도 메소드는 포스트이다.

똑같이 base.html에서 상속받아 바디 부분만 고쳐준다.

 

6. 완성화면

이제 파이썬을 다시 실행하고 ctrl F5를 해주면

짠!! 첫번째 투두앱 완성!!!

추가를 해보자.

성공적으로 추가된 모습 !

또 추가해보자

수정도 해볼까

오... 성공적.. 삭제도 된다...

 

끝~~~

 

 

 

 

마지막엔 급하게 마무리했지만 자세한 코드는 원작자 깃허브에 나와있다.

https://github.com/jakerieger/FlaskIntroduction (여기에)

 

생각보다 쉬웠고 다음번에는 나 혼자 처음부터 끝까지 만들어봐야겠다.

그럼 다음 리뷰까지 ㅎㅇㅌ ㅎㅇㅌ