(Django) 개발 환경 구축 및 CRUD 구현

  • 가상 환경 및 Python 개발 환경 설정
$ python -m venv venv
$ source venv/Scripts/activate
$ pip install django==3.2.18
$ django-admin startproject pjt
$ cd pjt
$ python (manage.py)(<http://manage.py/>) startapp app
  • settings.pyINSTALLED_APPS에 앱 추가
  • pjt 폴더에 템플릿 폴더 생성(base.html 저장)

Bootstrap CDN을 가져온 후 본문에 차단 태그를 붙여 넣습니다.

    
    
    
  
  
    {% block content %}
    {% endblock content %}
TEMPLATES = (
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': (BASE_DIR / 'templates'),
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': (
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ),
        },
    },
)
  • app 폴더에 templates\app\ 하위 폴더를 만듭니다(HTML 템플릿 저장).

Index.html 만들기

확장 태그로 base.html 호출, 블록 태그 삽입

{% extends 'base.html' %}

{% block content %}
<h1>INDEX</h1>
{% endblock content %}
  • 앱 URL 매핑

urls.py를 각 앱에 매핑

include(): 다른 urlconf를 참조할 수 있도록 도와주는 기능

URL 태그로 쉽게 참조할 수 있도록 경로 함수에 이름을 인수로 포함합니다.

from django.contrib import admin
from django.urls import path, include

urlpatterns = (
    path('admin/', admin.site.urls),
    path('app/', include('app.urls'), name="app"),
)
  • 앱 폴더에서 urls.py 생산하는

다른 앱이 동일한 URL을 사용하는 경우 app_name 생성

from django.urls import path
from . import views

app_name="app"
urlpatterns = (
    path('index/', views.index, name="index"),
)
  • views.index 정의
from django.shortcuts import render

# Create your views here.
def index(request):
    return render(request, 'app/index.html')

→ DB 스키마 정의

from django.db import models

# Create your models here.

class Article(models.Model):
    title = models.CharField(max_length=10)
    content = models.TextField()
    created_at = models.DateTimeField()
    updated_at = models.DateTimeField()
  • 정의된 스키마를 실제 데이터베이스에 반영하도록 마이그레이션
$ python manage.py makemigrations
$ python manage.py migrate
  • QuerySet API를 사용한 CRUD

django는 Python을 통해 데이터를 조작하는 ORM을 제공합니다.

  • 만들다
article = Article() # 클래스를 통한 인스턴스 생성
article.title=""
article.content="" # 인스턴스 변수 생성 후 값 할당
article.save() # save메서드 호출하여 저장
article = Article(title="", content="")
article.save()
Article.objects.create(title="", content="")
  • 읽다
Article.objects.all()
Article.objects.get(pk=1)
Article.objects.filter(title="ssafy")
Article.objects.filter(content__contains=='ss')
  • 업데이트하려면
article = Article.objects.get(pk=1) # 인스턴스 호출
article.title="update" # 인스턴스 변수 변경
article.save() # 저장
  • 끄다
article = Article.objects.get(pk=1) # 인스턴스 호출
article.detete() # 삭제
  • views.py에서 인덱스 함수를 정의합니다.

from django.shortcuts import render
from .models import Article

# Create your views here.
def index(request):
    articles = Article.objects.all()
    context = {
        'articles': articles
    }
    return render(request, 'app/index.html', context)
  • index.html 페이지 설정
{% extends 'base.html' %}

{% block content %}
<h1>INDEX</h1>
<hr>
{% for article in articles %}
    <p>글 번호: {{ article.pk }}</p>
    <p>글 제목: {{ article.title }}</p>
    <p>글 내용: {{ article.content }}</p>
    <hr>
{% endfor %}
{% endblock content %}
  • url.py상세 페이지 URL 설정
from django.urls import path
from . import views

app_name="app"
urlpatterns = (
    path('index/', views.index, name="index"),
    path('<int:pk>/', views.detail, name="detail"),
)
  • views.py에서 세부 기능 정의
def detail(request, pk):
    article = Article.objects.get(pk=pk)
    context = {
        'app': article
    }
    return render(request, 'app/detail.html', context)
  • detail.html 작성
{% extends 'base.html' %}

{% block content %}
<h1>DETAIL</h1>
<h3> {{ article.pk }} 번째 글</h3>
<hr>
<p>제목: {{ app.title }}</p>
<p>내용: {{ app.content }}</p>
<p>작성: {{ app.created_at }}</p>
<p>수정: {{ app.updated_at }}</p>
<hr>
<a href="http://stubborngastropod.m/{% url"app:index' %}">뒤로 가기</a>
{% endblock content %}
  • index.html의 게시물을 클릭할 수 있도록 태그 추가
{% extends 'base.html' %}

{% block content %}
<h1>INDEX</h1>
<hr>
{% for article in app %}
    <p>글 번호: {{ article.pk }}</p>
    <a href="http://stubborngastropod.m/{% url"app:detail' article.pk %}">
        <p>글 제목: {{ article.title }}</p>
    </a>
    <p>글 내용: {{ article.content }}</p>
    <hr>
{% endfor %}
{% endblock content %}
  • new.html 만들기
path('new/', views.new, name="new")
def new(request):
    return render(request, 'app/new.html')
{% extends 'base.html' %}

{% block content %}
    <h1>NEW</h1>
    <form action="app:create" method='GET'>
        <label for="title">제목:</label>
        <input type="text" name="title"><br>
        <label for="content">내용:</label>
        <textarea type="text" name="content"></textarea><br>
        <input type="submit">
    </form>
    <hr>
    <a href="http://stubborngastropod.m/{% url"app:index' %}">뒤로가기</a>
{% endblock content %}
  • new.html로 이동할 수 있는 태그
<a href="http://stubborngastropod.m/{% url"app:new' %}">새 글 작성</a>
  • 만들다
path('create/', views.create, name="create"),
from django.shortcuts import render, redirect

def create(request):
    title = request.GET.get('title')
    content = request.GET.get('content')
    
    article = Article(title=title, content=content)
    article.save()
    
    return redirect('app:detail', article.pk)
  • POST 방식 및 csrf

new.html에서 양식 방법을 POST로 변경(csrf 토큰 사용)

{% extends 'base.html' %}

{% block content %}
    <h1>NEW</h1>
		<form action="http://stubborngastropod.m/{% url"app:create' %}" method='POST'>
        {% csrf_token %}
        <label for="title">제목:</label>
        <input type="text" name="title"><br>
        <label for="content">내용:</label>
        <textarea type="text" name="content"></textarea><br>
        <input type="submit">
    </form>
    <hr>
    <a href="http://stubborngastropod.m/{% url"app:index' %}">뒤로가기</a>
{% endblock content %}

create 함수에서 요청을 받는 메서드 변경

def create(request):
    title = request.POST.get('title')
    content = request.POST.get('content')
    
    article = Article(title=title, content=content)
    article.save()

    return redirect('app:detail', article.pk)
  • 끄다

url, 보기 설정, POST 메서드를 사용하여 detail.html에 양식 태그 작성

path('<int:pk>/delete/', views.delete, name="delete"),
def delete(request, pk):
    article = Article.objects.get(pk=pk)
    article.delete()
    return redirect('app:index')
{% extends 'base.html' %}

{% block content %}
<h1>DETAIL</h1>
<h3> {{ app.pk }} 번째 글</h3>
<hr>
<p>제목: {{ app.title }}</p>
<p>내용: {{ app.content }}</p>
<p>작성: {{ app.created_at }}</p>
<p>수정: {{ app.updated_at }}</p>
<hr>
<form action="http://stubborngastropod.m/{% url"app:delete' app.pk %}" method='POST'>
    {% csrf_token %}
    <input type="submit" value="삭제">
</form>
<a href="http://stubborngastropod.m/{% url"app:index' %}">뒤로 가기</a>
{% endblock content %}
  • 업데이트하려면
path('<int:pk>/edit/', views.edit, name="edit"),
def edit(request, pk):
    article = Article.objects.get(pk=pk)
    context = {
        'article': article,
    }
    return render(request, 'app/edit.html', context)

edit.html

{% extends 'base.html' %}

{% block content %}
    <h1>EDIT</h1>
    <form action="#" method='POST'>
        {% csrf_token %}
        <label for="title">제목:</label>
        <input type="text" name="title" value="{{article.title}}"><br>
        <label for="content">내용:</label>
        <textarea type="text" name="content">{{article.content}}</textarea><br>
        <input type="submit">
    </form>
		<hr>
    <a href="http://stubborngastropod.m/{% url"app:index' %}">뒤로가기</a>
{% endblock content %}

편집 페이지로 이동하기 위해 detail.html에 태그를 생성합니다.

<a href="http://stubborngastropod.m/{% url"app:edit' app.pk %}">수정</a>

업데이트 로직 생성

path('<int:pk>/update/', views.update, name="update"),
def update(request, pk):
    article = Article.objects.get(pk=pk)
    article.title = request.POST.get('title')
    article.content = request.POST.get('content')
    article.save()
    return redirect('app:detail', article.pk)
{% extends 'base.html' %}

{% block content %}
    <h1>EDIT</h1>
    <form action="http://stubborngastropod.m/{% url"app:update' article.pk %}" method='POST'>
        {% csrf_token %}
        <label for="title">제목:</label>
        <input type="text" name="title" value="{{article.title}}"><br>
        <label for="content">내용:</label>
        <textarea type="text" name="content">{{article.content}}</textarea><br>
        <input type="submit">
    </form>
    <hr>
    <a href="http://stubborngastropod.m/{% url"app:index' %}">뒤로가기</a>
{% endblock content %}