728x90
python manage.py shell
from posts.forms import CommentForm
data = {"content": "SampleContent"}
form = CommentForm(data=data)
form.is_valid()
form.errors
from users.models import User
from posts.models import Post
user = User.objects.all()[0]
post = Post.objects.all()[0]
data = {"content": "SampleContent", "user": user, "post": post}
form = CommentForm(data=data)
form.is_valid()
comment = form.save()
comment.id
|
- Comment를 생성하기 위해 필요한 데이터
- 어떤 글(Post)의 댓글인지
- 어떤 사용자(User)의 댓글인지
어떤 내용(Comment)을 가지고 있는지
2.1.2 View에서 Template으로 Form 전달
- posts/views.py
from posts.forms import CommentForm
def feeds(request):
...
posts = Post.objects.all()
comment_form = CommentForm()
context = {
"posts": posts,
"comment_form": comment_form,
}
return render(request, "posts/feeds.html", context)
|
- templates/posts/feeds.html
- 직접 작성했던 input 요소를 삭제하고 comment_form.as_p 변수 사용
<div class="post-comments-create">
<form method="POST">
{% csrf_token %}
{{ comment_form.as_p }}
<button type="submit">게시</button>
</form>
</div>
|
- templates/posts/feeds.html
- CommentForm에는 post, content 필드가 있고 이 둘을 as_p로 렌더링한 경우
- 포스트의 드롭다운 요소를 클릭하면 Post 객체를 선택할 수 있음. 사용자가 어떤 글에 댓글을 다는지는 직접 입력할 필요 없이 템플릿에서 알아서 처리해 주어야 함
- 자동으로 < label>요소와 < input>요소가 만들어짐. 여기서는 "내용:"으로 나타나는 < label>요소가 필요하지 않음. content 값을 입력받을 < input> 요소만 있으면 됨
- CommentForm에는 post, content 필드가 있고 이 둘을 as_p로 렌더링한 경우
<div class="post-comments-create">
<form method="POST">
{% csrf_token %}
{{ comment_form.content }}
<button type="submit">게시</button>
</form>
</div>
|
- posts/forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = [ ... ]
widgets = {
"content": forms.Textarea(
attrs={
"placeholder": "댓글 달기...",
}
)
}
|
2.1.3 댓글 작성 처리를 위한 View 구현
- posts/views.py
from django.views.decorators.http import require_POST
def feeds(request):
...
@require_POST
def comment_add(request):
print(request.POST)
|
- posts/urls.py
from django.urls import path
from posts.views import feeds, comment_add
...
urlpatterns = [
path("feeds/", feeds),
path("comment_add/", comment_add),
]
|
2.1.4 form에서 comment_add View로 데이터 전달 및 처리
- templates/posts/feeds.html
- form의 action 속성
- method: GET과 POST 중 어떤 방식으로 데이터를 전달할지
- enctype: 기본값(application/x-www-form-urlencoded)과 파일 전송을 위한 값(multipart/form-data) 중 선택
- form의 action 속성
<div class="post-comments-create">
<form method="POST" action="/posts/comment_add/">
{% csrf_token %}
{{ comment_form.content }}
<button type="submit">게시</button>
</form>
</div>
|
- templates/posts/feeds.html
- 사용자가 직접 입력하지 않는 고정된 데이터를 form 내부에 위치
<div class="post-comments-create">
<form method="POST" action="/posts/comment_add/">
{% csrf_token %}
<input type="hidden" name="post" value="{{ post.id }}">
{{ comment_form.content }}
<button type="submit">게시</button>
</form>
</div>
|
- posts/views.py
- 사용자 정보를 View에서 직접 할당
@require_POST
def comment_add(request):
form = CommentForm(data=request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.user = request.user
comment.save()
print(comment.id)
print(comment.content)
print(comment.user)
return redirect{"/posts/feeds/"}
|
- templates/posts/feeds.html
- 작성 완료 후 원하는 Post 위치로 이동
<div id="feeds" class="post-container">
{% for post in posts %}
<article id="post-{{ post.id }}" class="post">
|
- posts/views.py
from django.http import HttpResponseRedirect
@require_POST
def comment_add(request):
form = CommentForm(data=request.POST)
if form.is_valid():
comment = form.save(commit=False)
...
return HttpResponseRedirect{f"/posts/feeds/#post-{comment.post.id}"}
|
2.1.5 글의 댓글 수 표시
- Terminal
python manage.py shell
from posts.models import Post
for post in Post.objects.all():
print(f"id: {post.id}, comment_count: {post.comment_set.count()}")
|
- templates/posts/feeds.html
<div class="post-buttons">
<button type="submit">Likes(0)</button>
<span>Comments({{ post.comment_set.count }})</span>
</div>
|
2.1.6 댓글 삭제
- posts/views.py
from posts.models import Post, Comment
@require_POST
def comment_add(request):
...
@require_POST
def comment_delete(request, comment_id):
if request.method == "POST":
comment = Comment.objects.get(id=comment_id)
comment.delete()
return HttpResponseRedirect(f"/posts/feeds/#post-{comment.post.id}")
|
- posts/urls.py
from django.urls import path
from posts.views import feeds, comment_add, comment_delete
...
urlpatterns = [
path("feeds/", feeds),
path("comment_add/", comment_add),
path("comment_delete/<int:comment_id>/", comment_delete)
]
|
- posts/views.py
- 삭제할 Comment가 요청한 사용자가 작성한 것인지 확인
from django.http import HttpResponseRedirect, HttpResponseForbidden
@require_POST
def comment_delete(request, comment_id):
comment = Comment.objects.get(id=comment_id)
if comment.user == request.user:
comment.delete()
return HttpResponseRedirect(f"/posts/feeds/#post-{comment.post.id}")
else:
return HttpResponseForbidden("이 댓글을 삭제할 권한이 없습니다.")
|
- templates/posts/feeds.html
- 템플릿에 삭제 버튼 추가
<div class="post-comments">
<ul>
{% for comment in post.comment_set.all %}
<li>
<span>{{ comment.user.username }}</span>
<span>{{ comment.content }}</span>
<!-- 댓글 삭제 form 추가 -->
{% if user == comment.user %}
<form method="POST" action="/posts/comment_delete/{{ comment.id }}/">
{% csrf_token %}
<button type="submit">삭제</button>
</form>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
|
2.2 글 작성하기
2.2.1 글 작성 기본구조 구현
- View: /posts/post_add/
- URL: post_add
- Template: templates/posts/post_add.html
- posts/views.py
...
def post_add(request):
return render(request, "posts/post_add.html")
|
- posts/urls.py
...
from posts.views import feeds, comment_add, comment_delete, post_add
...
urlpatterns = [
...
path("post_add/", post_add),
]
|
- templates/posts/post_add.html
{% externds 'base.html' %}
{% block content %}
<div id="post-add">
<h1>Post Add</h1>
</div>
{% endblock %}
|
2.2.2 PostForm 클래스 구현
- posts/forms.py
from posts.models import Comment, Post
...
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = [
"content",
]
|
2.2.3 View 로직, Template 구현
- posts/views.py
from posts.forms import CommentForm, PostForm
...
def post_add(request):
form = PostForm()
context = {"form": form}
return render(request, "posts/post_add.html", context)
|
- templates/posts/post_add.html
{% externds 'base.html' %}
{% block content %}
<nav>
<h1>Pystagram</h1>
</nav>
<div id="post-add">
<h1>Post Add</h1>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">게시</button>
</form>
</div>
{% endblock %}
|
2.2.4 여러 장의 이미지 업로드
- templates/posts/post_add.html
- Template에 직접 < input type="file"> 구성
...
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div>
<labl for="id_images">이미지</label>
<input id="id_images" name="images" type="file" multiple>
</div>
{{ form.as_p }}
<button type="submit">게시</button>
</form>
...
|
- posts/views.py
- View에서 multiple 속성을 가진 file input의 데이터 받기
from posts.models import Post, Comment, PostImage
...
def post_add(request):
if request.method == "POST":
# request.POST로 온 데이터 ("content")는 PostForm으로 처리
form = PostForm(request.POST)
if form.is_valid():
# Post의 "user"값은 request에서 가져와 자동할당한다
post = form.save(commit=False)
post.user = request.user
post.save()
# Post를 생성 한 후
# request.FILES.getlist("images")로 전송된 이미지들을 순회하며 PostImage객체를 생성한다
for image_file in request.FILES.getlist("images"):
# request.FILES또는 request.FILES.getlist()로 가져온 파일은
# Model의 ImageField부분에 곧바로 할당한다
PostImage.objects.create(
post=post,
photo=image_file,
)
# 모든 PostImage와 Post의 생성이 완료되면
# 피드페이지로 이동하여 생성된 Post의 위치로 스크롤되도록 한다
url = reverse("posts:feeds") + f"#post-{post.id}"
return HttpResponseRedirect(url)
# GET요청일 때는 빈 form을 보여주도록한다
else:
form = PostForm()
context = {"form": form}
return render(request, "posts/post_add.html", context)
|
2.2.5 내비게이션 바에 링크 추가
- templates/posts/feeds.html
- 피드 페이지에서 글 작성 페이지로의 작성 추가
<nav>
<h1>
<a href="/posts/feeds/">Pystagram</a>
</h1>
<a href="/posts/post_add/">Add post</a>
<a href="/users/logout/">Logout</a>
</nav>
|
- templates/posts/post_add.html
- 글 작성 페이지에서 피드 페이지로 돌아오는 링크 추가
<nav>
<h1>
<a href="/posts/feeds/">Pystagram</a>
</h1>
<a href="/users/logout/">Logout</a>
</nav>
|
728x90
'PYTHON-BACK' 카테고리의 다른 글
#파이썬 34일차_이커머스 클론코딩5_(1) (0) | 2024.08.22 |
---|---|
#파이썬 34일차_이커머스 클론코딩4_(2) (0) | 2024.08.21 |
#파이썬 33일차_이커머스 클론코딩3 (0) | 2024.08.20 |
#파이썬 32일차_이커머스 클론코딩2 (0) | 2024.08.19 |
#파이썬 31일차_이커머스 클론코딩1 (0) | 2024.08.16 |