파일 업로드
데이터베이스에서 파일 업로드 처리
일반적으로 파일의 경로를 데이터베이스에 저장 - blob 타입으로 파일의 내용을 저장
파일의 경로를 저장할 때 고려 사항 - 파일 이름의 중복 - UUID와 업로드 날짜 같은 정보를 추가해서 파일 이름을 생성
하나의 디렉토리에 저장할 수 있는 파일의 개수 제한
파일의 저장 위치 - 최근에는 거의 별도의 파일 서버에 저장
파일 업로드 경로를 설정 - settings.py
BASE_DIR(==mysite) 밑에 만들어야 함
python -m pip install pillow
models.py 파일 수정
from django.db import models
class Item(models.Model):
itemid = models.CharField(max_length=50, primary_key=True)
itemname = models.CharField(max_length=50)
price = models.IntegerField()
description = models.CharField(max_length=50)
pictureurl = models.ImageField(upload_to='images/',blank=True,null=True)
python manage.py makemigrations
python manage.py migrate
from django.contrib import admin
from django.urls import path
# 실제 함수가 위치할 파일을 Import
from mydjango import views
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
# 기본 요청이 왔을 때 mydjango의 views.py 파일의 index 함수가 처리
path("",views.index),
# detail/숫자 요청이 오면 views.py 파일의 detail 함수가 처리
path('detail/<int:itemid>',views.detail),
path('insert', views.insert),
] + static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>목록 보기</title>
</head>
<body>
<h2>상품 목록 화면</h2>
<p><a href="insert">데이터삽입</a></p>
<table border="1">
<thead>
<tr>
<th>상품ID</th>
<th>상품이름</th>
<th>가격</th>
</tr>
</thead>
<tbody>
{% for item in data %}
<tr>
<td>{{item.itemid}}</td>
<td><a href="/detail/{{item.itemid}}">{{item.itemname}}</td>
<td>{{item.price}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>데이터 삽입</title>
</head>
<body>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
아이템 이름<input type="text" name="itemname"/><br/>
아이템 가격<input type="text" name="price"/><br/>
설명<input type="text" name="description"/><br/>
설명<input type="file" name="pictureurl" accept="images/*"/><br/>
<input type="submit" value="삽입"/>
</form>
</body>
</html>
from django.db.models import Max
from django.shortcuts import redirect
def insert(request):
# 데이터 삽입화면으로 이동
if request.method == 'GET':
return render(request,'inserthtml.html')
# 데이터 삽입 처리
else:
# itemid를 생성 - 가장 큰 itemid + 1
# 가장 큰 itemid 찾기
obj = Item.objects.aggregate(itemid = Max('itemid'))
if obj['itemid'] == None:
obj['itemid'] = 0
# 가장 큰 itemid +1
itemid = int(obj['itemid']) + 1
# 파라미터 읽기
itemname = request.POST['itemname']
price = request.POST['price']
description = request.POST['description']
for img in request.FILES.getlist('pictureurl'):
item = Item()
item.itemid = itemid
item.itemname = itemname
item.price = price
item.description = description
item.pictureurl = img
# 데이터 삽입
item.save()
# 삽입 삭제 갱신 작업 후에는 forwarding 을 하지 않고 리다이렉트
return redirect('/')
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>상품 상세보기</title>
</head>
<body>
<h2>상품 상세 보기</h2>
<table border="1">
<tr>
<td>상품 아이디</td>
<td>{{item.itemid}}</td>
</tr>
<tr>
<td>상품 이름</td>
<td>{{item.itemname}}</td>
</tr>
<tr>
<td>가격</td>
<td>{{item.price}}</td>
</tr>
<tr>
<td colspan="2">
<img src="{{item.pictureurl.url}}"/>
</td>
</tr>
</table>
</body>
</html>
서버에서 데이터를 템플릿 엔진을 출력해서 클라이언트에게 전달하는 방식을 이용한 ToDO 애플리케이션 제작 - 서버와 클라이언트 화면을 하나의 프로젝트에 셍성
개발 환경
Programming Language
Back End - Python
Front End - HTML, CSS, JavaScript
Database : Maria DB 10
Framework
Back End : Django
Front End : Bootstrap(반응형 웹을 쉽게 만들어주는 라이브러리)
IDE : Pycharm
# 위에 넣어 주기
import os
from pathlib import Path
import pymysql
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'todo'
]
pymysql.install_as_MySQLdb()
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'tododb',
'USER' : 'euijoo',
'PASSWORD' : 'euijoo',
'HOST' : 'localhost',
'PORT' : '3306'
}
}
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'Asia/Seoul'
USE_I18N = True
USE_TZ = True
from django.db import models
# Create your models here.
class ToDo(models.Model):
title = models.CharField(max_length=100)
description = models.TextField(blank=True)
created = models.DateTimeField(auto_now_add=True)
complete = models.BooleanField(default=False)
important = models.BooleanField(default=False)
# 인스턴스 이름을 출력하는 메서드에 대입했을 때 호출되는 함수
def __str__(self):
return self.title
from django.contrib import admin
from .models import ToDo
# Register your models here.
admin.site.register(ToDo)
출력할 HTML 파일을 생성
데이터를 가져와서 HTML 파일로 출력하기 위한 함수를 생성 - views.py
from django.shortcuts import render
from .models import ToDo
# Create your views here.
def todo_list(request):
# 완료되지 않은 항목 가져오가
todos = ToDo.objects.filter(complete=False)
return render(request,'todo/todo_list.html',
{'todos':todos})
"""
from django.contrib import admin
from django.urls import path
from todo import views
urlpatterns = [
path('admin/', admin.site.urls),
path('', views.todo_list),
]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>메인</title>
</head>
<body>
메인
{{todos}}
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>메인</title>
<link rel = "stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" />
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrapicons@1.7.1/font/bootstrap-icons.css">
</head>
<body>
<h1>ToDo 목록</h1>
<p>
<a href=""><i class="bi-plus"></i>TODO 추가</a>
<a href="" class="btn btn-primary" style="float:right">완료한 TODO 목록</a>
</p>
<ul class="list-group">
{% for todo in todos %}
<li class="list-group-item">
<a href="">{{todo.title}}</a>
{% if todo.important %}
<span class="badge badge-danger">!</span>
{% endif %}
<div style="float:right">
<a href="" class="btn btn-danger">완료</a>
<a href="" class="btn btn-warning">수정</a>
</div>
</li>
{% endfor %}
</ul>
</body>
</html>
Model에 설정된 클래스의 폼을 자동으로 생성하도록 forms.py 파일에 작성 - 🔥Django의 강력한 기능🔥 (삽입 과 수정에 활용)
from django import forms
from .models import ToDo
class ToDoForm(forms.ModelForm):
class Meta:
model = ToDo
fields = ('title', 'description','important')
path('post',views.todo_post)
#폼을 만들었으니 폼을 임포트
from.forms import ToDoForm
from django.shortcuts import redirect
def todo_post(request):
if request.method == "GET":
# Form 을 만들어서 출력할 파일에 전달
form = ToDoForm()
return render(request,'todo/todo_post.html',{'form':form})
else:
# 데이터를 입력한 폼을 가져와서
form = ToDoForm(request.POST)
# 폼의 데이터가 유효하면
if form.is_valid():
# 폼에 입력한 데이터를 가지고 모델을 생성
todo = form.save(commit=False)
# 모델을 데이터베이스에 추가
todo.save()
# 데이터를 추가하고 todo_list로 이동시킴
return redirect('todo_list')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>post 타이틀</title>
<link rel = "stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" />
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrapicons@1.7.1/font/bootstrap-icons.css">
</head>
<body>
<div class="container">
<h1>ToDo 추가</h1>
<div class="row">
<div class="card">
<div class="card-body">
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">
등록
</button>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
href = 'post' 이부분
</head>
<body>
<h1>ToDo 목록</h1>
<p>
<a href="post"><i class="bi-plus"></i>TODO 추가</a>
<a href="" class="btn btn-primary" style="float:right">완료한 TODO 목록</a>
</p>
"""
from django.contrib import admin
from django.urls import path
from django.contrib import admin
from django.urls import path
from todo import views
urlpatterns = [
path('admin/', admin.site.urls),
path('', views.todo_list,name = 'todo_list'),
# 로컬호스트:8000으로 들어오면 views.todo_list 연결
path('post',views.todo_post, name='post'),
]
목록 보기에서 각 항목의 이름이나 아이디를 출력하는 부분에서 링크를 만들어서 기본키의 값과 함께 넘겨주고 넘겨온 기본키에 해당하는 데이터를 찾아서 출력해주는 방식
todo_list.html 파일에서 제목을 출력하는 부분에 상세보기 링크(url)를 생성
<ul class="list-group">
{% for todo in todos %}
<li class="list-group-item">
<a href="{% url 'detail' pk=todo.pk %}">{{todo.title}}</a>
{% if todo.important %}
<span class="badge badge-danger">!</span>
{% endif %}
<div style="float:right">
<a href="" class="btn btn-danger">완료</a>
<a href="" class="btn btn-warning">수정</a>
</div>
</li>
{% endfor %}
</ul>
path('<int:pk>',views.todo_detail, name='detail'),
def todo_detail(request,pk):
#pk 에 해당하는 데이터 찾아오기
todo= ToDo.objects.get(id=pk)
return render(request,'todo/todo_detail.html',{'todo':todo})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>상세보기 출력 화면</title>
<link rel = "stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" />
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrapicons@1.7.1/font/bootstrap-icons.css">
</head>
<body>
<div class="container">
<h1>ToDo 상세보기</h1>
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-body">
<h5 class="card-title">{{todo.title}}</h5>
<p class="card-text">
{{todo.description}}
</p>
<a href="{% url 'todo_list' %}" class="btn btn_primary">
목록으로 이동
</a>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
model 과 연동한 form에 인스턴스를 생성할 때 instance 옵션에 데이터를 설정하면 form에 데이터를 출력한다
데이터 수정은 기본키 값을 매개변수로 받아서 데이터를 조회해서 조회한 데이터를 출력하는 화면을 만들고 그 안에서 수정을 한 후 실제 수정요청을 하면 처리
todo_list.html 파일에 수정 요청을 설정
<div style="float:right">
<a href="" class="btn btn-danger">완료</a>
<a href="{% url 'edit' pk=todo.pk %}" class="btn btn-warning">수정</a>
</div>
path('<int:pk>/edit',views.todo_edit, name='edit'),
def todo_edit(request,pk):
# 기본키에 해당하는 데이터 찾기
todo = ToDo.objects.get(id=pk)
if request.method == 'GET':
form = ToDoForm(instance=todo)
return render(request,'todo/todo_post.html',{'form':form})
else:
form = ToDoForm(request.POST, instance=todo)
if form.is_valid():
todo = form.save(commit=False)
todo.save()
return redirect('todo_list')
todo_list.html 파일에 완료, 완료 목록 버튼에 링크를 설정
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>메인</title>
<link rel = "stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" />
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrapicons@1.7.1/font/bootstrap-icons.css">
</head>
<body>
<h1>ToDo 목록</h1>
<p>
<a href="post"><i class="bi-plus"></i>TODO 추가</a>
<a href="{% url 'done_list' %}" class="btn btn-primary" style="float:right">완료한 TODO 목록</a>
</p>
<ul class="list-group">
{% for todo in todos %}
<li class="list-group-item">
<a href="{% url 'detail' pk=todo.pk %}">{{todo.title}}</a>
{% if todo.important %}
<span class="badge badge-danger">!</span>
{% endif %}
<div style="float:right">
<a href="{% url 'todo_done' pk=todo.pk %}" class="btn btn-danger">완료</a>
<a href="{% url 'edit' pk=todo.pk %}" class="btn btn-warning">수정</a>
</div>
</li>
{% endfor %}
</ul>
</body>
</html>
"""
from django.contrib import admin
from django.urls import path
from django.contrib import admin
from django.urls import path
from todo import views
urlpatterns = [
path('admin/', admin.site.urls),
path('', views.todo_list,name = 'todo_list'),
# 로컬호스트:8000으로 들어오면 views.todo_list 연결
path('post',views.todo_post, name='post'),
path('<int:pk>',views.todo_detail, name='detail'),
path('<int:pk>/edit',views.todo_edit, name='edit'),
path('done/', views.done_list, name='done_list'),
path('done/<int:pk>',views.todo_done, name='todo_done')
]
def done_list(request):
dones = ToDo.objects.filter(complete=True)
return render(request, 'todo/done_list.html',{'dones':dones})
def todo_done(request,pk):
todo = ToDo.objects.get(id=pk)
todo.complete = True
todo.save()
return redirect('todo_list')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>완료된 목록</title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" />
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.1/font/bootstrap-icons.css"/>
</head>
<body>
<div class="container">
<h1>완료된 목록</h1>
<a href="{% url 'todo_list' %}" class="btn btn-primary">
완료되지 않은 목록
</a>
<ul class="list-group">
{% for done in dones %}
<li class="list-group-item">
<a href="{% url 'detail' pk=done.pk %}">
{{done.title}}
</a>
{% if done.important %}
<span class="badge badge-danger">!</span>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
</body>
</html>
C : 삽입은 삽입 화면으로 이동하고 (GET으로 처리) 내용을 입력하고 실제 삽입을 요청(POST로 처리)
R : 조회는 하나의 처리 (GET으로 처리)
U : 수정은 수정하고자 하는 데이터를 찾아서 수정 화면으로 출력하고(GET) 내용을 입력하고 실제 수정을 요청(POST - put)
D : 삭제는 하나의 처리(GET - delete)