Django를 사용해 API 서버 만들기 (TIL 11)

석형원·2024년 4월 8일

TIL

목록 보기
11/52

✏️ 오늘 학습한 내용

Django

  • 환경설정
  • 관계형 데이터베이스
  • Model 만들기
  • 다양한 필드 활용
  • 관리자 계정 생성
  • Django Admin - 모델 등록
  • Django Shell

🔎 Django

환경설정 - 윈도우

  1. python 설치

    https://www.python.org/downloads/

  2. python 가상 환경 설정

    • 프로젝트마다 가상환경을 설정해줘야 하는 이유

      서로 다른 유형과 목적의 프로젝트들을 한 공간에 넣어서 관리한다면, 매번 하나의 프로젝트를 실행할 때마다 그에 맞춰서 환경을 체크하고 변경해주어야 하며 심한 경우에는 프로젝트 간의 충돌이 발생할 수도 있기 때문

    • 가상 환경 생성
      $ py -m venv project-name

    • 가상 환경 활성화
      $ project-name\Scripts\activate.bat

    • 가상 환경 비활성화
      $ deactivate

    • Django 설치
      $ py -m pip install Django

    • 설치 확인
      $ django-admin --version

Django Project 생성

  • mysite라는 이름을 가진 프로젝트 생성
    $ django-admin startproject mysite

  • mysite로 이동
    $ cd mysite

  • 생성한 프로젝트를 서버에서 실행
    $ python manage.py runserver

  • 실행된 서버 확인

    http://127.0.0.1:8000/

Django App 생성

  • App의 특성

    • 하나의 프로젝트는 여러 개의 App으로 구성됨
    • App은 하나의 프로젝트에만 속할 수 있는 것은 아님 ( 다른 프로젝트에서도 동작 가능 )
  • 프로젝트 내에 App 생성
    $ python manage.py startapp app_name

  • Http Response 해보기

    • 요구 조건 :

      http://127.0.0.1:8000/polls를 입력 받았을 때, Hello, World를 출력하기

    • 코드 생성

      • polls/views.py
      # 출력문
      
      # Http response를 하기 위함
      from django.http import HttpResponse
      
      # index함수가 호출되면 hello world를 출력해라
      def index(request):
          return HttpResponse("Hello, world.")
      • polls/urls.py
      # http://127.0.0.1:8000/polls/path 입력이 들어왔을 때,
      # 여기서 path가 ''이면 views의 index로 연결
      
      from django.urls import path
      # 현재 App에서 view를 가져와라
      from . import views
      
      urlpatterns = [
          # url이 비어서 들어온다면
          # views의 index로 보내고 이름은 index로 해라
          path('', views.index, name='index'),
      ]
      
      • mysite/urls.py
      # http://127.0.0.1:8000/path 에서
      # path에 따른 연결을 진행
      
      from django.contrib import admin
      from django.urls import path, include
      
      urlpatterns = [
          # path에 polls란 url이 들어오면
          # polls 폴더 내 urls로 연결해라
          path('polls/', include('polls.urls')),
      
          # path에 admin이란 url이 들어오면 admin.site.urls으로 연결해라
          ## path('admin/', admin.site.urls),
          path('admin/', admin.site.urls),
      ]
      

관계형 데이터베이스

  • 정의

    데이터를 행과 열로 이루어진 테이블의 형태로 구성하고, 테이블 간의 관계를 정의하는 데이터베이스

  • 테이블

    데이터베이스에서 행과 열로 구성되어 있는 데이터의 집합

  • 열(column)

    • primary key

      테이블의 각 행을 고유하게 식별할 수 있는 열

    • foreign key

      다른 테이블의 primary key를 참조하는 열

  • 행(row)

    테이블에 저장된 데이터 레코드(Record)를 의미

    • 레코드(Record)

      • 하나의 단위로 표현되어지는 필드들의 집합

      • 고유한 값을 가지는 Primary Key로 식별됨

Model 만들기

  • Model이란

    • Django에서 DB를 읽어오는 기능을 담당
    • ORM
      • Django의 Model과 같은 기능
      • Model을 Table별로 읽어서 하나의 Table에 저장된 값을 코드에서 읽어들일 수 있도록 도와줌
  • 원하는 기능 :

    질문 : 여름에 놀러간다면 어디에 갈래?
    답 : 산, 강, 바다, 도심 호캉스
    -> 이 모델에 맞는 테이블을 만드는 것

  • 필요한 절차 :

    1. 모델 생성
    2. 모델을 테이블에 써 주기 위한 마이그레이션 작업
    3. 이 모델에 맞는 테이블 생성
  1. 모델 생성

    • polls.models.py
    # 모델을 만드려면 models.Model을 상속 받아야함
    class Question(models.Model):
        # 텍스트 형식의 질문을 하기 위해 Char형태로 만들어줌
        question_text = models.CharField(max_length=200)
    
        # 이 질문을 만든 날짜를 기록
        pub_date = models.DateTimeField('data published')
    
    # 질문에 대한 옵션을 만들기 위함
    class Choice(models.Model):
        # 이 question은 하나의 질문에 여러가지 옵션 중 하나, 
        # 어떤 질문에 대한 옵션인지를 가리켜야하므로
        # Question의 unique id를 저장 (Question의 PK를 참조하여 FK로 만듬)
        question = models.ForeignKey(Question, on_delete=models.CASCADE)
    
        choice_text = models.CharField(max_length=200)
    
        # 몇 개의 투표를 받았는 지 기록
        votes = models.IntegerField(default=0)
  2. 마이그레이션

    마이그레이션 : 모델의 변경 내역을 DB 스키마에 적용시키는 Django의 방법

    Migration을 만들기 전에 App을 등록해줘야함
    ( 장고의 기능은 설치된 App에 대해서 Migration을 만들기 때문 )

    • mysite.setting.py
    # mysite.setting.py의 INSTALLED_APPS에 polls를 등록
    # => 등록하는 법 : polls.apps.py에 있는 PollsConfig함수를 등록
    
    INSTALLED_APPS = [
      'polls.apps.PollsConfig',
      ...,
    ]
    • Migration 명령어
      python manage.py makemigrations polls

    • Migration 내용 확인
      python manage.py sqlmigrate polls 0001

      • Table이 만들어졌고, 정의하지 않았던 id가 생성되었음을 확인할 수 있음
        ( 식별할 수 있는 Primary Key가 꼭 필요하기 때문 )
      • Model Choice에 INDEX 테이블이 만들어진 이유?
        -> Question의 PK를 참조하는 foreign key를 만들었기 때문
  3. 테이블 생성

    • Migration 실행
      python manage.py migrate
      ( 변경된 내용을 데이터베이스에 저장 )

Django의 다양한 필드 활용

  • Django의 필드 확인하는 법

    https://docs.djangoproject.com/en/5.0/ref/models/fields/

  • Model 수정 및 Migration

    • polls.models.py
    class Question(models.Model):
      question_text = models.CharField(max_length=200)
      pub_date = models.DateTimeField('date published')
      # 새로 추가
      is_something = models.BooleanField(default=False)
      average_score = models.FloatField(default=0.0)
    • Migrate 후 DB 확인
      $ sqlite3 db.sqlite3

    • sqlite3 내에서 DB 살펴보기

      • table 목록 확인
        .tables

      • Migration 기록 확인
        SELECT * FROM django_migrations;

      • Polls Table 구조 확인
        .schema polls_question

    • Migration Rollback
      python manage.py migrate polls 0001
      ( 0002를 생성했었더라면 0002파일을 지워줘야 다음 Migration때, 반영이 되지 않는다. )

관리자 계정 생성

  • Admin(관리자) 페이지

    일반적으로, 시스템을 관리하는 관리자들이 데이터를 추가, 수정하는 페이지

  • 관리자 계정

    admin 페이지를 개발자가 일일이 만들기보다,
    Django에서는 만든 모델들을 기반으로 데이터를 만들고 읽고 수정할 수 있는 기능을 제공

    • CRUD 기능 제공
      ( create, read, update, delete )
  • 관리자 계정 생성
    python manage.py createsuperuser

  • Admin 접속 방법
    http://127.0.0.1:8000/admin

Django Admin - 모델 등록

  • polls.admin.py
from django.contrib import admin
# polls내 models에 있는 모든 함수를 불러옴
from .models import *

# Question, Choice 모델을 불러옴
admin.site.register(Question)
admin.site.register(Choice)
  • polls.models.py
class Question(models.Model):

    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('data published')

    # 자동 생성 되는 id 컬럼을 원하는 제목으로 정의
    # __str__(self) : 자기 자신의 문자열 정의를 표현
    def __str__(self):
        return f'제목: {self.question_text}, 날짜: {self.pub_date}'

Django Shell

  • Django Shell 실행
    python manage.py shell
    ( 장고 환경을 그대로 로딩, 정의한 모델들을 활용 가능 )

  • Question 모델 import
    from polls.models import *
    ( 직접 경로 지정 )

  • Question의 객체들 확인
    Question.objects.all()

  • 보다 세부적인 접근도 가능
    choice = Choice.objects.all()[0]
    choice.question
    choice.question.question_text

    qustion = Question.objects.all()[0]
    question.choice_set.all()

  • 현재 시간 구하기

    • datetime
      from datetime import datetime
      datetime.now()

    • timezone

      datetime에 비해 tzinfo 정보가 추가됨,
      주로 django에서는 timezone을 사용

      from django.utils import timezone
      timezone.now()

  • Record 생성 ( Data 생성 )
    e.g.)

    # Django shell
    
    from polls.models import *
    
    # q1 객체 생성
    q1 = Question(question_text = "커피 vs 녹차 ")
    # timezome import 
    from django.utils import timezone
    # q1에 pub_date 정보를 추가
    q1.pubdate = timezone.now()
    # table에 업로드 ( Record 생성 )
    q1.save()
    
    # q1의 choice 옵션을 추가
    q1.choice_set.create(choice_text='a')
    q1.choice_set.create(choice_text='b')
    
    # 이런식으로도 q1에 choice 옵션 추가 가능
    choice_c = Choice(choice_text='c',question=q1)
    choice_c.save()
    • polls.modes.py : pub_date 수정
      # pub_date가 자동 생성되게끔 수정
        pub_date = models.DateTimeField(auto_now_add=True)
  • Record 수정 및 삭제

    • 수정

      from polls.models import *
      # 마지막 Question 객체를 호출
      q = Question.objects.last()
      # q의 내용을 수정
      q.question_text = q.question_text + '???'
      # 저장하여 반영
      q.save()
    • 삭제

      # 마지막 객체 참조 'a','b','c' 중 'c'
      choice = Choice.objects.last()
      # choice의 question 저장
      q = choice.question
      # choice 삭제 'c' 삭제
      choice.delete()
      # 삭제 됬는지 확인
      q.choice_set.all()
      # id가 비어있는 것으로도 확인 가능
      choice.id
      
  • 모델 필터링 (Model Filtering)

    모델을 통해서 Table에서 다양한 조건에 맞는 Record를 불러오는 방법

    참조 : https://docs.djangoproject.com/en/5.0/ref/models/querysets/#id4

    ex1)

    from polls.models import *
    # id가 1인 question 객체를 불러옴
    Question.objects.get(id=1)
    
    # question_text가 '휴가를'으로 시작하는 객체를 불러옴
    # __? : 조건
    Question.objects.get(question_text__startswith='휴가를')
    
    # pub_date에 second가 59인 객체 불러옴
    Question.object.get(pub_date__second=59)
    
    # get : 하나의 객체만 불러올 수 있음
    # filter : 여러 개의 객체를 불러올 수 있음
    Question.objects.filter(pub_date__year=2024)
    
    # count도 가능
    Question.objects.all().count()
    
    # query를 출력하면 사용된 SQL문을 확인할 수 있음
    print(Question.objects.filter(pub_date__year=2024).query)
    

    ex2)

    # '휴가'가 들어간 질문을 추가
    q = Question(question_text="휴가를 가실 계획인가요?")
    q.save()
    
    # '휴가'가 들어간 모든 질문을 가져옴
    Question.objects.filter(question_text__contains='휴가')
    
    # gt : ~ 보다 큰
    # update : 수정
    choice = Choice.objects.first()
    choice.votes = 5 # choice의 votes를 5로 변경
    choice.save() # 이를 저장
    
    # votes가 0보다 큰 모든 객체를 0으로 수정 및 저장
    Choice.objects.filter(votes__gt=0).update(votes=0)
    
    # delete
    choice.votes = 5
    choice.save()
    Choice.objects.filter(votes__gt=0).delete()
    
    # regex : r'^시작 문자열.*0개 이상의 임의의 문자열'
    # '휴가' 단어로 시작하고 '어디' 단어가 포함된 질문을 가져옴
    q = Question(question_text="휴가 계획이 있나요?")
    q.save()
    Question.objects.filter(question_text__regex=r'^휴가.*어디')
    
    # 위와 동일한 표현
    Question.objects.filter(question_text__startswith='휴기').filter(question_text__contains='어디')
  • Django 모델 관계기반 필터링

    • choice 수정

    e.g.)

    from polls.models import *
    
    # 휴가라는 질문에 대한 Choice들을 선택
    # question의 외래키로 들어가 휴가로 시작하는 choice들을 가져옴
    Choice.objects.filter(question__question_text__startswith='휴가')
    
    # exclude : 이 조건을 포함하지 않는 모든 객체들을 가져옴
    Question.objects.exclude(question_text__startswith='휴가')
  • 모델 메소드
    ex) save()
    ( 모델이 Models.Model을 상속받기 때문에, 그 내부에 정의된 save() 메소드를 사용할 수 있음 )

    • polls.modes.py : 메소드 추가

      from django.db import models
      from django.utils import timezone
      import datetime
      
      class Question(models.Model):
          ...
      
          # 메소드 추가
          # 어제보다 최근에 만들어진 경우
          def was_published_recently(self):
              return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
          
          def __str__(self):
            if self.was_published_recently():
                new_badge = 'NEW!!!'
            else:
                new_badge = ''
            return f'{new_badge} 제목: {self.question_text}, 날짜: {self.pub_date}
      ...
      
    • shell에서 확인

      from polls.models import *
      q = Question.objects.first()
      # q가 오늘 만들어졌기에 True
      q.was_published_recently()
profile
데이터 엔지니어를 꿈꾸는 거북이, 한걸음 한걸음

0개의 댓글