TIL_20230306_Django 프레임워크 제대로 배우기 03

창고·2023년 3월 6일
0

5. 테이블 생성하기

(1) 테이블의 개념, 생성

  • 테이블의 생성
    • python manage.py migrate 로 기본적인 테이블, 앱들이 설치됨
    • 이렇게 설치된 앱들은 setting.py 파일에서 확인
  • setting.py 내 DATABASES 설정
    • application.yml의 spring.datasource 느낌인듯
# Database
# https://docs.djangoproject.com/en/4.1/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

(2) 데이터 모델

  • 모델의 개념
    • 어떤 프로그램을 만들 때 데이터가 저장되고 관리되는 것이 필요한데 이런 것을 처리하기 위한 표본(모델)이 필요 -> 데이터 모델, 데이터베이스 모델
    • 어떤 필드(컬럼)들이 필요하고 필드 타입은 어떻게 할 것인지 등등...
    • 이를 통해 만들어진 데이터 모델 -> 테이블
  • 앱 모델 관련 설정 - models.py에서
from django.db import models

# Create your models here.
class Memo(models.Model):

    # PK는 따로 작성하지 않아도 된다 (Django에서 PK를 알아서 생성을 해주므로)
    description = models.CharField(max_length=200) # @Column(length = 200) 느낌
    created_at = models.DateTimeField(auto_now_add=True) # @CreatedDate 느낌

    # models.py 작성 후 어드민 사이트에 반영을 위해 admin.py를 열고 추가 작성이 필요
  • 모델 설정을 해당 앱 어드민에 연동 (admin.py)
from django.contrib import admin
from onememos.models import Memo # models.py의 Memo class import

# Register your models here.
admin.site.register(Memo) # admin.site에 등록
  • settings.py 수정
# Application definition

INSTALLED_APPS = [
    'onememos.apps.OnememosConfig', # 하위 app config 추가
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
  • migrations 파일 생성 py manage.py makemigrations
    • makemigrations 이후 앱 디렉토리 내 migrations에 새 파일 추가됨
    • models.py 설정을 바꾸고 migrations 할 때 마다 새 설정 파일이 계속 생성됨
# Generated by Django 4.1.7 on 2023-03-06 00:47

from django.db import migrations, models


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='Memo',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), # PK 관련 설정인듯
                ('description', models.CharField(max_length=200)),
                ('created_at', models.DateTimeField(auto_now_add=True)),
            ],
        ),
    ]
  • migrate 하여 테이블 생성 py manage.py migrate
    • 어드민 사이트 접속 시 생성된 테이블 확인 가능

  • 테이블 생성 흐름 정리
    • 최상위 환경 설정 폴더 -> settings.py 작성
    • py manage.py makemigrations : 앱 내 migrations 생성
    • migrate

6. URL Patterns 설정

  • URL Patterns 설정 : 최상단 디렉터리의 urls.py
    • 슬래시(/)는 기본적으로 장고가 알아서 처리하기는 하지만 특별한 경우가 아닐 경우 꼭 붙여준다
    • 마지막 단은 콤마를 생략해도 되고 붙여도 됨
    • 서버 구동 시 변화가 감지되면 자동으로 리로딩
    • 잘못된 코드로 오류가 날 경우 서버 구동이 자동적으로 감지하고 에러를 내며 수정되면 다시 재구동
    • 초기화의 경우 view.index 또는 views.main 등으로 해도 됨
  • 앱 내 urls.py, views.py 수정
from django.urls import path

from . import views

# 사용자 요청 url 패턴 및 응답 정의
# path(요청 url, 처리 함수)
# controller의 RequestMapping 느낌인듯
# request url이 최상위 urls.py -> app별 urls.py 로 mapping 되는 것 같음
# 사용자가 localhost:8080/onememos로 들어올 경우를 가정하면
# 해당 urls.py가 onememos 내부에 있으므로 추가 path를 적을 필요는 없음

urlpatterns = [
    #path('', views.index, name='index'), #views.py의 함수 매핑. DispatcherServlet -> Controller 확인 및 해당 컨트롤러의 핸들러 메서드로 매핑해주는 느낌
    path('', views.main),
    path('createMemo/', views.createMemo),
    path('detail', views.detail, name='detail') # 앞에 /를 안 붙여도 됨 (자동으로 붙음)
]
from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.

def main(request): # 기본적인 요청 흐름 url 요청 -> urls.py -> main()
    return HttpResponse("안녕 my 월드야")

def detail(request):
    return HttpResponse("안녕 my detail 월드야")

def createMemo(request):
    # request 객체는 사용자가 폼 페이지를 통해서 입력한 폼 데이터 값들을 받는다
    # request.GET, request.POST, request.COOKIE -> 딕셔너리형 데이터 GET, POST, COOKIE 정보들을 받음

    memoContent = request.GET['memoContent']

    return HttpResponse("create memo = " + memoContent)

7. 템플릿 생성하기

(1) render 함수 vs template 파일

  • render 함수와 template 파일의 관계
    • 웹사이트 개발 시 파이썬 코드와 데이터를 템플릿 파일로 만들어주는 함수
    • HTML로 변환해서 적용해주는 함수라고 보면 됨
    • 결국 views는 템플릿을 리턴하게 됨 (템플릿 파일이 HTML 파일은 아님. 프레임 워크 전용 파일의 개념)
    • 자바 스프링 진영의 템플릿은 JSP, Thymeleaf
    • 템플릿 파일에는 템플릿 관련 문법 또는 태그를 적용해야 하므로 순수 HTML 파일은 아님

(2) template 폴더 생성

  • 템플릿 폴더 생성 방법 2가지
    • 프로젝트 루트 폴더에 templates 폴더 만들어 사용 -> settings.py -> 템플릿 경로 추가
    • 생성한 앱 폴더 하위에 templates 폴더 만들어 사용 -> 앱별로 템플릿 사용 가능
      • 앱 폴더 하위의 templates 폴더 생성 시 별다른 설정 없이 템플릿 디렉토리를 인식하게 됨. 대신 서버 재시작 필요
  • render 함수 사용
def main(request): # 기본적인 요청 흐름 url 요청 -> urls.py -> main()
    #return HttpResponse("안녕 my 월드야")
    return render(request, 'main.html') # 템플릿 파일명 (앱 폴더 하위 templates에 있다면 경로 지정 하지 않아도 됨)

(3) 뷰 페이지 템플릿 생성

  • 관리자 모드에서 DB 조작
    • admin 페이지에서 직접 데이터를 생성할 수 있음
  • CMD 명령 프롬프트에서의 DB 조작 (dbshell)
    • sqlite tool 설치 필요 (sqlite-tools-win32-x86-3410000.zip)
    • sqlite3.exe 실행파일을 생성한 프로젝트 루트 폴더에 넣어놓고 sqlite DB에 접속
      • py manage.py dbshell
      • 관련 이슈 : 지난번 Git bash 관련 동일한 이슈가 발생하여 winpty 옵션 적용
    • 툴을 통해 DB의 테이블 정보 확인 및 테이블 규칙성 확인 -> 실제 테이블명 확인
      • sqlite에서 .table 입력 시 전체 테이블 조회 가능
      • 앱명_테이블명 으로 테이블이 생성됨
      • SQL 쿼리로 DB 조회 가능
  • Form 작성 및 DB 입출력
    • views.py 에서 HTTP 메서드로 전달 받은 객체를 다룰 수 있음
def createMemo(request):
    # request 객체는 사용자가 폼 페이지를 통해서 입력한 폼 데이터 값들을 받는다
    # request.GET, request.POST, request.COOKIE -> 딕셔너리형 데이터 GET, POST, COOKIE 정보들을 받음

    memoContent = request.POST['memoContent']

    return HttpResponse("create memo = " + memoContent)
<!--main.html-->

	...

        <form action="http://localhost:8000/onememos/createMemo" method="POST" id="memoWriteForm"> <!-- 절대 경로를 넣어줘야 한다-->
            <label for="memo">메모 입력</label>
            <input type="text" id="memo" name="memoContent" size="100" placeholder="내용을 입력하세요" autocomplete="false"> <!--name = views에서 받을 데이터 변수명 일치-->
        </form>
  • CSRF 토큰 처리
    • CSRF(Cross Site Request Forgery) : 특정 사용자가 마치 접속하여 요청값을 보낸 것처럼 글 작성, 즉 위조된 요청 액션을 보내 악의적으로 요청을 이용하는 행위
      • 예시 : iframe 등을 몰래 삽입하여 로그인 사용자가 본인도 모르게 글 작성을 요청 넣는 것처럼 하는 행위
    • 장고에서는 가장 편리하게 토큰을 이용하여 대비할 수 있음
    • CSRF 토큰은 발급 시 매번 값이 변경되며 뷰가 호출되기 전 해당 토큰을 통해 유효성을 검증
    • POST 방식을 사용하는 템플릿 form 태그 쪽에 {% csrf_token %} 태그 사용
    <body>

        <h1>메모 작성 폼</h1>

        <form action="http://localhost:8000/onememos/createMemo/" method="POST" id="memoWriteForm"> <!-- 절대 경로를 넣어줘야 한다-->
        {% csrf_token %}
            <label for="memo">메모 입력</label>
            <input type="text" id="memo" name="memoContent" size="100" placeholder="내용을 입력하세요" autocomplete="false"> <!--name = views에서 받을 데이터 변수명 일치-->
        </form>
        <div>
            <button type="submit" form="memoWriteForm">메모 등록</button>
        </div>
    </body>

8. 게시판 기능 구현

(1) 데이터베이스 Article 입력

  • views.py에서 바로 저장할 article 객체를 만들어서 저장
def createMemo(request):
    # request 객체는 사용자가 폼 페이지를 통해서 입력한 폼 데이터 값들을 받는다
    # request.GET, request.POST, request.COOKIE -> 딕셔너리형 데이터 GET, POST, COOKIE 정보들을 받음

    memoContent = request.POST['memoContent']

    # DB 입력
    article = Memo(description = memoContent)
    article.save() # 별도의 DAO 없이 바로 가능하구나....

    return HttpResponse("create memo = " + memoContent)
  • 만약 저장이 안된다면
    • models.py에서 필요한 모든 것을 import 했는지 체크
    • NOT NULL 필드 입력했는지 체크
    • 오타 체크

(2) 리스트 뷰 만들기

  • redirect와 reverse
    • django.urls 에서 reverse를 import
    • redirect() : 특정 URL로 리다이렉션
    • reverse() : URL을 역으로 계산해서 path가 변경되어도 URL을 외울 필요가 없음
      • urls.py에서 만든 URL Patterns들의 name을 사용해서 해당 name의 url을 반환
      • name 정보가 틀릴 경우 페이지 에러 발생
    • return 타입을 HttpResponseRediect로
def createMemo(request):
    # request 객체는 사용자가 폼 페이지를 통해서 입력한 폼 데이터 값들을 받는다
    # request.GET, request.POST, request.COOKIE -> 딕셔너리형 데이터 GET, POST, COOKIE 정보들을 받음

    memoContent = request.POST['memoContent']

    # DB 입력
    article = Memo(description = memoContent)
    article.save() # 별도의 DAO 없이 바로 가능하구나....

    # 리다이렉트를 위한 HttpResponseRedirect, reverse
    # redirect() - 특정 url로 리다이렉션
    # reverse() - URL을 역으로 계산하여 path가 변경되어도 url을 외울 필요가 없음, urls.py에서 만든 URL Patterns의 name을 이용, name을 통해 해당 name의 url을 반환
    # name 정보 틀릴 경우 페이지 에러 발생

    return HttpResponseRedirect(reverse('main'))
  • objects.all()
    • DB에서 전체 데이터를 조회하는 메서드 (findAll())
    • DB로부터 전체 데이터를 가져온 후 템플릿 파일로 전달(변수)할 때 all() 메서드 사용
def main(request): # 기본적인 요청 흐름 url 요청 -> urls.py -> main()

    lists = Memo.objects.all() # Memo 객체 전부 조회
    data = {'lists' : lists} # 반드시 딕셔너리 형태로 저장하여 템플릿 파일로 전달해야 함 (map.addAttribute() 느낌)

    return render(request, 'main.html', data) # 템플릿 파일명 (앱 폴더 하위 templates에 있다면 경로 지정 하지 않아도 됨)
  • 템플릿 태그 및 출력
    • {% %} : 템플릿 태그, 안쪽에 파이썬 문법을 사용
      • 반복 처리나 조건 처리 등을 위해 사용
      • 예시 : for article in lists
      • 직접적인 딕셔너리 이름(data)을 사용하지 않고 key를 사용 (lists)
      • 즉, key(=lists) 이름을 통해 value 값에 접근
    • {{ }} : 템플릿 태그, 데이터 값을 화면에 출력할 때 사용
      • 반복 처리 시 하나하나의 item(=article, object) 항목에 들어 있는 속성값을 꺼내서 화면에 출력시키고자 할 때 사용
        <!--리스트 뷰-->
        <div style="padding-left: 50px;">
            <ul style="line-height: 2em;">

                {% for article in lists %} <!--thymeleaf의 th:each 느낌-->

                <li>
                    <b>{{article.id}}</b>
                    {{article.description}}
                    <small style="color:deeppink">
                        ({{article.created_at | date:'y-m-d'}}) <!--datetime format을 여기서도 지정할 수 있음-->
                    </small>
                </li>

                {% endfor %} <!--thymeleaf에서는 딱히 없었다-->
            </ul>
        </div>
  • 결과
profile
공부했던 내용들을 모아둔 창고입니다.

0개의 댓글