본문 바로가기

LIKELION 9th

[멋쟁이 사자처럼] Django로 블로그 만들기(CRUD 기능 구현하기)

지난 이야기🌿

6주 차에는 장고(Django)를 시작했다.

 

 

7주 차(2021.05.10-2021.05.13)

7주 차에는 지난 세션에 이어서 장고에 대해 배웠다. blog라는 이름의 프로젝트를 만들고 CRUD 기능을 구현했다.

* CRUD는 대부분의 컴퓨터 소프트웨어가 가지는 기본적인 데이터 처리 기능인 Create(생성), Read(읽기), Update(갱신), Delete(삭제)를 묶어서 일컫는 말이다.(출처 위키백과)

 

장고로 블로그 만들기

  • 제목과 내용을 넣을 수 있는 글쓰기 기능 구현
  • 글을 쓰면 제목과 내용, 작성일로 글 생성
  • 글 수정 및 삭제 기능 구현

 

1. 장고 프로젝트와 앱 생성하기

1) 가상환경 만들고 프로젝트와 앱 생성하기

$ python -m venv myven	#가상환경 생성
$ . myvenv/Scripts/activate	#가상환경 실행
$ pip install django		#장고 설치
$ django-admin startproject blog	#'blog' 프로젝트 생성
$ cd blog	#'blog' 프로젝트로 이동
$ python manage.py startapp main #'main' 앱 생성
$ python manage.py runserver #http://127.0.0.1:8000/ 웹 서버 실행

로켓 등장🚀

 

2) 프로젝트 내 settings.py에 만든 앱 등록하기

settings.py에 앱 등록

3) 앱 내 templates 폴더 만들기

* 자세한 내용은 지난 글 참고

 

2. models.py 작성하기

블로그의 글에는 제목, 내용, 작성일 등의 내용이 들어간다. 장고 안의 모델을 사용해 이 내용을 데이터베이스에 저장해보자.

 

지난 포스팅의 그림 다시보기❗

 

1) 앱 내 models.py 작성하기

#models.py
from django.db import models

class Write(models.Model):
    title = models.CharField(max_length=50)	#글자 수가 제한된 텍스트 정의(짧은 문자열)
    contents = models.TextField()	#글자 수 제한이 없는 긴 텍스트 정의
    updated_at = models.DateTimeField(auto_now=True)	#날짜와 시간 정의
  • Write 모델을 만들었다(클래스 이름의 첫 글자는 대문자로 써야 한다).
  • models는 Write가 장고 모델임을 의미한다.

* auto_now_add=True로 하면 최초 글 작성 시간을, auto_now=Ture로 하면 글이 수정될 때마다의 시간을 반영한다. 

 

2) 데이터베이스에 등록하기

$ python manage.py makemigrations
$ python manage.py migrate

 

3) admin.py 작성하기

models.py에서 만든 class Write를 admin.py에 등록하자.

#admin.py
from django.contrib import admin
from .models import Write

# Register your models here.
admin.site.register(Write)

 

4) http://127.0.0.1:8000/admin 에서 Write 객체 확인하기

  • 장고에서 기본으로 제공하는 admin 페이지에서 객체를 확인해 보자.
  • 페이지에 접속하기 위해서는 superuser 계정이 필요하다.
$ python manage.py createsuperuser

 

superuser 계정으로 페이지에 접속하면 아래와 같이 Write 객체를 확인하고 관리할 수 있다.

 

/admin

3. forms.py 작성하기

admin 권한이 없는 일반 사용자가 데이터를 생성, 수정, 삭제할 수 있게 해보자. 장고에서는 form을 통해 models.py 구조에 해당하는 데이터를 입력받을 수 있다.

* 앱 내 forms.py 파일을 직접 만들었다.

 

#forms.py
from django import forms
from .models import Write

class WriteForm(forms.ModelForm):
    class Meta:
        model = Write
        fields = '__all__'

 

  • Meta 클래스는 어떤 모델을 사용할지, 해당 모델에서 어떤 필드를 사용할지 정의한다.
  • fields 값을 '__all__'로 사용하면 모델의 모든 필드("title", "contents", "updated_at")를 가져온다.

 

4. urls.py - views.py - templates

데이터 준비를 마쳤으니 이제 웹 페이지에 보이게 해보자.

 

1) urls.py

 

from django.contrib import admin
from django.urls import path
from main import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.index, name='index'),
    path('create/', views.create, name='create'),
    path('detail/<int:write_id>', views.detail, name='detail'),
    path('update/<int:write_id>', views.update, name='update'),
    path('delete/<int:write_id>', views.delete, name='delete'),
]

 

 

2) views.py

from django.shortcuts import get_object_or_404, redirect, render
from .forms import WriteForm
from .models import Write
# Create your views here.

def index(request):
    all_write = Write.objects.all()
    return render(request, 'index.html', {'all_write':all_write})

def create(request):
    if request.method == "POST":
        create_form = WriteForm(request.POST)
        if create_form.is_valid():
            create_form.save()
        return redirect('index')
    else:
        create_form = WriteForm()

    return render(request, 'create.html', {'create_form':create_form})

def detail(request, write_id):
    my_write = get_object_or_404(Write, pk=write_id)
    return render(request, 'detail.html', {'my_write':my_write})

def update(request, write_id):
    my_write = get_object_or_404(Write, id=write_id)
    if request.method == "POST":
        update_form = WriteForm(request.POST, instance=my_write)
        if update_form.is_valid:
            update_form.save()
            return redirect('index')
    update_form = WriteForm(instance=my_write)
    return render(request, 'update.html', {'update_form':update_form})

def delete(request, write_id):
    my_write = get_object_or_404(Write, id=write_id)
    my_write.delete()
    return redirect('index')

 

3) index.html

#views.py
(···)
def index(request):
    all_write = Write.objects.all()
    return render(request, 'index.html', {'all_write':all_write})
(···)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>Donghae Blog</h1>
    
    <div class="create_write">
        <a href="{% url 'create'%}"><div class="create_btn">🙋‍♂️글쓰기🙋‍♂️</div></a>
    </div>

    {% for write in all_write %}
    <div class="all_write">
        <a href = "{% url 'detail' write.id %}">
        <div>{{write.id}}</div><br>
        <div>{{write.title}}</div><br>
        <div>{{write.contents}}</div><br>
        <div>{{write.updated_at|date:"Y.m.d"}}</div><br>
        <a>
    </div>
    {% endfor %}

</body>
</html>

 

  • 글쓰기 기능을 위해 버튼을 생성하고 클릭하면 url 'create'로 넘어가도록 설정
  • Write 객체를 담은 all_write 변수를 for문을 사용하여 출력
  • all_write 변수, 즉 게시물 내용을 클릭하면 url 'detail'로 넘어가도록 설정

 

4) create.html

#views.py
(···)
def create(request):
    if request.method == "POST":
        create_form = WriteForm(request.POST)
        if create_form.is_valid():
            create_form.save()
        return redirect('index')
    else:
        create_form = WriteForm()

    return render(request, 'create.html', {'create_form':create_form})
(···) 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <form method="POST", action="">
        {% csrf_token %}
        {{create_form.as_p}}
        <input type="submit" value="제출">
    <form>
</body>
</html>

 

  • "POST"요청에 대해 장고에서 기본으로 제공하는 보안 수단인 {% csrf_token %} 태그 사용
  • {{create_form.as_p}}를 사용해 각 필드를 p태그 안에서 배치 

* CSRF 공격(Cross Site Request Forgery): 웹사이트 취약점 공격의 하나로 사용자의 의지와는 무관하게 수정, 삭제, 등록 등을 웹사이트에 요청하게 하는 공격

 

5) detail.html

#views.py
(···)
def detail(request, write_id):
    my_write = get_object_or_404(Write, pk=write_id)
    return render(request, 'detail.html', {'my_write':my_write})
(···)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    {{my_write.id}}번째 글
    <br>제목: {{my_write.title}}
    <br>내용: {{my_write.contents}}
    <br><a href="{% url 'update' my_write.id %}"><button>수정</button><a>
    <a href="{% url 'delete' my_write.id %}"><button>삭제</button><a>
</body>
</html>

 

  • detail 메서드에서 id값(int)에 해당하는 객체가 있으면 이를 my_write 변수로 가져오고, 없으면 404 에러 띄우기.
  • 수정(update) 및 삭제(delete) 기능을 구현하기 위해 버튼 생성

* pk is the attribute that contains the value of the primary key for the model.
* id is the name of the field created as a primary key by default if none is explicitly specified.

 

6) update.html

#views.py
(···)
def update(request, write_id):
    my_write = get_object_or_404(Write, id=write_id)
    if request.method == "POST":
        update_form = WriteForm(request.POST, instance=my_write)
        if update_form.is_valid:
            update_form.save()
            return redirect('index')
    update_form = WriteForm(instance=my_write)
    return render(request, 'update.html', {'update_form':update_form})
(···)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <form method="POST", action="">
        {% csrf_token %}
        {{update_form.as_p}}
        <button type="submit" value="제출">제출</button>
    <form>
</body>
</html>

 

  • update 메서드는 "POST"요청에 대해 id값에 해당하는 인스턴스를 받아서 내용을 불러온다.
  • 변수 이름을 제외하면 그 외의 내용은 create와 동일하다.

 

이제 웹 페이지를 열어서 기능을 확인해보자!

index.html
create.html

 

글 작성 후 index.html

* 네 번째 쓰는 글이라 id값으로 4표시

 

detail.html
update.html
수정 후 index.html
글 삭제 후 index.html

 

디자인은 엉망진창이지만 그래도 기능은 구현 완료😅👍

views.py에 메서드마다 변수 이름이 달라서 처음에는 엄청 헷갈렸는데 세 번째 보니까 좀 익숙해졌다,,,

 

이제 css 파일 만들러 가자🤸‍♀️

 


참고자료

 

Django: 데이터 관계

1. PK & ID > is the attribute that contains the value of the primary key for the model. is the name of the field created as a primary key by default

velog.io

 

[Django Basic 09] {% csrf_token %}

1. CSRF(Cross Site Request Forgery) : 웹사이트 취약점 공격의 하나로, 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위(수정, 삭제, 등록 등) 를 특정 웹사이트에 요청하게 하는 공격 이미 사용

chagokx2.tistory.com

 

Django — CRUD 기능 구현

쌩 Django로 기본 CRUD 기능 구현하기

asaprocky123.medium.com

 

Python Django 강좌 : 제 7강 - Serializers | 076923

Django Serializers

076923.github.io

 

Model field reference | Django documentation | Django

Django The web framework for perfectionists with deadlines. Overview Download Documentation News Community Code Issues About ♥ Donate

docs.djangoproject.com