본문 바로가기
Web 개발/Django

Django

by yororing 2024. 3. 18.

00 Django란

  • 웹 프레임워크 중 하나로, 웹 애플리케이션을 효율적으로 빠르게 개발하는 데 사용할 수 있는 소프트웨어
  • 일반적인 웹 개발 업무에 즉시 사용할 수 있는 구조를 제공하여 몇 줄만으로 코드를 작성 가능
  • Python 언어로 작성됨

01 제공하는 구조

  • 사용자 인증, 콘텐츠 관리, 사이트 맵, RSS 피드

1. 사용자 인증

  • 현대의 웹 사이트는 사용자를 인증하고 권한을 부여해야 함
  • 인증은 사용자의 ID를 확인하고 권한 부여는 인증된 사용자가 사이트에서 수행할 수 있는 작업을 결정함
  • Django는 다양한 용도로 인증을 관리 가능:
    • 사용자 계정
    • 사용자가 특정 웹 사이트 태스크를 수행할 수 있도록 허용하는 권한 및 ‘예 또는 아니요’ 플래그
    • 유사한 권한을 가진 여러 사용자 계정의 그룹
    • 쿠키 기반 사용자 세션
    • 구성 가능한 암호 해싱 시스템과 양식 및 보기의 콘텐츠를 제한하는 도구를 제공

02 DBMS와 연동

  • 연동되는 DBMS: PostgreSQL, MariaDB, MySQL, Oracle, SQLite

0. 참조

  1. https://docs.djangoproject.com/en/4.2/ref/databases/
  2. https://www.qu3vipon.com/django-orm

1. ORM이란

  • Object-Relational Mapping의 약자

1) 정의 및 기능

  • 객체(Object)와 관계형 데이터베이스(Relational DB)를 연결해줌
  • ORM 이용시 SQL을 적지 않고도 프로그래밍 언어로 DML을 수행 가능
  • 객체지향적인 코드를 통해 데이터를 다루기 때문에 코드 가독성이 높고, 이는 생산성을 높여줌
  • lazy-loading이나 caching 같은 기능을 통해 불필요한 쿼리를 줄여줌
  • Django는 ORM을 지원

2) Django ORM 개요

  • 하나의 Model Class하나의 DB Table에 해당
  • 모델 클래스를 통해 생성한 하나의 Instance하나의 Table Record에 해당
  • SQL은 기본적으로 Table 단위로 쿼리하기에 Django에서 역시 Model Class 단위로 쿼리

3) 용어

  • QuerySet (쿼리셋)
    • Python에서 실행한 SQL 쿼리에 대한 결과/반환값
    • iterable한 객체

 4) Django의 ORM 문법

  • 클래스부터 시작 (SQL의 FROM절을 적는 것과 같음)
    • >>> User
  • DB에서 데이터 읽어오기
    • DB에서 데이터를 읽어오기 위해서 Manager를 통해 QuerySet을 만들어야 함
    • User라는 모델 클래스의 objects라는 attribute가 Django ORM Manager에 해당
      • >>> User.objects
      • >>> manager = User.objects
        
        >>> manager
        # 결과값
        <django.db.models.manager.Manager object at 0x7fe8d3b90668>
        
        >>> type(queryset)
        # 결과값
        <class 'django.db.models.manager.Manager'>
      • Django의 모든 모델 클래스는 기본적으로 objects라는 이름의 Manager를 갖고 있음
      • Django의 ORM Manager는 모델과 DB 사이의 인터페이스
      • Manager가 있기 때문에 추가적인 기능 (예: 특정 조건의 쿼리 재사용 등) 활용 가능
    • Manager에 쿼리하는 데이터에 대한 조건 추가시 (예: 클래스명.objects.조건()) QuerySet 완성
  • 조건 (함수) 종류
    • all() 메소드: Table의 모든 레코드 반환 
      • >>> User.objects.all()
        
        >>> queryset = User.objects.all()
        >>> type(queryset)
        # 결과값
        <class 'django.db.models.query.QuerySet'>
      •  위의 QuerySet (User.objects.all()) 은 아래의 SQL을 만들어냄
      • SELECT * FROM User
    • filter() 메소드: SQL의 WHERE 절
      • >>> User.objects.filter(is_active=True)
      • SELECT * FROM User WHERE is_active = 1
    • count() 메소드: 레코드 세는 기능
      • >>> User.objects.count()
        3
      • SELECT COUNT(*) FROM User
    • aggregate(집계함수('컬럼명')) 메소드: 집계함수 - SUM, COUNT, AVG, MIN, MAX 등
      • 여러 레코드로부터 하나의 결과값 반환
      • Null 값 주의
        • 특정 컬럼을 기준으로 집계시 해당 컬럼이 NULL 값을 갖는 레코드는 집계에서 제외
        • Count 함수 경우, 특정 컬럼 대신 *를 인자로 전달시 모든 레코드의 개수를 셈
      • Count 함수
        • >>> from django.db.models import Count
          >>> User.objects.aggregate(Count('id'))
          {"id__count": 3}
        • __count는 Django에 의해 기본으로 추가되는 이름
        • SELECT COUNT(id) AS id__count FROM User​
        • alias 추가하여 집계 결과의 이름 지정 가
        • >>> User.objects.aggregate(count=Count('id'))
          {"count": 3}
        • SELECT COUNT(id) AS count FROM User
      • Avg 함수
        • >>> from django.db.models import Avg
          >>> Goods.object.aggregate(Avg('price'))
          {'prive__avg': 15000}
        • SELECT AVG(price) FROM Goods

 

  • SQL & Execution Plan
    • 반환값을 변수에 할당
      • >>> queryset = User.objects.all()
         
    • QuerySet이 생성한 SQL쿼리 확인
      • >>> print(queryset.query)
        # 결과값
        SELECT * FROM User
        
        >>> str(queryset.query)
        # 결과값
        'SELECT * FROM User'
    • sqlparse 활용하여 SQL 쿼리를 보기 좋게 출력
      • >>> import sqlparse
        >>> query = str(queryset.query)
        >>> print(sqlparse.format(query, reindent=True))
        
        # 결과값 예)
        SELECT "DCVBridge_server"."hostname",
               "DCVBridge_server"."status",
               "DCVBridge_server"."core",
               "DCVBridge_server"."session_count",
               "DCVBridge_server"."update_date"
        FROM "DCVBridge_server"
    • SQL 실행 계획 확인
      • >>> print(queryset.explain())
        # 결과값 예시
        1 SIMPLE User None ALL None None None None 1 100.0 Using filesort
        # 결과값 예시
        Seq Scan on "DCVBridge_server"  (cost=0.00..1.02 rows=2 width=27)
      • 실행 계획은 쿼리의 성능 분석을 위해 반드시 확인해야 함 

1. PostgreSQL

 

03 Django 사용하기

0. 참조

  1. https://076923.github.io/posts/Python-Django-9/#django-migration
  2. https://hub1234.tistory.com/26#google_vignette

 

1. Migrations

  • Migration: DB의 Schema를 관리하기 위한 방법
    • Schema: DB에서 자료의 구조, 자료 간의 관계 등을 기술한 것
  • DB에서는 스키마를 비롯해 테이블, 필드 등의 변경이 발생했을 때 지정된 DB에 적용하는 과정을 의미
  • model.py에는 모델이 정의만 되어있을 뿐, 이 자체로는 데이터베이스를 생성하고 적용하지 않음
  • Migration을 통해 DB를 생성하고 모델의 생성, 변경, 삭제 등에 따라 작업 내역 관리 및 DB 업데이트 가능

1) 기본 Migration

 

실행:

python3.6 manage.py migrate

 

결과:

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying sessions.0001_initial... OK
  • python3.6 manage.py migrate으로 현재 프로젝트의 마이그레이션을 진행
  • Migration 진행시, Django 프로젝트에서 사용하는 11개의 기본 테이블이 생성됨
  • sqlite3 데이터베이스의 경우 Migration 진행시 생성되는 테이블은 다음과 같음:
    • auth_groupauth_group_permissionsauth_permissionauth_userauth_user_groupsauth_user_user_permissionsdjango_admin_logdjango_content_typedjango_migrationsdjango_sessionsqlite_sequence

2) Migration 상태 확인

 

실행:

python3.6 manage.py showmigrations

 

결과:

admin
 [X] 0002_logentry_remove_auto_add
 [X] 0003_logentry_add_action_flag_choices
auth
 [X] 0001_initial
 [X] 0002_alter_permission_name_max_length
 [X] 0003_alter_user_email_max_length
 [X] 0004_alter_user_username_opts
 [X] 0005_alter_user_last_login_null
 [X] 0006_require_contenttypes_0002
 [X] 0007_alter_validators_add_error_messages
 [X] 0008_alter_user_username_max_length
 [X] 0009_alter_user_last_name_max_length
 [X] 0010_alter_group_name_max_length
 [X] 0011_update_proxy_permissions
contenttypes
 [X] 0001_initial
 [X] 0002_remove_content_type_name
first_app
 (no migrations)
sessions
 [X] 0001_initial
  • python3.6 manage.py showmigrations 명령어로 현재 Migration 상태 확인
  • 기본적인 Migration으로 11개의 테이블이 생성되었지만, 모델(model.py)에서 생성한 테이블을 생성되지 않은 것을 확인
  • 이제, 애플리케이션(앱)에서 생성한 모델에 대해 Migration을 적용할 것

3) 애플리케이션 Migration

 

실행: 

python3.6 manage.py makemigrations first_app

 

결과:

Migrations for 'first_app':
  first_app\migrations\0001_initial.py
    - Create model UserModel
  • python3.6 manage.py makemigrations [앱 이름]으로 모델에서 생성한 사항이나, 변경 사항된 사항을 감지하여 파일로 생성
  • 단순하게 마이그레이션을 진행할 구조를 생성하는 것이므로, 적용은 되지는 않음
  • 다시 python3.6 manage.py showmigrations 명령어를 통해 마이그레이션 상태를 확인할 경우, 다음과 같이 표시됨 (주의: 데이터베이스 종류에 따라 다른 SQL이 생성됨)
admin
 [X] 0001_initial
 [X] 0002_logentry_remove_auto_add
 [X] 0003_logentry_add_action_flag_choices
auth
 [X] 0001_initial
 [X] 0002_alter_permission_name_max_length
 [X] 0003_alter_user_email_max_length
 [X] 0004_alter_user_username_opts
 [X] 0005_alter_user_last_login_null
 [X] 0006_require_contenttypes_0002
 [X] 0007_alter_validators_add_error_messages
 [X] 0008_alter_user_username_max_length
 [X] 0009_alter_user_last_name_max_length
 [X] 0010_alter_group_name_max_length
 [X] 0011_update_proxy_permissions
contenttypes
 [X] 0001_initial
 [X] 0002_remove_content_type_name
first_app
 [ ] 0001_initial
sessions
 [X] 0001_initial
  • first_app에서 (no migrations)으로 표시되던 항목이 [ ] 0001_initial로 표시되는 것 확인 가능
  • 이 변경사항에 대해 마이그레이션 진행
python3.6 manage.py migrate first_app

 

결과:

Operations to perform:
  Apply all migrations: first_app
Running migrations:
  Applying first_app.0001_initial... OK
  • 결과에서 확인할 수 있듯이, first_app 앱에 대한 마이그레이션이 적용됨
  • 다시 python3.6 manage.py showmigrations 명령어를 통해 마이그레이션 상태를 확인할 경우, 다음과 같이 표시
admin
 [X] 0001_initial
 [X] 0002_logentry_remove_auto_add
 [X] 0003_logentry_add_action_flag_choices
auth
 [X] 0001_initial
 [X] 0002_alter_permission_name_max_length
 [X] 0003_alter_user_email_max_length
 [X] 0004_alter_user_username_opts
 [X] 0005_alter_user_last_login_null
 [X] 0006_require_contenttypes_0002
 [X] 0007_alter_validators_add_error_messages
 [X] 0008_alter_user_username_max_length
 [X] 0009_alter_user_last_name_max_length
 [X] 0010_alter_group_name_max_length
 [X] 0011_update_proxy_permissions
contenttypes
 [X] 0001_initial
 [X] 0002_remove_content_type_name
first_app
 [X] 0001_initial
sessions
 [X] 0001_initial
  • first_app의 0001_initial이 적용된 것을 확인 가능
  • python3.6 manage.py migrate first_app 명령어는 현재 적용되지 않은 Migration을 적용
  • Migration이 정상적으로 적용될 경우, [앱_클래스명]의 형태로 테이블이 생성됨
  • 예제를 기준으로 테이블의 이름을 확인한다면, first_app_usermodel 테이블이 생성됨
  • 정상적으로 Migration이 완료되었다면, 프로젝트 실행 가능

4) 주의사항

  • Migration 적용 후 모델(model.py) 수정시 다음과 같이 Migration 다시 적용 필수 (특정 앱에 대해 Migration 파일을 생성 후, 모든 변경사항을 적용하는 것)
    • python3.6 manage.py makemigrations [앱 이름]
      python3.6 manage.py migrate [앱 이름]
  • Model Migration 진행시, 경고 문구가 발생한다면 필수 필드가 생성되었지만 기본값이 할당되어 있지 않아서 발생하는 문제
    • 임의의 값을 모두 채워주거나 취소하여 건너 뛰기 가능
    • 단, 임의의 값으로 채울 때 올바르지 않은 값을 채운다면 치명적인 오류 발생 가능
  • Migration이 정상적으로 적용되었을 경우 다음과 같은 파일 구조를 갖음
    • [현재 프로젝트]/
        > 📁 [장고 프로젝트 이름]
        ⬇ 📁 [장고 앱 이름]
          > 📁 __pycache__
          ⬇ 📁 migrations
            > 📁 __pycache__
            🖹 __init__.py
            🖹 0001_initial.py
          🖹 __init__.py
          🖹 admin.py
          🖹 apps.py
          🖹 models.py
          🖹 serializers.py
          🖹 tests.py
          🖹 urls.py
          🖹 view.py
        🖹 db.sqlite3
        🖹 manage.py
      
  • Migration은 한 명만 진행하는 것이 좋음 (여러 명이 작업하는 경우 DB가 꼬일 수 있음)
  • Migration은 DB Schema에 변화를 발생시키지 않더라도 수행하는 것 권장
  • Migration은 모델의 변경 내역을 누적하는 역할을 하며, 적용된 Migration 파일은 제거하면 안됨
  • Migration을 취소하거나 돌아가야하는 경우 다음과 같이 실행
    • python3.6manage.py migrate [앱 이름] 0001_initial
    • 위의 명령어를 실행할 경우 0001_initial의 상태로 되돌아감
    • 현재 Migration이 적용된 상태가 
      • 0001_initial 이전일 경우: 정방향(forward)으로 Migration이 진행됨
      • 0001_initial 이후일 경우:  순차적으로 지정된 Migration까지 역방향(backward)으로 Migration이 진행됨
  • Migration을 초기화 해야하는 경우 다음과 같이 실행
    • python3.6 manage.py migrate [앱 이름] zero
    • 현재 앱에 적용된 모든 Migration을 삭제함
    • no such column 오류 발생시 Migration이 진행되지 않았다는 의미
    • Migration은 디펜던시(dependencies) 순서에 의해 진행됨

5) DB 완전 초기화

  • DB 삭제 및 완전 처음의 상태로 돌아가기 위해서는 다음과 같은 파일을 제거시 가능
    • [현재 프로젝트]/
        ⬇ 📁 [장고 프로젝트 이름]
          > 📁 __pycache__
        ⬇ 📁 [장고 앱 이름]
          > 📁 __pycache__
          ⬇ 📁 migrations
            > 📁 __pycache__
            🖹 0001_initial.py
        🖹 db.sqlite3
    • 위 구조에서 [장고 프로젝트 이름]/__pycache__, [장고 앱 이름]/__pycache__, [장고 앱 이름]/migrations/__pycache__, [장고 앱 이름]/migrations/0001_initial.py, db.sqlite3을 삭제
    • 모든 캐시 파일(__pycache__), 마이그레이션 내역(0001_initial.py), 데이터베이스(db.sqlite3)를 삭제한다면 초기 상태로 돌아가기 가능
    • 위와 같은 파일을 제거할 경우, 기본 Migration부터 다시 진행해야 함

 

2. Serialization

  • Serialization(직렬화): 메모리를 디스크에 저장하거나 네트워크 통신에 사용하기 위한 형식으로 변환하는 것
    • 즉 QuerySet 등의 complex type을 JSON, XML 등의 content type으로 변환시키는 것
  • Desrialization(역직렬화): 그 반대로 디스크에 저장한 데이터를 읽거나, 네트워크 통신으로 받은 데이터를 메모리에 쓸 수 있도록 다시 변환하는 것
  • Django의 Serializer
    • Django에서 사용하는 파이썬 객체, QuerySet 같은 복잡한 객체 (모델 인스턴스)들을 REST API에서 사용할 JSON 과 같은 형태로 변환해주는 어댑터 역할

 

참조:

 

    1. https://aws.amazon.com/ko/what-is/django/
    2. https://docs.djangoproject.com/en/4.2/ref/databases/
    3. https://www.qu3vipon.com/django-orm
    4. https://076923.github.io/posts/Python-Django-9/#django-migration
    5. https://hub1234.tistory.com/26#google_vignette
    6. https://www.incodom.kr/Django_ORM#h_32232b2c9daa072f5dbf7584fba696bf