Django +2

LEE EUI JOO·2023년 2월 3일
0

Web Programming

목록 보기
4/17
post-custom-banner

데이터 삽입

  • 파일 업로드

    • 데이터베이스에서 파일 업로드 처리

    • 일반적으로 파일의 경로를 데이터베이스에 저장 - blob 타입으로 파일의 내용을 저장

    • 파일의 경로를 저장할 때 고려 사항 - 파일 이름의 중복 - UUID와 업로드 날짜 같은 정보를 추가해서 파일 이름을 생성

    • 하나의 디렉토리에 저장할 수 있는 파일의 개수 제한

    • 파일의 저장 위치 - 최근에는 거의 별도의 파일 서버에 저장

  • 파일 업로드 경로를 설정 - settings.py

    BASE_DIR(==mysite) 밑에 만들어야 함

파일 업로드 위치 설정

  • 프로젝트의 media 디렉토리로 설정

  • 프로젝트에 media 디렉터리 생성

  • 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)
  • DB 에 접속해서 기존 테이블 삭제

  • 데이터베이스 마이그레이션 작업
python manage.py makemigrations

python manage.py migrate 



  • urls.py 파일에 삽입 요청에 대한 처리
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)
  • index.html 파일에 삽입을 위한 링크 추가

<!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>
  • 삽입 화면 templates 디렉토리에 생성 - inserthtml.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>
  • views.py 파일에 데이터 삽입을 위한 라우팅 처리 함수 작성
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('/')
  • detail.html 파일의 이미지 출력 부분 수정

{% 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>
  • 접속 - 데이터삽입 - 데이터 입력 - 확인


SSR (Server Side Rendering)

  • 서버에서 데이터를 템플릿 엔진을 출력해서 클라이언트에게 전달하는 방식을 이용한 ToDO 애플리케이션 제작 - 서버와 클라이언트 화면을 하나의 프로젝트에 셍성

  • 개발 환경

    • Programming Language

      • Back End - Python

      • Front End - HTML, CSS, JavaScript

    • Database : Maria DB 10

    • Framework

      • Back End : Django

      • Front End : Bootstrap(반응형 웹을 쉽게 만들어주는 라이브러리)

    • IDE : Pycharm


앱 생성

  • 프로젝트가 저장될 디렉토리를 생성하고 가상 환경을 설정

  • 필요한 패키지 설치 - django, mysql-client
  • 프로젝트 생성 : django-admin startproject mytodo
  • 앱 생성

  • 기본 설정 (settings.py)- DB, TimeZone 설정
# 위에 넣어 주기
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

화면에 출력되는 파일의 디렉토리를 생성 - 애플리케이션/templates/todo

애플리케이션 디렉터리 안에 forms.py(데이터 삽입과 수정 화면을 위한 파일) 파일을 생성


관리자 계정 생성 - python manage.py createsuperuser

  • 아마 테이블 생성이 완료가 안돼서 생성할 수 없다는 오류를 뿜을 것임

  • 해결 방법은 일단 마이그레이션을 진행하고 생성하면 해결될 것이다

모델 생성 - models.py

  • 테이블 설계
    • 제목 - 문자열(길이는 100자까지)
    • 설명 - 긴 문자열
    • 생성 날짜 - 날짜
    • 완료 여부 - boolean
    • 중요성 - boolean
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

데이터 베이스에 모델 변경 내용을 적용

  • python manage.py makemigration
  • python manage.py migrate

DB 에 접속해서 테이블이 생성되었는지 확인

  • desc 테이블이름
    • 구조를 확인해보면 model을 만들 때 pk를 설정하지 않아 id라고 정수 컬럼이 추가가 됐고, 이 컬럼이 기본키가 된다.

superuser 생성

관리자가 생성된 모델을 확인할 수 있도록 admin.py 파일을 수정

from django.contrib import admin
from .models import ToDo
# Register your models here.
admin.site.register(ToDo)

django 실행하여 localhost:8000/admin 크롬창으로 접속

ToDo Add

완료되지 않은 항목 가져오기

  • 출력할 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})
  • URL 과 출력하기 위한 함수를 연결 - mytodo 의 urls.py
"""
from django.contrib import admin
from django.urls import path
from todo import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.todo_list),
]
  • templates/todo/ 디렉토리에 todo_list.html 피일 생성
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>메인</title>
</head>
<body>
메인
{{todos}}
</body>
</html>

부트스트랩 사용 - class 사용해서 +모양, 버튼 모양을 추가 가능

  • 템플릿 기능 작성 - todo/templates/todo 디렉토리 안에 완료되지 않은 todo 목록을 출력할 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=""><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>

ToDo 추가🔥

  • Model에 설정된 클래스의 폼을 자동으로 생성하도록 forms.py 파일에 작성 - 🔥Django의 강력한 기능🔥 (삽입 과 수정에 활용)

    • fields 에 필요한 칼럼을 추가하면 된다
from  django import forms
from .models import ToDo

class ToDoForm(forms.ModelForm):
    class Meta:
        model = ToDo
        fields = ('title', 'description','important')
  • url 과 처리할 함수를 연결 - urls.py
path('post',views.todo_post)
  • 처리할 함수를 작성 - views.py
#폼을 만들었으니 폼을 임포트
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')
  • 삽입할 화면을 작성 - templates/todo/todo_post.html
<!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>

todo_list 파일에 삽입에 대한 링크 추가

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>

urls.py 에서 name=''으로 별명 추가

"""
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)를 생성

    • detail 이라는 요청을 히고 pk에 todo의 pk값을 전달한다.
<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>
  • urls.py파일에 detail과 처리할 함수를 설정
path('<int:pk>',views.todo_detail, name='detail'),
  • views.py에서 상세보기를 처리할 todo_detail 함수 작성
def todo_detail(request,pk):
    #pk 에 해당하는 데이터 찾아오기
    todo= ToDo.objects.get(id=pk)
    return render(request,'todo/todo_detail.html',{'todo':todo})
  • templates/todo 디렉토리에 상세보기 출력화면인 todo_detail.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>
<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>
  • urls.py 파일에서 함수와 연결
path('<int:pk>/edit',views.todo_edit, name='edit'),
  • views.py 에서 todo_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 파일에 완료, 완료 목록 버튼에 링크를 설정

    • 전체 작업에는 pk를 넘겨줄 필요없고 하나의 작업을 할때에는 pk를 넘기는 방식을 사용해야한다
<!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>

  • urls.py 파일에 완료 요청과 완료된 보기 요청을 위한 url 을 설정
"""
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')

]
  • views.py 파일에 완료와 완료목록을 처리 하는 함수를 작성
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')
    
  • done_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/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>

  • 완료 버튼 클릭

  • 목록이 없어진 것을 볼 수 있음
  • 목록을 보기 위해 완료한 TODO 목록 클릭


서버 사이드 렌더링에서의 작업

  • C : 삽입은 삽입 화면으로 이동하고 (GET으로 처리) 내용을 입력하고 실제 삽입을 요청(POST로 처리)

  • R : 조회는 하나의 처리 (GET으로 처리)

  • U : 수정은 수정하고자 하는 데이터를 찾아서 수정 화면으로 출력하고(GET) 내용을 입력하고 실제 수정을 요청(POST - put)

  • D : 삭제는 하나의 처리(GET - delete)


profile
무럭무럭 자라볼까
post-custom-banner

0개의 댓글