STATIC_URL = 'static/'
STATIC_DIR = BASE_DIR / 'static' # 명시적으로 정의한 거임,
# 안쓰고 STATICFILES_DIR 에 한번에 적어도 됨
# STATIC_JS_DIR = BASE_DIR /'js' 이런식으로도 있을 수도 있음
# 개발환경에 따라 여러가지 경로들을 같이 넣을 수 있게 만들어져 잇음
# 결국 나눠져있는 static들을 한곳에 넣어서 배포하기 위해 files에 넣고 root로 보내는 거임
STATICFILES_DIRS =[ # 장고 개발시 스태틱 파일들을 여러곳으로 모아 놓을수 있다.
STATIC_DIR,
]
# 배포 환경에서 여러가지 경로들을 한곳으로 모아서 관리할 수 있게 만들어진 경로
STATIC_ROOT = BASE_DIR / '.static_root' # 나눠져있는 STATIC DIR 들 배포하시 한곳에 모아서 사용하기에 나눠져 있는거임
# 장고는 static 을 만들고 settings에 기본적으로 static을 등록해야함
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>블로그 프로젝트</title
<!-- settings에 staitc 경로를 지정해 주었기에 바로 그다음 경로부터 입력해 주었다. -->
<link href="{% static 'css/bootstrap.css' %}" rel = stylesheet>
<!-- 젤밑에 -->
{% block content %}{% endblock %} <!-- 컨텐트라는 구멍을 뚫어놓음 -->
<footer>
</footer>
<script src = "{% static 'js/bootstrap.bundle.js' %}">
</script>
{% block js %}{% endblock %}
따로 앱을 만들어 제작해도 되지만 댓글은 blog 기능 안에 만드는것이 합리적이라 안에 작성
# utils/models.py
from django.db import models
class TimestampModel(models.Model): # 이 안에 테이블로 만드는것 해줫기에 상속시키면 또 상속 안시켜도 됨
created_at = models.DateTimeField('작성일자', auto_now_add=True)
updated_at = models.DateTimeField('수정일자', auto_now=True)
class Meta:
abstract = True # 상속을 할거고 실제로 db에서 관리할 것이 아니기에 이것은 absract 하다고 넣어준 것임
```python
from utils.models import TimestampModel
class Comment(TimestampModel):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE) # 블로그가없으면 댓글도 필요없으니 CASCADE
content = models.CharField('본문', max_length=255)
author = models.ForeignKey(User, on_delete=models.CASCADE) # author 니까 user 연결
# Admin 페이지에서 객체목록을 볼떄 사용
# 디버깅시 print(comment) 또는 str(comment)를 호출하면 나온다.
def __str__(self):
return f'{self.blog.title} 댓글'
class Meta:
verbose_name='댓글'
verbose_name_plural = '댓글 목록'
admin.site.register(Comment)
# 블로그 안에서 이것을 수정할거기야 Inline
class CommentInline(admin.TabularInline): # 표로 만든 형태
model = Comment # 모델을 불러오고
fields = ['content', 'author'] # 수정할 필드
@admin.register(Blog)
class BlogAdmin(admin.ModelAdmin):
inlines = [ # 블로그를 수정시 블로그안에서 보고 수정하는것이 좀더 보기 좋아서 inline을 써서 하였다.
CommentInline
]
8. 댓글 입력 후 db에 들어가나 확인
- blog 페이지의 아래에 댓글이 존재하게 설정
- comment form을 생성해 DetailView 에서 렌더링
from django import forms
from blog.models import Blog, Comment
class CommentForm(forms.ModelForm):
class Meta:
model =Comment
fields = ('content',)
class BlogDetailView(DetailView):
model = Blog
template_name = 'blog_detail.html'
# context 가 딕셔너리 형태로 저장되어 있기에 **kwargs
def get_context_data(self, **kwargs): # DetailView 내장함수
context = super().get_context_data(**kwargs) # 기존의 데이터 가져오기
context['comment_form'] = CommentForm() # 컨텍스트에 키 벨류 추가
return context # 저장
<div style="text-align: right">
{{ blog.author.username }}
</div>
<p>{{ blog.content }}</p>
<hr>
<form method="post">
{{ comment_form.as_p }}
</form>
<hr>
<a class="btn btn-sm btn-info" href ="{% url 'blog:list' %}">목록</a>
{% endblock %}
blog/forms.py
class CommentForm(forms.ModelForm):
class Meta:
model =Comment
fields = ('content',)
widgets ={ # 안넣으면 기본값 실제 화면 그려줄대 어떻게 나타낼지
'content' : forms.TextInput(attrs={'class': 'form-control'}) # 컨텐트를 수정할꺼, html 에서 타입은 text 인 인풋창이 생기게 할거야
# attrs attribute 는 form-control 이야
}
labels = { # 원래는 모델에서 정해준 본문 이라 나오지만 이 내용을 폼에서 보여줄때 댓글, 이런식으로 보여줘 한것
'content' : '댓글'
}
{% if request.user.is_authenticated %}
<form method="post">
{% csrf_token %}
{{ comment_form.as_p }}
<div class="text-end">
<button class="btn btn-primary">작성</button>
</div>
</form>
<hr>
<a class="btn btn-sm btn-info" href ="{% url 'blog:list' %}">목록</a>
{% endif %}
def post(self, *args, **kwargs):
# 사용자가 보내온 post요청이 commentForm 에 맞춰 comment_form에 등록
comment_form = CommentForm(self.request.POST)
if not comment_form.is_valid():
self.object = self.get_object() # 해당 객체 받아오고 그대로 유지 폼이 타당하지 않기에
context = self.get_context_data(object = self.object)
context['comment_form'] = commment_form
# 유효하지 않는 form을 담는 이유는 이형태를 다시 그상태 그대로 클라이언트에게 보내기 위해서, 어떤 것을 잘못보낸지 가르쳐주기 위해서
return self.render_to_response(context)
# 유효하지 않은 데이터를 개발자가 아닌 응답페이지로 보내는 것임.
if not self.request.user.is_authenticated: # 혹시 잘못된 요청이 들어올떄 서버에서 한번더 먹는거
raise Http404
# 유효한 상태 저장
comment = comment_form.save(commit=False)
# comment.blog = self.get_object() 이렇게 해도되고 아래로 해도되고
comment.blog_id = self.kwargs['pk'] # url 에서 가져오는 것
comment.author = self.request.user
comment.save()
# 클래스에선 HttpResponseReriect 써야함
# 왜이렇게 쓰냐 현재 urls에 <int:pk> 라고 써놨음 그러면 blog:detail의 경로에서 저 값을 얻고 그것을 'blog:detail': pk값 이렇게 저장한다. 딕셔너리 형태로 받아와져서 kwargs='pk':self.kwargs['pk']})) 가 되는것이다.
# path 경로가 <int:blog_pk>로 저장되어 있으면 kwargs={'blog_pk':self.kwargs['blog_pk']} 가 맞다.
return HttpResponseRedirect(reverse_lazy('blog:detail', kwargs={'pk':self.kwargs['pk']}))
class BlogDetailView(DetailView):
model = Blog
queryset = Blog.objects.all().prefetch_related('comment_set','comment_set__author')
class TodoDetailView(DetailView):
model = Todo
template_name = 'todo_detail.html'
def get_context_data(self, **kwargs):
# 기존의 context 가져오기
context = super().get_context_data(**kwargs)
# 현재의 Todo 객체
todo = self.get_object()
# comments를 context에 추가
context['comments'] = todo.comments.all() # related_name='comments' 덕분에 접근 가능
return context
queryset을 설정하면 기본적으로 다음과 같은 데이터가 컨텍스트에 포함됩니다:
1. todo (또는 object): 해당 Todo 객체.
• self.object로 접근 가능.
2. todo.comment_set.all:
• Todo 객체와 연결된 모든 댓글을 미리 가져와 사용 가능.
3. comment.author:
• 각 댓글의 작성자 정보도 미리 가져와 사용 가능.
lass CommentCreateView(LoginRequiredMixin, CreateView):
model = Comment
form_class = CommentForm
def get(self, *args, **kwargs):
raise Http404
def form_valid(self, form):
blog = self.get_blog()
self.object = form.save(comm it=False)
self.object.author = self.request.user
self.object.blog = self.get_blog()
self.object.save()
return HttpResponseRedirect(reverse('blog:detail', kwargs={'pk':blog.pk}))
def get_blog(self):
pk = self.kwargs['blog_pk'] # comment pk 랑 헷갈릴수 잇기에
blog = get_object_or_404(Blog, pk=pk)
return blog
# /comment/create/<int:blog_pk>/
#blog/urls.py
path('comment/create/<int:blog_pk>', cb_views.CommentCreateView.as_view(), name='comment_create'),
]
def get(self, *args, **kwargs):
raise Http404
<form method="post" action="{% url 'blog:comment_create' blog.pk %}">
{% csrf_token %}
{{ comment_form.as_p }}
<div class="text-end">
<button class="btn btn-primary">작성</button>
</div>
</form>
<hr>
class BlogDetail(ListView):
model = Comment
template_name = 'blog_detail.html'
# listview이기에 paginate 사용가능
paginate_by = 10
def get(self, request, *args, **kwargs):
# url에 전달된 blog_pk를 받아 Blog에서 모델에서 해당블로그 조회 그 객체를 self.object 에 저장
self.object = get_object_or_404(Blog, pk=kwargs.get('blog_pk'))
# 부모 클래스인 ListView의 get 메서드를 호출하여, 기본 동작(HTML 렌더링 및 get_queryset 호출)을 수행합니다.
return super().get(request,*args,**kwargs)
# prefetch_realted 를 사용하여 DB에서 쿼리를 한번만 일으킨다.
# html에서 사용해서 commmet.all() 을 사용하면 DB에 쿼리를 한번더 주는거라 부하가 심해진다
# 그래서 한번에 가져올떄 prefetch.related 를 사용해서 가져와서 db에 부하를 덜준다.
def get_queryset(self):
return self.model.objects.filter(blog=self.object).prefetch_related('author')
# 사용할 context 추가.
def get_context_data(self, **kwargs):
context= super().get_context_data(**kwargs)
context['commnet_form'] = CommentForm()
context['blog'] = self.object
return context
html 똑같은 부분이 있으면 html 파일 하나 만들어서 넣어놓고 그 파일안의 내용을 쓰려면
{% include '해당파일.html' %} 이렇게 사용 가능
shell_plus 를 통한 댓글 갯수 늘리기
In [1]: todo_list = Todo.objects.all()
In [2]: todo_list
Out[2]: <QuerySet [<Todo: 생성>, <Todo: cbv 새러운생성>, <Todo: cbvv생성>, <Todo: 생성2>]>
In [3]: new_list = []
In [4]: for i in range(40):
...: for todo in todo_list:
...: todo.id = None
...: new_list.append(todo)
...:
In [5]: Todo.objects.bulk_create(new_list)
This Django blog project walkthrough by Haks is a fantastic Sprunki Pyramixed resource for anyone looking to dive into building a comment system with Django!