티스토리 뷰

저번 글과 이어서 진행하도록 하겠습니다. 저번 글에서는 Django와 MySQL을 연동하는 작업까지만 하고 마쳤습니다.

이번에는 쿼리문을 작성하여 MySQL에 저장되어있는 데이터를 가져와 view에서 사용하려 합니다. MySQL을 사용하더라

도 기존에 우리가 사용한 objects.all(), objects.get() 등의 queryset을 통하여 데이터를 가져올 수 있습니다. 하지만 SQL문

을 사용하는 것이 Django 외의 프로그래밍 영역에서 활용도가 훨씬 높기 때문에 SQL문을 사용하기로 합니다.

(+ SQL문 연습)


1. BookListView 변경

# 새로 import 하는 모듈
from django.db import connection


def BookListView(request):
    # books = Book.objects.all()
    try:
        cursor = connection.cursor()

        strSql = "SELECT code, name, author FROM bookstore_book"
        result = cursor.execute(strSql)
        books = cursor.fetchall()

        connection.commit()
        connection.close()

    except:
        connection.rollback()
        print("Failed selecting in BookListView")


    return render(request, 'book_list.html', {'books': books})
  • connection: 데이터베이스에 접속을 하기 위한 모듈
    • settings.py에 입력한 데이터베이스 정보를 가지고 접속
  • cursor(): cursor 객체 생성
    • cursor 란 SQL문을 수행하고 결과를 얻는데 사용하는 객체
  • cursor.execute(): 쿼리문을 연결된 DB로 보내 쿼리를 실행
  • cursor.fetchall(): 쿼리 실행 결과로 반환된 전체 데이터를 데이터베이스 서버로부터 가져옴
  • connection.commit(): 데이터에 대한 변경사항이 있다면 이를 확정, 갱신
  • connection.close(): 데이터베이스와의 연결을 닫음
  • connection.rollback(): 쿼리문 실행 도중 잘못된 경우 실행 전으로 되돌려 놓음

 

위의 코드를 작성 후 실행시키면 아래 화면과 같이 바로 에러가 납니다.

에러 내용을 보니 detail url 패턴에 code라는 인자가 필요한데 그 자리에 아무 것도 없다는 내용입니다.

 

디버깅 모드로 데이터가 어떻게 들어왔는지 살펴보니 데이터들이 하나의 객체가 아닌 튜플로 들어온 것을

확인할 수 있습니다. (디버깅 모드는 추후 글 작성 예정)

즉, 각 로우 별로 어떤 컬럼의 값인지 상세 정보가 들어있는 것이 아니라 단순히 로우 별로 쿼리문에서

선택한 컬럼 값들만 순서대로 가져와 나열한 것입니다. 따라서, 우리가 이 결과 값들을 가지고 각 값이

어떤 컬럼의 값인지 알기 쉽고, 템플릿에서 사용하기 쉽도록 데이터를 구성해야 합니다.

 

 

BookListView 코드를 다음과 같이 변경합니다.

def BookListView(request):
    # books = Book.objects.all()
    try:
        cursor = connection.cursor()

        strSql = "SELECT code, name, author FROM bookstore_book"
        result = cursor.execute(strSql)
        datas = cursor.fetchall()

        connection.commit()
        connection.close()

        books = []
        for data in datas:
            row = {'code': data[0],
                   'name': data[1],
                   'author': data[2]}

            books.append(row)

    except:
        connection.rollback()
        print("Failed selecting in BookListView")


    return render(request, 'book_list.html', {'books': books})

위의 코드로 작성한 후 실행해보면 북리스트 페이지에서 다음과 같이 책 제목이 나오지 않고 딕셔너리 자료형 하나가

통째로 나옵니다.

 

book_list.html 파일을 살펴보면 원인은 바로 제목 부분에 book이라는 변수 자체를 사용하고 있기 때문이라는 것을

알 수 있습니다.

이전에 queryset을 사용할 때는 books는 세 개의 객체가 담긴 리스트였고 book은 하나의 객체에 해당되었습니다.

이 객체를 직접 사용할 때는 우리가 models.py의 Book 모델에 정의한 것처럼 해당 객체의 name 컬럼이 반환되도록

메소드를 정의했기 때문에 책 제목이 나왔습니다. 하지만 SQL문을 사용할 때는 데이터베이스로부터 객체가 아닌 단순 컬럼 값만을 가져와 사용하기 쉽도록 데이터를 구성했습니다.

따라서 현재 템플릿에 넘어온 books는 세 개의 딕셔너리 데이터가 있고 book은 하나의 딕셔너리에 해당됩니다.

그렇기 때문에 book을 그대로 사용하면 딕셔너리가 보이는 것입니다.

 

딕셔너리 중 책 제목 데이터를 가지고 있는 키 값을 template에 지정하여 책 제목이 나오도록 한다.

<li><a href="{% url 'bookstore:detail' book.code %}"> 제목: {{book.name}}</a><br>저자: {{book.author}}</li>

 

다음 코드를 작성한 후 실행하면 이전과 같이 올바르게 나온 것을 확인할 수 있습니다.

 

2. BookDetailView 변경

BookListView와 같은 방식으로 BookDetailView의 코드를 변경합니다.

def BookDetailView(request, code):
    # book1 = get_object_or_404(Book, code=code)
    # book2 = Book.objects.get(code=code)

    try:
        cursor = connection.cursor()

        strSql = "SELECT code, name, author, price, url FROM bookstore_book WHERE code = (%s)"
        result = cursor.execute(strSql, (code,))
        datas = cursor.fetchall()

        connection.commit()
        connection.close()

        book = {'code': datas[0][0],
                'name': datas[0][1],
                'author': datas[0][2],
                'price': datas[0][3],
                'url': datas[0][4]}

    except:
        connection.rollback()
        print("Failed selecting in BookListView")


    return render(request, 'book_detail.html', {'book': book})
  • (%s): string 타입의 문자열 포매팅
    • 문자열 포매팅: 문자열 안에 어떤 값을 삽입하는 방법
      • 문자열 안의 특정한 값을 바꿔야 할 경우가 있을 때 이것을 가능하게 해줌
  • cursor.execute(strSql, (code,)): 쿼리문을 실행하는데 포매팅된 부분을 code로 치환
    • 튜플 안에 값이 하나만 있는 경우, 꼭 뒤에 콤마를 붙여야 함. 그렇지 않으면 튜플이 아닌
    • 일반 괄호로 인식
  • datas[0][0]: 이중 튜플로 결과값이 반환되기 때문에 이런 방식으로 인덱싱을 해야 함

 


 

참고 자료

 

댓글