Django 모델 만들기 방법

소개

이전 튜토리얼 “Django 앱 만들고 데이터베이스에 연결하는 방법”에서는 MySQL 데이터베이스를 만드는 방법, Django 애플리케이션을 만들고 시작하는 방법, 그리고 MySQL 데이터베이스에 연결하는 방법에 대해 다루었습니다.

이번 튜토리얼에서는 저장할 블로그 애플리케이션 데이터의 필드 및 동작을 정의하는 Django 모델을 만들 것입니다. 이러한 모델은 Django 애플리케이션의 데이터를 데이터베이스에 매핑합니다. 이것이 Django가 객체 관계 매핑(ORM) API를 통해 데이터베이스 테이블을 생성하는 데 사용하는 것으로, “모델”이라고 합니다.

필수 조건

이 튜토리얼은 Django 개발 시리즈의 일부이며 해당 시리즈의 연속입니다.

이 시리즈를 따라오지 않았다면 다음을 가정합니다:

  • Django 버전 4 이상이 설치되어 있습니다.
  • Django 앱을 데이터베이스에 연결했습니다. MySQL을 사용하며, 이 연결을 하기 위해 Django 시리즈의 “Django 앱을 만들고 데이터베이스에 연결하는 방법”의 두 번째 부분을 따라할 수 있습니다.
  • Unix 기반 운영 체제, 특히 우분투 22.04 클라우드 서버에서 작업하고 있다고 가정합니다. 비슷한 환경에서 Django를 설정하려면 “우분투 22.04에서 Django를 설치하고 개발 환경을 설정하는 방법” 튜토리얼을 참조하십시오.

이 튜토리얼은 주로 Django 모델에 관련된 내용을 다루므로, 약간 다른 설정이 있더라도 따라할 수 있을 수도 있습니다.

단계 1 – Django 앱 생성

모듈성의 Django 철학과 일치하기 위해, 블로그 웹사이트를 만들기 위해 필요한 모든 파일을 포함하는 Django 앱을 프로젝트 내에 생성할 것입니다.

Python과 Django에서 작업을 시작할 때는 Python 가상 환경을 활성화하고 앱의 루트 디렉토리로 이동해야 합니다. 시리즈를 따라했다면 다음을 입력하여 이를 수행할 수 있습니다.

  1. cd ~/my_blog_app
  2. . env/bin/activate
  3. cd blog

그런 다음, 다음 명령을 실행해봅시다.

  1. python manage.py startapp blogsite

이제 우리 앱을 만들 것입니다. blogsite 디렉터리와 함께.

튜토리얼 시리즈의 이 시점에서 다음과 같은 프로젝트 디렉터리 구조가 있을 것입니다:

my_blog_app/
└── blog
    ├── blog
    │   ├── __init__.py
    │   ├── __pycache__
    │   │   ├── __init__.cpython-38.pyc
    │   │   ├── settings.cpython-38.pyc
    │   │   ├── urls.cpython-38.pyc
    │   │   └── wsgi.cpython-38.pyc
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── blogsite
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── migrations
    │   │   └── __init__.py
    │   ├── models.py
    │   ├── tests.py
    │   └── views.py
    └── manage.py

이 튜토리얼에서 집중할 파일은 models.py 파일입니다. 이 파일은 blogsite 디렉터리에 있습니다.

단계 2 — 포스트 모델 추가

먼저, models.py 파일을 열고 편집하여 Post 모델을 생성하는 코드를 포함시켜야 합니다. Post 모델에는 다음과 같은 데이터베이스 필드가 포함됩니다:

  • title — 블로그 게시물의 제목입니다.
  • slug — 유효한 URL이 저장되고 웹 페이지를 생성합니다.
  • content — 블로그 게시물의 본문 내용입니다.
  • created_on — 게시물이 생성된 날짜입니다.
  • author — 게시물을 작성한 사람입니다.

이제 models.py 파일이 있는 디렉터리로 이동하세요.

  1. cd ~/my_blog_app/blog/blogsite

cat 명령을 사용하여 터미널에서 파일 내용을 표시합니다.

  1. cat models.py

파일에는 모델을 가져오는 코드와 함께 이 models.py 파일에 무엇을 넣어야 하는지 설명하는 주석이 있어야 합니다.

models.py
from django.db import models

# 여기에 모델을 생성하세요.

자주 사용하는 텍스트 편집기를 사용하여 models.py 파일에 다음 코드를 추가하세요. 저희는 텍스트 편집기로 nano를 사용할 것이지만, 원하시는 편집기를 사용하셔도 됩니다.

  1. nano models.py

이 파일 안에는 모델 API를 가져오기 위한 코드가 이미 추가되어 있습니다. 그러므로 뒤이어 나오는 주석은 삭제해도 됩니다. 그런 다음 문자열에서 슬러그를 생성하기 위해 slugify를 가져오고, Django의 인증을 위해 User를 가져오고, URL을 생성할 때 django.urls에서 reverse를 가져옵니다.

models.py
from django.db import models
from django.template.defaultfilters import slugify
from django.contrib.auth.models import User
from django.urls import reverse

그런 다음, 모델 클래스에 호출할 Post라는 클래스 메서드를 추가하고 다음과 같은 데이터베이스 필드를 추가하세요: title, slug, content, created_on, 그리고 author. 이들을 가져오는 문장 아래에 추가하세요.

models.py
...
class Post(models.Model):
    title = models.CharField(max_length=255)
    slug = models.SlugField(unique=True, max_length=255)
    content = models.TextField()
    created_on = models.DateTimeField(auto_now_add=True)
    author = models.TextField()

다음으로, URL 생성 및 게시물 저장 기능을 추가해야 합니다. 이것은 고유한 게시물에 대한 고유한 링크를 생성하기 때문에 중요합니다.

models.py
...
    def get_absolute_url(self):
        return reverse('blog_post_detail', args=[self.slug])

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.title)
        super(Post, self).save(*args, **kwargs)

이제 모델에 포스트가 웹 페이지에서 어떻게 정렬되고 표시될지를 알려줘야 합니다. 이에 대한 로직은 중첩된 내부 Meta 클래스에 추가됩니다. Meta 클래스에는 일반적으로 데이터베이스 필드 정의와 관련이 없는 다른 중요한 모델 로직이 들어 있습니다.

models.py
...
    class Meta:
        ordering = ['created_on']

        def __unicode__(self):
            return self.title

마지막으로, 이 파일에 Comment 모델을 추가하겠습니다. 이를 위해 또 다른 클래스인 Comment를 추가하고 시그니처에 models.Models를 정의하고 다음과 같은 데이터베이스 필드를 정의합니다:

  • name — 댓글을 게시하는 사람의 이름.
  • 이메일 — 댓글을 작성한 사람의 이메일 주소.
  • 텍스트 — 댓글 자체의 텍스트.
  • 포스트 — 댓글이 작성된 게시물.
  • 작성일 — 댓글이 작성된 시간.
models.py
...
class Comment(models.Model):
    name = models.CharField(max_length=42)
    email = models.EmailField(max_length=75)
    website = models.URLField(max_length=200, null=True, blank=True)
    content = models.TextField()
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
    created_on = models.DateTimeField(auto_now_add=True)

이 시점에서 models.py는 완성될 것입니다. models.py 파일이 다음과 일치하는지 확인하세요.

models.py
from django.db import models
from django.template.defaultfilters import slugify
from django.contrib.auth.models import User
from django.urls import reverse


class Post(models.Model):
    title = models.CharField(max_length=255)
    slug = models.SlugField(unique=True, max_length=255)
    content = models.TextField()
    created_on = models.DateTimeField(auto_now_add=True)
    author = models.TextField()

    def get_absolute_url(self):
        return reverse('blog_post_detail', args=[self.slug])
    
    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.title)
        super(Post, self).save(*args, **kwargs)

    class Meta:
        ordering = ['created_on']

        def __unicode__(self):
            return self.title


class Comment(models.Model):
    name = models.CharField(max_length=42)
    email = models.EmailField(max_length=75)
    website = models.URLField(max_length=200, null=True, blank=True)
    content = models.TextField()
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
    created_on = models.DateTimeField(auto_now_add=True)


반드시 파일을 저장하고 닫으세요. nano를 사용하는 경우 CTRLX를 입력한 후 Y, 그런 다음 ENTER로 저장할 수 있습니다.

models.py 파일이 설정되었으면 settings.py 파일을 업데이트할 차례입니다.

단계 3 — 설정 업데이트

이제 애플리케이션에 모델을 추가했으므로 방금 추가한 blogsite 앱의 존재를 프로젝트에 알려야 합니다. 이를 위해 settings.pyINSTALLED_APPS 섹션에 추가합니다.

settings.py 파일이 있는 디렉토리로 이동하세요.

  1. cd ~/my_blog_app/blog/blog

여기서 nano 등을 사용하여 settings.py 파일을 엽니다.

  1. nano settings.py

파일이 열렸으면 아래에 표시된 대로 INSTALLED_APPS 섹션에 blogsite 앱을 추가하세요.

settings.py
# 애플리케이션 정의
INSTALLED_APPS = [
    'blogsite',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

blogsite 앱이 추가된 상태에서 파일을 저장하고 종료할 수 있습니다.

이제 우리는 이러한 변경 사항을 적용할 준비가 되었습니다.

단계 4 — 마이그레이션 만들기

PostComment 모델을 추가했으므로 다음 단계는 이러한 변경 사항을 적용하여 MySQL 데이터베이스 스키마가 이를 인식하고 필요한 테이블을 생성하도록 하는 것입니다.

먼저, 우리는 명령어 makemigrations을 사용하여 모델 변경 사항을 개별 마이그레이션 파일로 패키징해야 합니다. 이 파일들은 Git과 같은 버전 관리 시스템의 commits와 유사합니다.

이제 ~/my_blog_app/blog/blogsite/migrations으로 이동하고 ls를 실행하면 __init__.py 파일만 있는 것을 알 수 있습니다. 이것은 마이그레이션을 추가하면 변경됩니다.

cd를 사용하여 블로그 디렉토리로 이동하십시오.

  1. cd ~/my_blog_app/blog

그런 다음 manage.py에서 makemigrations 명령을 실행하십시오.

  1. python manage.py makemigrations

그런 다음 터미널 창에서 다음 출력을 받아야 합니다:

Output
Migrations for 'blogsite': blogsite/migrations/0001_initial.py - Create model Post - Create model Comment

기억하세요. 우리가 /~/my_blog_app/blog/blogsite/migrations로 이동했을 때 __init__.py 파일만 있었던 것을요? 이제 해당 디렉터리로 다시 cd해보면 두 항목이 추가된 것을 알 수 있습니다: __pycache__0001_initial.py. 0001_initial.py 파일은 makemigrations을 실행할 때 자동으로 생성되었습니다. makemigrations을 실행할 때마다 비슷한 파일이 생성됩니다.

해당 디렉터리에서 less 0001_initial.py를 실행하여 파일 내용을 확인하고 싶다면 실행하세요.

이제 ~/my_blog_app/blog로 이동해보겠습니다:

  1. cd ~/my_blog_app/blog

마이그레이션 파일을 만들었으므로 이 파일들이 설명하는 변경 사항을 데이터베이스에 적용해야 합니다. migrate 명령을 사용하여 변경 사항을 적용하겠습니다. 하지만 먼저, 현재 존재하는 마이그레이션을 확인해 보겠습니다. showmigrations 명령을 사용합니다.

  1. python manage.py showmigrations
Output
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 blogsite [ ] 0001_initial contenttypes [X] 0001_initial [X] 0002_remove_content_type_name sessions [X] 0001_initial

0001_initial에 대한 마이그레이션이 확인되지 않는다는 것에 유의하세요. 이는 방금 PostComment 모델로 만든 마이그레이션입니다.

이제 마이그레이션을 만들면 실행되는 SQL 문을 확인해 보겠습니다. 다음 명령을 사용합니다. 마이그레이션과 마이그레이션 제목을 인수로 취합니다:

  1. python manage.py sqlmigrate blogsite 0001_initial

아래에는 실제로 수행되는 SQL 쿼리가 표시됩니다.

Output
-- -- 모델 Post 생성 -- CREATE TABLE `blogsite_post` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `title` varchar(255) NOT NULL, `slug` varchar(255) NOT NULL UNIQUE, `content` longtext NOT NULL, `created_on` datetime(6) NOT NULL, `author` longtext NOT NULL); -- -- 모델 Comment 생성 -- CREATE TABLE `blogsite_comment` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` varchar(42) NOT NULL, `email` varchar(75) NOT NULL, `website` varchar(200) NULL, `content` longtext NOT NULL, `created_on` datetime(6) NOT NULL, `post_id` integer NOT NULL); ALTER TABLE `blogsite_comment` ADD CONSTRAINT `blogsite_comment_post_id_de248bfe_fk_blogsite_post_id` FOREIGN KEY (`post_id`) REFERENCES `blogsite_post` (`id`);

이제 MySQL 데이터베이스에 마이그레이션을 수행하여 변경 사항이 적용되도록 하겠습니다.

  1. python manage.py migrate

다음 출력을 받게 될 것입니다:

Output
Operations to perform: Apply all migrations: admin, auth, blogsite, contenttypes, sessions Running migrations: Applying blogsite.0001_initial... OK

당신은 이제 마이그레이션을 성공적으로 적용했습니다.

MySQL을 백엔드로 사용하는 경우 Django 마이그레이션에는 세 가지 주의 사항이 있습니다. Django 설명서에 명시된 대로입니다.

  • 스키마 변경 작업 주위의 트랜잭션 지원이 부족합니다. 다시 말해, 마이그레이션이 성공적으로 적용되지 않으면 변경한 내용을 수동으로 다시 해제해야만 다른 마이그레이션을 시도할 수 있습니다. 실패한 마이그레이션 이전에 변경 사항이 없을 때 이전 지점으로 롤백할 수 없습니다.
  • 대부분의 스키마 변경 작업에서 MySQL은 테이블을 완전히 다시 작성합니다. 최악의 경우, 시간 복잡도는 열을 추가하거나 제거하는 테이블의 행 수에 비례합니다. Django 설명서에 따르면, 이것은 백만 행 당 1분이 걸릴 수 있습니다.
  • MySQL에서는 열, 테이블 및 인덱스의 이름 길이에 제한이 있습니다. 또한 모든 열 및 인덱스 커버의 결합된 크기에도 제한이 있습니다. 일부 다른 백엔드는 Django에서 생성된 더 높은 제한을 지원할 수 있지만, 동일한 인덱스는 MySQL 백엔드가 있는 경우 생성되지 못할 것입니다.

Django와 함께 사용할 각 데이터베이스에 대해 각각의 장단점을 고려해야 합니다.

단계 5 — 데이터베이스 스키마 확인

마이그레이션이 완료되면 Django 모델을 통해 생성한 MySQL 테이블이 성공적으로 생성되었는지 확인해야 합니다.

터미널에서 다음 명령을 실행하여 MySQL에 로그인합니다. 우리는 이전 튜토리얼에서 만든 djangouser를 사용할 것입니다.

  1. mysql blog_data -u djangouser

이제, blog_data 데이터베이스를 선택합니다. 사용 중인 데이터베이스를 모를 경우에는 SQL에서 SHOW DATABASES;를 사용하여 모든 데이터베이스를 표시할 수 있습니다.

  1. USE blog_data;

그런 다음 다음 명령을 입력하여 테이블을 보여줍니다.

  1. SHOW TABLES;

이 SQL 쿼리는 다음을 나타내어야 합니다:

Output
+----------------------------+ | Tables_in_blog_data | +----------------------------+ | auth_group | | auth_group_permissions | | auth_permission | | auth_user | | auth_user_groups | | auth_user_user_permissions | | blogsite_comment | | blogsite_post | | django_admin_log | | django_content_type | | django_migrations | | django_session | +----------------------------+ 12 rows in set (0.01 sec)

테이블 중에는 blogsite_commentblogsite_post가 있습니다. 이들은 우리가 방금 만든 모델입니다. 정의한 필드가 있는지 확인해 보겠습니다.

  1. DESCRIBE blogsite_comment;
Output
+------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int | NO | PRI | NULL | auto_increment | | name | varchar(42) | NO | | NULL | | | email | varchar(75) | NO | | NULL | | | website | varchar(200) | YES | | NULL | | | content | longtext | NO | | NULL | | | created_on | datetime(6) | NO | | NULL | | | post_id | int | NO | MUL | NULL | | +------------+--------------+------+-----+---------+----------------+ 7 rows in set (0.00 sec)
  1. DESCRIBE blogsite_post;
Output
+------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int | NO | PRI | NULL | auto_increment | | title | varchar(255) | NO | | NULL | | | slug | varchar(255) | NO | UNI | NULL | | | content | longtext | NO | | NULL | | | created_on | datetime(6) | NO | | NULL | | | author | longtext | NO | | NULL | | +------------+--------------+------+-----+---------+----------------+ 6 rows in set (0.00 sec)

Django 모델 마이그레이션에서 데이터베이스 테이블이 성공적으로 생성된 것을 확인했습니다.

CTRL + D로 MySQL을 종료할 수 있고, Python 환경을 종료하려면 deactivate 명령을 실행할 수 있습니다:

  1. deactivate

프로그래밍 환경 비활성화로 터미널 명령 프롬프트로 돌아갑니다.

결론

{
“error”: “Upstream error…”
}

Source:
https://www.digitalocean.com/community/tutorials/how-to-create-django-models