테킷스쿨에는 멋쟁이사자처럼 [대학11기] 파이썬 Django 실습 강의가 있습니다.
멋쟁이사자처럼 대학 동아리에 소속되어 활동을 시작하며, 이 강의를 수강할 수 있게 되었습니다.
이번에 리뷰하려는 것은 ✨챕터 5 : 백엔드의 필수: CRUD 개발하기 ✨ 입니다.
create, read, update, delete의 약자로, 소프트웨어가 가지는 기본적인 데이터 처리 기능이다.
웹 서비스의 기반이 되는 개념이다.
추가, 조회, 수정, 삭제로 한글로 부른다. 상황에 따라 고려해야 할 점들이 각각 다르다
삭제는 soft/hard 두 가지가 있다. 삭제했다고 정리하고 실제 완전히 삭제하지 않는 것을 soft delete이라고 한다.
클라이언트는 입력을 주로 하며, 서버에게 요청한다.
서버는 검증 및 연상을 주로 하며, 데이터베이스에게 요청한다.
데이터베이스는 처리(crud)를 하고, 다시 서버에게 이를 전송한다.
서버가 없어도 클라이언트와 데이터베이스 만 있어도 작동은 되지만, 서버가 있어야 보안을 강화하는 효과 등이 있다.
데이터베이스로의 데이터 요청과 응답이 기본적인 crud의 흐름이다.
데이터의 특정 테이블의 생성을 create이라고 한다.
create의 흐름 :
폼 요청 -> 폼 응답 -> 데이터 생성 요청 -> 데이터베이스의 데이터 응답
사용자에게 입력받는 데이터
시스템에서 생성하는 데이터
두 가지로 구성되어있다.
인증권한; 로그인이 필요하거나 관리지만 생성할 수 있게 한다
데이터 유효성; 클라이언트가 입력한 데이터가 유효한지 검증한다. (나이, 전화번호, 숫자만 들어오도록 만든다 등)
이전까지 작업하던 파일에 들어가서
페이지 소스를 확인하면, 새로고침을 했을 때 네트워크에 다양한 경로로 내용을 가져온다는 것을 확인할 수 있다.
여기에서 {% ~~ %} 내용을 입력하면, 하드코딩 없이도 변경된 url 주소를 불러올 수 있다.
예를 들어서,
{% url 'posts:post-create' %}
을 입력하면, 설령 경로가 create가 아니더라도 이름이 post-create에 해당하는 내용으로 url이 연결된다.
만약 기존 파일에서, 내가 파일을 브라우져로 가져오면 이를 받아들이는 프로그램을 만들과자 한다면, views.py와 post_form.html 파일을 수정하면 된다.
// posts > views.py
def post_create_view(request):
if request.method == "GET":
return render(request, 'posts/post_form.html/')
else:
image = request.FILES.get('image')
content = request.POST.get('content')
print(image)
print(content)
Post.objects.create(
image=image,
content=content,
)
return redirect('index')
post_create_view 는 내가 앞에서 view로 선언을 해두었기 때문에 꼭 return 값이 있어야 한다.
만약 post_form.html 을 수정하고자 한다면,
// post_form.html
{% extends 'base.html' %}
{% block title %} POST 입력 {% endblock %}
{% block content %}
<h1>POST 입력 화면</h1>
<form action="{% url 'posts:post-create' %}" method="POST" enctype="multipart/form-data"> {% csrf_token %}
<div>
<label for="id_image">이미지</label>
<input type="file" name="image" accept="image/*" id="id_image">
</div>
<div>
<label for="id_content">내용</label>
<textarea name="content" id="id_content" cols="30" rows="10"></textarea>
</div>
<div>
<input type="submit">
</div>
</form>
{% endblock %}
사진을 넣으면, admin에서 해당 변경 내용을 확인할 수 있다.
settings 에 아래 코드를 추가하고
// config > settings.py
MEDIA_URL = 'media/'
MEDIA_ROOT = BASE_DIR / 'media'
config 와 동등한 위치에서 media 폴더를 생성한다.
이제 이미지를 업로드하면 여기에 저장될 것이다..
장고 디버그 툴바를 여기에서 다운받아 사용할 수 있다.
https://django-debug-toolbar.readthedocs.io/en/latest/installation.html
화면 우측에 생긴 바를 통해서 sql 등을 확인할 수 있다.
조금 디테일을 살려서 문서를 수정해보자.
여러 포스트를 모아서 리스트 모양으로 보고 싶다면 post_list_view 함수를 수정해주어야 한다.
def index(request):
post_list = Post.objects.all().order_by('-created_at')
만약 여러 포스트를 최신순으로 정렬하고 싶다면,
def index(request):
post_list = Post.objects.all().order_by('-created_at')
뒤에 order_by를 추가해준다.
그리고 리스트를 조회할 때 댓글에 대해서 알고싶다면post_card.html 파일에서 수정하면 되는데,
이미지가 있을 때, 이를 보이게 하고싶다면 card__body 부분에 아래의 코드를 추가해주면 된다.
<div>
{% if post.image %}
<img class="card__image" src="{{ post.image.url }}" alt=""/>
{% else %}
<img class="card__image" src="http://via.placeholder.com/600x600" alt=""/>
{% endif %}
</div>
댓글에 대한 내용은 아래의 코드를 추가해주면 된다.
<div>
<p class="">댓글 {{ post.comment_set.all.count }}개 {% if not detail %}모두 보기{% endif %}</p>
<ul class="card__comment-group">
{% if not detail %}
{% for comment in post.comment_set.all %}
<li><p><span class="card__user-name">{{ comment.writer }}</span>{{ comment.content }}</p>
</li>
{% empty %}
<li><p>댓글 없음</p></li>
{% endfor %}
{% else %}
{% for comment in post.comment_set.all|slice:":2" %}
<li><p><span class="card__user-name">{{ comment.writer }}</span>{{ comment.content }}</p>
</li>
{% empty %}
<li><p>댓글 없음</p></li>
{% endfor %}
{% endif %}
</ul>
</div>
<span class="card__created-at">{{ post.created_at|date:"Y년 m월 d일" }}</span>
작성자를 나타내는지의 유무와, 댓글이 없을 경우에는 '댓글 없음'을 출력하고 있다면 그 내용을 불러와 출력한다. 또한 시간을 제외한 날짜만 출력하기 위해서 맨 마지막 태그를 추가했다!
포스트에서 게시물 글의 '더보기'를 누르면 상세내용이 나오게 만들고 싶다면, 기존의 post_card.html 파일에서 아래 코드를 추가해준다.
<a href="/posts/{{post.id}}">더보기</a>
저 코드에 있는 post.id는 index.html 파일에서 for 문을 돌리면서 그 내부에 post_card.html 파일을 불러왔기 때문에 가능하다.
만약 더 편하게 url을 불러오고 싶다면 urls.py 에 가서, post_detail_view에 이름을 추가해주면 된다.
path('<int:id>', post_detail_view, name='post-detail'),
그리고 post_card도 수정하면 된다.
{% if not detail %}
<a href="{% url 'posts:post-detail' post.id %}">더보기</a>
{% endif %}
기존에 작성한 게시물의 글 혹은 사진을 수정하고 싶다면, view.py 파일과 post_form.html 파일을 수정하면 된다.
// views.py
def post_update_view(request, id):
post = Post.objects.get(id=id)
if request.method == 'GET':
context = { 'post':post }
return render(request, 'posts/post_form.html/', context)
elif request.method == 'POST':
pass
if 문을 통해서 더 구체적인 코드를 작성할 수 있다.
// post_form.html
<div>
<label for="id_image">이미지</label>
{% if post.image %}
<p>현재 : <a href="{{post.image.url }}">{{ post.image.name }}</a></p>
<p>변경 : </p>
{% else %}
{% endif %}
<input type="file" name="image" accept="image/*" id="id_image">
</div>
<div>
<label for="id_content">내용</label>
<textarea name="content" id="id_content" cols="30" rows="10">{% if post %} {{ post.content }} {% endif %}
</textarea>
</div>
기존에 파일을 수정하면서 이미지를 바꿀 수 있는데, 이 과정에서 기존 파일은 삭제되고 새 파일이 저장되어야 한다.
이를 위해서 views.py 파일을 수정한다.
// posts > views.py
def post_update_view(request, id):
# post = Post.objects.get(id=id)
post = get_object_or_404(Post, id=id)
if request.method == 'GET':
context = { 'post':post }
return render(request, 'posts/post_form.html/', context)
elif request.method == 'POST':
new_image = request.FILES.get('image')
content = request.POST.get('content')
print(new_image)
print(content)
if new_image:
post.image.delete()
post.image = new_image
post.content = content
post.save()
return redirect('posts:post-detail', post.id )
더 안전한 코드 유지를 위해서
post = Post.objects.get(id=id)
대신에
post = get_object_or_404(Post, id=id)
이를 이용할 수 있다.
delete에 두 가지 종류의 삭제가 있는데, 소프트가 아닌 하드 딜리트, 즉 완전 삭제로 파일을 삭제하기 위한 코드를 이번 8과 9에서 살펴보자.
먼저 쉬운 url 연결을 위해서 urls.py에서 post_delete_view에 이름을 붙여주자
path('<int:id>/delete/', post_delete_view, name='post-delete'),
그리고 views.py 에서도 수정을 더해주는데, 만약 포스트를 찾지 못하면 404 에러 화면을 띄우도록 하며, 포스트가 있다면 해당 html파일로 연결하는 if 문과 return 문을 작성한다.
def post_delete_view(request, id):
post = get_object_or_404(Post, id=id)
if request.method == 'GET':
context = { 'post': post }
return render(request, 'posts/post_confirm_delete.html', context)
return render(request, 'posts/post_confirm_delete.html/')
삭제하기 버튼은 일단 임의로 수정하기 버튼 옆에 만들어 준다.
// templates > post_card.html
<!-- 수정하기 버튼 -->
{% if request.user == post.writer %}
<a href="{% url 'posts:post-update' post.id %}"> 수정하기 </a>
<a href="{% url 'posts:post-delete' post.id %}"> 삭제하기 </a>
{% endif %}
가장 중요한 post_confirm_delete.html 파일은 아래와 같다.
// templates > posts > post_confirm_delete.html
{% extends 'base.html' %}
{% block title %} POST 삭제 {% endblock %}
{% block content %}
<h1>POST 삭제 화면</h1>
<form action="" method="POST">
{% csrf_token %}
<p>한 번 삭제된 데이터는 복구가 불가능 합니다. </p>
<p>그래도 삭제 하시겠습니까? ID : {{ post.id }} </p> <br/> <br/>
<a href="{% url 'index' %}">돌아가기</a>
<input type="submit" value="삭제하기">
</form>
<br/>
{% include 'mixin/posts/post_card.html' with detail=True %}
{% endblock %}
실행하면 화면은 아래와 같이 나온다.
위의 코드에 이어서, 정말로 삭제를 하도록 만들기 위해서는 views.py에 post.delete()코드를 작성하는 것이 핵심이다.
// posts > views.py
def post_delete_view(request, id):
post = get_object_or_404(Post, id=id)
if request.method == 'GET':
context = { 'post': post }
return render(request, 'posts/post_confirm_delete.html', context)
else:
post.delete()
return redirect('index')
더 다양한 강의노트 및 강좌 후기 👉🏻 https://blog.naver.com/jimin201396