📚 2. Django ORM을 활용하여 Model 생성하기
1️⃣ 프로젝트 초기 세팅
🔸 프로젝트 생성
django-admin startproject account-book .
python manage.py startapp accountbook
2️⃣ 모델 작성 (models.py)
✏️ 처음 작성한 models.py
처음 models.py를 작성할 때, 우리는 다음처럼 만들었어:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.utils import timezone
from .constants import (
BANK_CODES, ACCOUNT_TYPE, TRANSACTION_TYPE,
TRANSACTION_METHOD, ANALYSIS_TYPES, ANALYSIS_ABOUT
)
class CustomUserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
if not email:
raise ValueError('이메일은 필수입니다')
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password=None, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_admin', True)
extra_fields.setdefault('is_active', True)
return self.create_user(email, password, **extra_fields)
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=255, unique=True)
nickname = models.CharField(max_length=50, blank=True)
name = models.CharField(max_length=50, blank=True)
phone_number = models.CharField(max_length=20, blank=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
last_login = models.DateTimeField(auto_now=True)
date_joined = models.DateTimeField(default=timezone.now)
objects = CustomUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
class Meta:
db_table = 'users'
class Account(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='accounts')
account_number = models.CharField(max_length=30)
bank_code = models.CharField(max_length=3, choices=BANK_CODES, default='000')
account_type = models.CharField(max_length=20, choices=ACCOUNT_TYPE, default='CHECKING')
balance = models.DecimalField(max_digits=15, decimal_places=2, default=0)
class Meta:
db_table = 'accounts'
class TransactionHistory(models.Model):
account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='transactions')
transaction_amount = models.DecimalField(max_digits=15, decimal_places=2)
post_transaction_amount = models.DecimalField(max_digits=15, decimal_places=2)
transaction_details = models.CharField(max_length=255)
transaction_type = models.CharField(max_length=20, choices=TRANSACTION_TYPE)
transaction_method = models.CharField(max_length=20, choices=TRANSACTION_METHOD)
transaction_timestamp = models.DateTimeField()
class Meta:
db_table = 'transaction_history'
class Analysis(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='analyses')
analysis_target = models.CharField(max_length=20, choices=ANALYSIS_ABOUT)
analysis_period = models.CharField(max_length=20, choices=ANALYSIS_TYPES)
start_date = models.DateField()
end_date = models.DateField()
description = models.TextField(blank=True)
result_image = models.ImageField(upload_to='analysis_results/', blank=True, null=True)
class Meta:
db_table = 'analysis'
class Notification(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='notifications')
message = models.TextField()
is_read = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'notifications'
3️⃣ constants.py 작성
모델에서 사용하기 위해 각종 상수 데이터 (choices) 파일도 만들었어.
BANK_CODES = [
("000", "알수없음"),
("001", "한국은행"),
("002", "산업은행"),
# ...(생략)...
("090", "카카오뱅크"),
("092", "토스뱅크"),
]
ACCOUNT_TYPE = [
("CHECKING", "입출금"),
("SAVING", "적금"),
("LOAN", "대출"),
]
TRANSACTION_TYPE = [
("DEPOSIT", "입금"),
("WITHDRAW", "출금"),
]
TRANSACTION_METHOD = [
("ATM", "ATM 거래"),
("TRANSFER", "계좌이체"),
("CARD", "카드결제"),
]
ANALYSIS_TYPES = [
("DAILY", "일간"),
("WEEKLY", "주간"),
("MONTHLY", "월간"),
]
ANALYSIS_ABOUT = [
("TOTAL_SPENDING", "총 지출"),
("TOTAL_INCOME", "총 수입"),
]
Media 파일 설정 settings.py
result_image 필드 썼으니까 settings.py에 아래 추가 필요:
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
(배포할 때는 AWS S3나 별도 스토리지 쓰면 됨.)
# config/urls.py
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
# your urls
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
✅ 지금은
- 그냥 BASE_DIR / media에 저장해도 돼. (개발이니까)
✅ 배포할 때
- AWS S3 같은 외부 스토리지에 저장해야 해.
- Django storages 라이브러리 설치하고, S3 설정 연결.
pip install django-storages[boto3]
그리고 settings에 이렇게 S3 세팅:
# settings.py
# S3 설정
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
AWS_ACCESS_KEY_ID = 'your-aws-access-key'
AWS_SECRET_ACCESS_KEY = 'your-aws-secret-key'
AWS_STORAGE_BUCKET_NAME = 'your-bucket-name'
AWS_QUERYSTRING_AUTH = False # public access
📝 pyproject.toml 파일 설정 루트폴더
[tool.black]
line-length = 88
target-version = ['py311'] # 너가 쓰는 Python 버전 맞춰 (ex. 3.12이면 py312)
skip-string-normalization = true
include = '\.pyi?$'
[tool.isort]
profile = "black"
line_length = 88
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
combine_as_imports = true
known_first_party = ["accountbook"] # 너 프로젝트 app 이름
skip_gitignore = true
4️⃣ Pillow 설치 문제
🛑 ImageField 때문에 Pillow가 필요했는데 처음엔 설치가 안 되어 있었어.
- 로컬에서:
pip install Pillow
- Docker에서는 requirements.txt에 추가했어:
Django==5.2.2
Pillow
psycopg2
그리고 도커 재빌드:
docker-compose up -d --build
5️⃣ settings.py 분리
개발환경/배포환경 분리해서 세팅했지.
- settings/base.py: 공통
- settings/dev.py: 로컬용 (localhost, 직접 깔린 Postgres 연결)
- settings/prod.py: Docker용 (my-db 서비스 연결)
⚙️ manage.py 수정
→ 환경변수 기반으로 dev/prod 자동선택:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", os.getenv("DJANGO_SETTINGS_MODULE", "config.settings.dev"))
⚙️ docker-compose.yml 수정
→ 환경변수 기반으로 prod 자동선택:
environment:
- DJANGO_SETTINGS_MODULE=config.settings.prod
6️⃣ 로컬 마이그레이션
✅ makemigrations
python manage.py makemigrations accountbook --settings=config.settings.dev
✅ migrate
python manage.py migrate --settings=config.settings.dev
7️⃣ 마이그레이션 충돌 발생
🛑 에러
InconsistentMigrationHistory:
Migration admin.0001_initial is applied before its dependency accountbook.0001_initial
📚 원인: 기존 admin 테이블은 migrate 됐고 accountbook은 안 된 상태.
🛠️ 해결:
- 로컬 Postgres DB Drop
- 새로 생성
DROP DATABASE "django-postgres";
CREATE DATABASE "django-postgres" WITH OWNER "postgres";
다시 migrate:
python manage.py migrate --settings=config.settings.dev
8️⃣ Django Admin 설정
Django Admin 설정
🔸 admin.py 작성
from django.contrib import admin
from .models import CustomUser, Account, TransactionHistory, Analysis, Notification
@admin.register(CustomUser)
class CustomUserAdmin(admin.ModelAdmin):
list_display = ('email', 'nickname', 'is_admin', 'is_active')
search_fields = ('email', 'nickname')
list_filter = ('is_admin', 'is_active')
ordering = ('email',)
@admin.register(Account)
class AccountAdmin(admin.ModelAdmin):
list_display = ('user', 'account_number', 'bank_code', 'account_type', 'balance')
@admin.register(TransactionHistory)
class TransactionHistoryAdmin(admin.ModelAdmin):
list_display = ('account', 'transaction_amount', 'transaction_type', 'transaction_timestamp')
@admin.register(Analysis)
class AnalysisAdmin(admin.ModelAdmin):
list_display = ('user', 'analysis_target', 'analysis_period', 'start_date', 'end_date')
@admin.register(Notification)
class NotificationAdmin(admin.ModelAdmin):
list_display = ('user', 'message', 'is_read', 'created_at')
- admin.py에 등록 완료
- 슈퍼유저 생성:
python manage.py createsuperuser --settings=config.settings.dev
9️⃣ Docker 환경 마이그레이션
✅ 도커컴포즈 올림
docker-compose up -d --build
✅ 컨테이너 접속
docker exec -it django bash
✅ migrate
python manage.py migrate
1️⃣0️⃣ Docker 마이그레이션 충돌
🛑 또 에러 발생:
InconsistentMigrationHistory:
Migration admin.0001_initial is applied before its dependency accountbook.0001_initial
🛠️ 해결:
- docker-compose down -v 로 볼륨 초기화
- docker-compose up -d --build 다시 빌드/실행
- 다시 migrate 성공
📝 정리
항목상태
models.py 작성 | ✅ |
constants.py 작성 | ✅ |
Pillow 설치 | ✅ |
settings 분리 (base/dev/prod) | ✅ |
manage.py 환경변수 분기 처리 | ✅ |
로컬 마이그레이션 | ✅ |
Migration 충돌 해결 (DB Drop) | ✅ |
Admin 설정 및 슈퍼유저 생성 | ✅ |
Docker 환경 마이그레이션 | ✅ |
Docker 볼륨 초기화 후 migrate 성공 | ✅ |
'프로젝트' 카테고리의 다른 글
📌 [3단계] DRF(Django REST Framework) 설치 및 Docker 적용 (0) | 2025.06.09 |
---|---|
📮4. Django DB 연결 대기 커스텀 커맨드 제작 및 GitHub Actions 적용기 (0) | 2025.06.09 |
📮 3. ERD를 구성 Mermid 활용 (0) | 2025.06.09 |
📮2. 데이터베이스 스키마 설계 (1) | 2025.06.09 |
📮 1. ERD(Entity Relationship Diagram) 작성하기 (0) | 2025.06.09 |