django-is-awsome/
│
├── accounts/
│ ├── migrations/
│ │ └── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── serializers.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
│
├── transactions/
│ ├── migrations/
│ │ └── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── serializers.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
│
├── core/
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
│
│
├── docker/
│ (폴더 내부 내용 생략 — Docker 관련 설정 파일들이 위치)
│
├── .gitignore
├── .python-version
├── db.sqlite3
├── manage.py
├── poetry.lock
├── pyproject.toml
└── README.md
core/settings.pycore/urls.pyaccounts/ , transactions/ 앱 URL을 include해서 연결.core/wsgi.pycore/asgi.pyaccounts/models.pyaccounts/serializers.pyaccounts/views.pyAccountListCreateView : 로그인 유저의 계좌 목록 조회 & 생성AccountDetailView : 한 계좌 조회·수정·삭제accounts/urls.py/accounts/ 경로에 대해:transactions/models.pytransactions/serializers.pyread_only: user, created_at, updated_attransactions/views.pyTransactionListCreateViewTransactionDetailViewtransactions/urls.py/transactions/ 경로 처리:<pk>/ → 단건 조회·수정·삭제from django.urls import path
from .views import AccountListCreateView, AccountDetailView
app_name = 'accounts'
urlpatterns = [
path('', AccountListCreateView.as_view(), name='account_list'),
path('<int:pk>/', AccountDetailView.as_view(), name='account_detail'),
]
app_name = 'accounts' → URL 네임스페이스 설정
path('', AccountListCreateView.as_view(), name='account_list')
/accounts/ → 계좌 목록 조회 & 생성path('<int:pk>/', AccountDetailView.as_view(), name='account_detail')
/accounts/<pk>/ → 계좌 상세 조회/수정/삭제from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
from .models import Account
from .serializers import AccountSerializer
class AccountListCreateView(ListCreateAPIView):
serializer_class = AccountSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
return Account.objects.filter(user=self.request.user) # 현재 로그인 유저의 계좌만 반환
def perform_create(self, serializer):
serializer.save(user=self.request.user) # 계좌 생성 시 자동으로 user 연결
ListCreateAPIViewpermission_classes = [IsAuthenticated] → 인증 필요IsAuthenticated serializer_class = AccountSerializer → Serializer 지정AccountSerializerclass AccountDetailView(RetrieveUpdateDestroyAPIView):
serializer_class = AccountSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
return Account.objects.filter(user=self.request.user) # 본인의 계좌만 접근 허용
RetrieveUpdateDestroyAPIViewfrom rest_framework import serializers
from .models import Account
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'bank_name', 'account_number', 'balance']
serializers.ModelSerializerfieldsfrom django.db import models # Django 모델 기능 import
from django.contrib.auth import get_user_model
User = get_user_model()
class Account(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='accounts')
bank_name = models.CharField(max_length=50)
account_number = models.CharField(max_length=30, unique=True)
balance = models.IntegerField(default=0) # 잔액 (기본값 0)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.user.username} - {self.bank_name}"
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
from rest_framework.permissions import IsAuthenticated
from .models import Transaction
from .serializers import TransactionSerializer
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter
from rest_framework.exceptions import PermissionDenied
DjangoFilterBackend / OrderingFilter / PermissionDeniedclass TransactionListCreateView(ListCreateAPIView):
serializer_class = TransactionSerializer
permission_classes = [IsAuthenticated]
filter_backends = [DjangoFilterBackend, OrderingFilter]
filterset_fields = ['category']
ordering_fields = ['transacted_at', 'amount']
ListCreateAPIView -> 거래 조회 + 생성 APIfilter_backends = [DjangoFilterBackend, OrderingFilter] -> 필터/정렬 백엔드 적용filterset_fields = ['category'] -> /?category=food 필터ordering_fields = ['transacted_at', 'amount'] -> 정렬 필드 허용 def get_queryset(self):
user = self.request.user # 현재 로그인 유저
qs = Transaction.objects.filter(user=user) # 본인 거래만 조회
start = self.request.query_params.get("start") # 날짜 필터: 시작
end = self.request.query_params.get("end") # 날짜 필터: 끝
if start:
qs = qs.filter(transacted_at__gte=start) # 지정한 날짜 이후
if end:
qs = qs.filter(transacted_at__lte=end) # 지정한 날짜 이전
return qs
def perform_create(self, serializer):
account = serializer.validated_data['account'] # body에서 account 필드 추출
if account.user != self.request.user: # 본인 계좌인지 확인
raise PermissionDenied("이 계좌에 거래를 생성할 수 없습니다.") # 아니면 오류 발생
serializer.save(user=self.request.user) # 거래 생성 시 user 자동 등록
class TransactionDetailView(RetrieveUpdateDestroyAPIView):
serializer_class = TransactionSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
return Transaction.objects.filter(user=self.request.user) # 본인 거래만 접근 가능
RetrieveUpdateDestroyAPIViewfrom django.urls import path
from .views import TransactionListCreateView, TransactionDetailView
app_name = 'transactions'
urlpatterns = [
path('', TransactionListCreateView.as_view(), name='transaction_list'),
path('<int:pk>/', TransactionDetailView.as_view(), name='transaction_detail'),
]
/transactions//transactions/<pk>/from rest_framework import serializers
from .models import Transaction
class TransactionSerializer(serializers.ModelSerializer):
class Meta:
model = Transaction
fields = [
'id', 'account','user' ,'amount', 'category', 'memo',
'transacted_at', 'created_at', 'updated_at'
]
read_only_fields = ['user', 'created_at', 'updated_at']
read_only_fields -> 수정 불가 필드from django.db import models
from django.contrib.auth import get_user_model
from accounts.models import Account
User = get_user_model()
class Transaction(models.Model):
CATEGORY_CHOICES = [
('food', '식비'),
('transport', '교통'),
('shopping', '쇼핑'),
('income', '수입'),
('etc', '기타'),
]
account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name="transactions") # 거래된 계좌
user = models.ForeignKey(User, on_delete=models.CASCADE) # 거래 주인 (로그인 유저)
amount = models.IntegerField() # 금액 (+입금 / -출금)
category = models.CharField(max_length=20, choices=CATEGORY_CHOICES)
memo = models.CharField(max_length=255, blank=True) # 메모 (선택)
transacted_at = models.DateTimeField() # 실제 거래 시각
created_at = models.DateTimeField(auto_now_add=True) # 생성 시각 자동 기록
updated_at = models.DateTimeField(auto_now=True) # 수정 시각 자동 기록
class Meta:
ordering = ["-transacted_at"] # 최신 거래 먼저
def __str__(self):
return f"{self.account} / {self.amount}" # 관리자 페이지 등에서 보일 문자열
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls), # /admin/ 관리자 페이지
path("accounts/", include('accounts.urls')), # accounts 앱 URL 연결
path("transactions/", include('transactions.urls')), # transactions 앱 URL 연결
]
# settings.py
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
]
# Views.py
permission_classes = [IsAuthenticated]
http://127.0.0.1:8000/accounts/http://127.0.0.1:8000/accounts/1/
http://127.0.0.1:8000/transactions/http://127.0.0.1:8000/transactions/1/
http://127.0.0.1:8000/transactions/?category=food -> 식비만 보기 http://127.0.0.1:8000/transactions/?category=shopping -> 쇼핑만 보기

start = self.request.query_params.get("start")
end = self.request.query_params.get("end")
if start:
qs = qs.filter(transacted_at__gte=start)
if end:
qs = qs.filter(transacted_at__lte=end)
http://127.0.0.1:8000/transactions/?start=2025-01-01http://127.0.0.1:8000/transactions/?end=2025-01-31http://127.0.0.1:8000/transactions/?start=2025-01-01&end=2025-01-31
http://127.0.0.1:8000/transactions/?category=food&start=2025-01-01&end=2025-01-15
ordering_fields = ['transacted_at', 'amount']
/transactions/?category=food&ordering=-transacted_at/transactions/?category=shopping&ordering=amount/transactions/?category=transport&start=2025-01-01&end=2025-01-31&ordering=-amountpoetry add drf-yasgsettings.py 설정 -> INSTALLED_APPS -> 'drf_yasg'
http://127.0.0.1:8000/swagger/ / http://127.0.0.1:8000/redoc/