더보기
76일 차 회고.
밤에 잠에 일찍 안 들다 보니까 하루 종일 피곤해서 오늘은 일찍 자야 할 것 같다. 특강을 다 들으면 9시 반이 되니까 12시 전에는 자도록 노력해봐야 할 것 같다.
1. Django
1-1. Login - Class
가상환경 생성
uv venv .venv -p 3.13
.\.venv\Scripts\activate
uv pip install django mysqlclient
Django 프로젝트 생성
django-admin startproject config .
Django 앱 생성
python manage.py startapp user
python manage.py startapp todolist
teamplates 생성
mkdir templates
mkdir .\templates\base
mkdir .\templates\user
mkdir .\templates\todolist
static 생성
mkdir static
mkdir .\static\css
코드 작성
- TEMPLATE & STATIC 기존 코드 사용
- MODEL
# user/models.py
from django.db import models
# AbstractBaseUser: A base class for creating custom user models.
# BaseUserManager: A helper class for creating user managers for custom user models.
# PermissionsMixin: A mixin that provides the permission-related fields and methods.
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
class CustomUserManager(BaseUserManager):
use_in_migrations = True
# Create user
def create_user(self, username, email=None, password=None):
if not email:
raise ValueError('The Email field must be set')
user = self.model(
username=username,
email=self.normalize_email(email),
)
user.set_password(password)
user.save(using=self._db)
return user
# Create superuser
def create_superuser(self, username, email=None, password=None):
user = self.create_user(
username=username,
email=email,
password=password,
)
user.is_admin = True
user.is_superuser=True
user.save(using=self._db)
return user
class CustomUser(AbstractBaseUser, PermissionsMixin):
objects = CustomUserManager()
username = models.CharField(max_length=30, unique=True, null=False)
email = models.EmailField(max_length=200, unique=True, null=False)
is_superuser = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
created_dt = models.DateTimeField(auto_now_add=True)
updated_dt = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'custom_user'
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
# todolist/models.py
from django.db import models
from user.models import CustomUser
class TodoList(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
status = models.BooleanField(default=False)
created_dt = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class Meta:
db_table = 'todo_list'
ordering = ['status', '-created_dt']
- VIEW
# user/views.py
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login, logout
from django.contrib import messages
from .models import CustomUser
def user_register(request):
if request.user.is_authenticated:
return redirect(to='todolist')
elif request.method == 'POST':
username = request.POST.get('username').strip()
email = request.POST.get('email').strip()
password = request.POST.get('password').strip()
if CustomUser.objects.filter(username=username).exists():
messages.error(request, 'Username already exists')
return redirect(to='user-register')
elif not username or not email or not password:
messages.error(request, 'Email and password are required')
return redirect(to='user-register')
new_user = CustomUser.objects.create_user(
username=username,
email=email,
password=password
)
new_user.save()
return redirect(to='user-login')
return render(request, 'user/register.html')
def user_login(request):
if request.user.is_authenticated:
return redirect(to='todolist')
elif request.method == 'POST':
username = request.POST.get('username').strip()
password = request.POST.get('password').strip()
auth_user = authenticate(username=username, password=password)
if not auth_user:
messages.error(request, 'Invalid username or password')
return redirect(to='user-login')
login(request, auth_user)
return redirect(to='todolist')
return render(request, 'user/login.html')
def user_logout(request):
logout(request)
return redirect(to='user-login')
# todolist/views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from .models import TodoList
@login_required(login_url='user-login')
def todolist(request):
if request.method == 'POST':
task = request.POST.get('task').strip()
if TodoList.objects.filter(title=task, user=request.user).exists():
messages.error(request=request, message='Task already exists')
return redirect(to='todolist')
elif not len(task):
messages.error(request=request, message='Task cannot be empty')
return redirect(to='todolist')
new_task = TodoList(title=task, user=request.user)
new_task.save()
content = {
"todo_list": TodoList.objects.filter(user=request.user).order_by('status'),
}
return render(request, 'todolist/todolist.html', content)
@login_required(login_url='user-login')
def update_task(request, task_id):
task = TodoList.objects.get(id=task_id, user=request.user)
task.status = True
task.save()
return redirect(to='todolist')
@login_required(login_url='user-login')
def delete_task(request, task_id):
TodoList.objects.get(id=task_id, user=request.user).delete()
return redirect(to='todolist')
- URL
# user/urls.py
from django.urls import path
from .views import user_register, user_login, user_logout
urlpatterns = [
path('register/', user_register, name='user-register'),
path('login/', user_login, name='user-login'),
path('logout/', user_logout, name='user-logout'),
]
# todolist/urls.py
from django.urls import path
from .views import todolist, update_task, delete_task
urlpatterns = [
path('', todolist, name='todolist'),
path('todolist-update/<int:task_id>', update_task, name='todolist-update'),
path('todolist-delete/<int:task_id>', delete_task, name='todolist-delete'),
]
# config/urls.py
from django.urls import path, include
urlpatterns = [
path('user/', include('user.urls')),
path('', include('todolist.urls')),
]
- SETTING
# config/settings.py
import os
from pathlib import Path
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'user',
'todolist',
]
...
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
...
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': '',
'USER': '',
'PASSWORD': '',
'HOST': 'localhost',
'PORT': '3306',
}
}
...
STATIC_URL = 'static/'
STATIC_PATH = os.path.join(BASE_DIR, 'static')
STATICFILES_DIRS = (STATIC_PATH,)
...
AUTH_USER_MODEL = 'user.CustomUser'
서버 실행
python manage.py makemigrations
python manage.py migrate
python manage.py runserver
1-2. Docker
Django
- 아래의 수정된 코드를 제외하고는 위의 코드를 사용한다.
# django_server/requirements.txt
django
gunicorn
# django_server/config/settings.py
...
SECRET_KEY = os.getenv('SECRET_KEY')
DEBUG = os.getenv('DEBUG')
ALLOWED_HOSTS = ['*']
CSRF_TRUSTED_ORIGINS = [
'http://localhost',
'http://127.0.0.1',
'http://localhost:8000',
'http://127.0.0.1:8000',
'http://0.0.0.0',
'http://0.0.0.0:8000',
]
...
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
...
STATIC_ROOT = '/static/'
STATIC_URL = 'static/'
STATIC_PATH = os.path.join(BASE_DIR, 'static')
STATICFILES_DIRS = (STATIC_PATH,)
...
Nginx
- Configure
# nginx_server/default.conf
upstream django {
server django_gunicorn:8000;
}
server {
listen 80;
location / {
proxy_pass http://django;
}
location /static/ {
alias /static/;
}
}
- Dockerfile
# nginx_server/Dockerfile
FROM nginx
COPY ./default.conf /etc/nginx/conf.d/default.conf
Docker
- Environment
SECRET_KEY=
DEBUG=False
SUPER_USER_NAME=
SUPER_USER_PASSWORD=
SUPER_USER_EMAIL=
- Dockerfile
# Dockerfile
FROM python:3.13-alpine
COPY ./django_server /app
COPY ./entrypoint.sh /app/entrypoint.sh
WORKDIR /app
RUN pip3 install --upgrade pip && pip3 install -r /app/requirements.txt
ENTRYPOINT [ "sh", "/app/entrypoint.sh" ]
- Shell
# entrypoint.sh
#!/bin/sh
# models.py
python manage.py makemigrations --no-input
# apply migration
python manage.py migrate --no-input
# collect static file -> Nginx(WEB server)
python manage.py collectstatic --no-input
DJANGO_SUPERUSER_PASSWORD=$SUPER_USER_PASSWORD python manage.py createsuperuser --username $SUPER_USER_NAME --email $SUPER_USER_EMAIL --noinput
# gunicorn(WAS server)
gunicorn config.wsgi:application --bind 0.0.0.0:8000
- Docker Compose
# docker-compose.yml
version: '3.7'
services:
django_gunicorn:
container_name: django_gunicorn
volumes:
- static:/static
env_file:
- .env
build:
context: .
ports:
- '8000:8000'
networks:
- compose_net
nginx:
container_name: nginx
build: ./nginx_server
volumes:
- static:/static
ports:
- '80:80'
depends_on:
- django_gunicorn
networks:
- compose_net
volumes:
static:
networks:
compose_net:
name: django_net
driver: bridge
Docker 실행
docker-compose up -d
'SK네트웍스 Family AI캠프 10기 > Daily 회고' 카테고리의 다른 글
| 78일차. AWS - EC2 (1) | 2025.05.02 |
|---|---|
| 77일차. AWS - IAM & EC2 (0) | 2025.04.30 |
| 75일차. Django (0) | 2025.04.28 |
| 74일차. Django (0) | 2025.04.25 |
| 73일차. Django (0) | 2025.04.24 |