CBV를 이용해서 모놀로그식 방식으로 동영상 CRUD와 로그인을 구현해보자.
url링크는 다음과 같다.
/tube # 목록
/tube/1 # 영상 재생과
/tube/create/ # 게시물 생성
/tube/update/<int:pk>/ # 게시물 업데이트
/tube/delete/<int:pk>/ # 게시물 삭제
/tube/tag/<str:tag>/ # 해당 태그를 가진 게시물 목록
/tube/?q='keyword' # 키워드 검색 목록
/accounts/signup/ # 가입
/accounts/login/ # 로그인
/accounts/logout/ # 로그아웃
/accounts/profile/ # 프로필
실습
tube, accounts 앱 추가,
settings.py 수정
'DIRS': [BASE_DIR / 'templates'],
LANGUAGE_CODE = 'ko-kr'
TIME_ZONE = 'Asia/Seoul'
STATIC_URL = 'static/'
STATICFILES_DIRS = [
BASE_DIR / 'static',
]
MEDIA_ROOT = BASE_DIR / 'media'
MEDIA_URL = '/media/'
static 폴더 생성, media 폴더 생성
createsuperuser로 admin계정 생성
tutorialdjango / urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static
from django.conf import settings
from django.views.generic.base import RedirectView
urlpatterns = [
# path('', RedirectView.as_view(url='tube/'), name='root'), # 이렇게도 가능
path('', RedirectView.as_view(pattern_name='tube:post_list'), name='root'), # ''주소일때, tube앱의 post_list url로 가겠다.
path('admin/', admin.site.urls),
path('tube/', include('tube.urls')),
path('accounts/', include('accounts.urls')),
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # 미디어 url 추가
tube / urls.py 추가
accounts 먼저 구현을 위해서 일단 이렇게만.
from django.urls import path
from . import views
urlpatterns = []
accounts / urls.py 추가
from django.urls import path
from . import views
app_name = 'accounts'
urlpatterns = [
path('signup/', views.signup, name='signup'),
path('login/', views.login, name='login'),
path('logout/', views.logout, name='logout'),
path('profile/', views.profile, name='profile')
]
accounts / views.py 수정
CBV로 회원가입, 로그인, 로그아웃, 프로필 구현
from django.shortcuts import render
from django.views.generic import CreateView, TemplateView
from django.contrib.auth.forms import UserCreationForm
from django.urls import reverse_lazy
from django.contrib.auth.views import LoginView, LogoutView
from django.contrib.auth.mixins import LoginRequiredMixin # 로그인 됐을때 뷰에 접근을 허용하는 믹스인
signup = CreateView.as_view(
form_class = UserCreationForm,
template_name = 'accounts/form.html',
success_url = reverse_lazy('accounts:login')
)
login = LoginView.as_view(
template_name = 'accounts/form.html',
)
logout = LogoutView.as_view(
next_page = reverse_lazy('accounts:login'),
)
class ProfileView(LoginRequiredMixin, TemplateView):
template_name = 'accounts/profile.html'
profile = ProfileView.as_view()
templates / accounts / form.html 추가
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Submit">
</form>
templates / accounts / profile.html 추가
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>프로필</title>
</head>
<body>
<h1>프로필 페이지입니다.</h1>
<p>ID : {{user.username}}</p>
</body>
</html>
runserver 후에 login과 logout이 잘 되는지 확인.
이제 tube 구현
tube / models.py
Post모델, Comment모델, Tag모델 구현
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
author = models.ForeignKey(
User, on_delete=models.CASCADE
)
title = models.CharField(max_length=100)
content = models.TextField()
thumb_image = models.ImageField(
upload_to='tube/images/%Y/%m/%d/', blank=True)
file_upload = models.FileField(
upload_to='tube/files/%Y/%m/%d/', blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateField(auto_now=True)
view_count = models.PositiveIntegerField(default=0)
tags = models.ManyToManyField('Tag', blank=True)
def __str__(self):
return self.title
# def get_absolute_url(self):
# return f'/tube/{self.pk}/'
class Comment(models.Model):
post = models.ForeignKey(
Post, on_delete=models.CASCADE, related_name='comments'
)
author = models.ForeignKey(
User, on_delete=models.CASCADE
)
message = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateField(auto_now=True)
def __str__(self):
return self.message
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
def __str__(self):
return self.name
makemigrations, migrate 해주기
tube / admin.py 수정
from django.contrib import admin
from .models import Post, Comment, Tag
admin.site.register(Post)
admin.site.register(Comment)
admin.site.register(Tag)
admin으로 접속 후, 잘 되는지 게시물을 올려서 테스트.
tube / forms.py 추가
from django import forms
from .models import Post, Comment, Tag
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content', 'thumb_image', 'file_upload', 'tags']
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['message']
class TagForm(forms.ModelForm):
class Meta:
model = Tag
fields = ['name']
tube / urls.py 수정
from django.urls import path
from . import views
app_name = 'tube'
urlpatterns = [
path('', views.post_list, name='post_list'),
path('new/', views.post_new, name='post_new'),
path('<int:pk>/', views.post_detail, name='post_detail'),
path('<int:pk>/edit/', views.post_edit, name='post_edit'),
path('<int:pk>/delete/', views.post_delete, name='post_delete'),
path('tag/<str:tag>/', views.post_tag_list, name='post_tag_list'),
path('comment/new/<int:pk>/', views.comment_new, name='comment_new'),
path('new/tag/', views.tag_new, name='tag_new'),
]
tube / views.py 수정
중요
from django.shortcuts import render, redirect
from django.views.generic import ListView, DeleteView, UpdateView, DetailView, CreateView
from .models import Post, Comment, Tag
from .forms import PostForm, CommentForm, TagForm
from django.urls import reverse_lazy
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
class PostListView(ListView):
model = Post
def get_queryset(self): # DB에서 보여줄 쿼리셋을 가져오는 함수
qs = super().get_queryset() # 전체 가져오기
q = self.request.GET.get('q', '') # get요청일때 q값
if q: # q값이 있으면
qs = qs.filter(title__icontains=q) # q를 제목에 포함중인 쿼리셋만 가져오기
return qs # 리턴
post_list = PostListView.as_view()
class PostTagListView(ListView): # 태그를 가진 게시물 리스트 보여주기
model = Post
def get_queryset(self):
qs = super().get_queryset()
tag = self.kwargs['tag']
qs = qs.filter(tags__name__iexact=tag)
q = self.request.GET.get('q', '')
if q:
qs = qs.filter(title__icontains=q)
return qs
post_tag_list = PostTagListView.as_view()
class PostDetailView(DetailView):
model = Post
def get_context_data(self, **kwargs): # 원하는 쿼리셋이나 object를 추가해서 템플릿으로 전달
context = super().get_context_data(**kwargs) # 원래 전달할 context데이터
context['comment_form'] = CommentForm() # context데이터에 comment_form을 추가해서 같이 전달
return context
def get_object(self, queryset=None): # PostDetailView에서 사용할 object를 변경해서 반환.
pk = self.kwargs['pk']
post = Post.objects.get(pk=pk)
post.view_count += 1
post.save()
return super().get_object(queryset)
# def get_object(self): # PostDetailView에서 사용할 object를 변경해서 반환.
# pk = self.kwargs['pk'] # kwargs로 전달된 pk 저장
# post = super().get_object() # 원래 사용할 object
# post.view_count += 1 # view_count를 늘림
# post.save() # 저장
# return post # 반환
post_detail = PostDetailView.as_view()
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
form_class = PostForm
template_name = 'tube/form.html'
def form_valid(self, form): # 폼이 유효한지 확인
video = form.save(commit=False) # DB에 올리지 않고 임시 저장
video.author = self.request.user # video의 author을 현재 user로 설정
return super().form_valid(form) # 폼이 유효한지 확인하고 저장
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) # 원래 전달할 context데이터
context['tag_form'] = TagForm() # context데이터에 tag_form 추가해서 같이 전달
return context
def get_success_url(self): # 성공시 갈 url
return reverse_lazy('tube:post_detail', kwargs={'pk': self.object.pk}) # kwargs로 방금 만든 게시물 pk를 전달
post_new = PostCreateView.as_view()
class PostUpdateView(UserPassesTestMixin, UpdateView):
model = Post
form_class = PostForm
template_name = 'tube/form.html'
def get_success_url(self): # 성공시 갈 url
return reverse_lazy('tube:post_detail', kwargs={'pk': self.object.pk}) # kwargs로 방금 만든 게시물 pk를 전달
def test_func(self): # UserPassesTestMixin안의 메서드로 True, False로 접근 제한
return self.get_object().author == self.request.user
post_edit = PostUpdateView.as_view()
class PostDeleteView(UserPassesTestMixin, DeleteView):
model = Post
def get_success_url(self): # 성공시 갈 url
return reverse_lazy('tube:post_list')
def test_func(self): # UserPassesTestMixin안의 메서드로 True, False로 접근 제한
return self.get_object().author == self.request.user
post_delete = PostDeleteView.as_view()
class CommentCreateView(LoginRequiredMixin, CreateView): # 댓글 달기
model = Comment
form_class = CommentForm
def form_valid(self, form):
pk = self.kwargs['pk']
post = Post.objects.get(pk=pk)
comment = form.save(commit=False)
comment.author = self.request.user
comment.post = post
comment.save()
return redirect('tube:post_detail', pk=pk)
comment_new = CommentCreateView.as_view()
class TagCreateView(CreateView): # 태그 생성하기
model = Tag
form_class = TagForm
def form_valid(self, form):
form.save()
return redirect('tube:post_new')
tag_new = TagCreateView.as_view()
templates / tube / post_list.html 추가
<h1>Post List</h1>
<form action="" method="GET">
<input type="text" name="q" value="{{ request.GET.q }}">
<input type="submit" value="검색">
</form>
<ul>
{% for post in post_list %}
<li>
<a href="{% url 'tube:post_detail' post.pk %}">
{{ post.title }}
</a>
</li>
{% endfor %}
</ul>
<a href="{% url 'tube:post_new' %}">업로드</a>
templates / tube / post_detail.html 추가
<p>{{post.title}}</p>
<p>{{post.content}}</p>
<p>조회수 : {{post.view_count}}</p>
{% if post.file_upload %}
<video src="{{post.file_upload.url}}" controls></video>
{% endif %}
<p>태그 :
{% for tag in post.tags.all %}
<a href="{% url 'tube:post_tag_list' tag %}">{{tag}}</a>
{% endfor %}
</p>
<hr>
<section>
<h3>댓글</h3>
{% for comment in post.comments.all %}
<p>{{comment.message}}</p>
<p>{{comment.author}}</p>
<p>{{comment.updated_at}}</p>
{% endfor %}
</section>
<section>
<h3>댓글 작성</h3>
<form action="{% url 'tube:comment_new' post.pk %}" method="post">
{% csrf_token %}
{{ comment_form.as_p }}
<input type="submit" value="댓글 작성">
</form>
</section>
<a href="{% url 'tube:post_list' %}">목록</a>
{% if user == post.author %}
<a href="{% url 'tube:post_edit' post.pk %}">수정</a>
<a href="{% url 'tube:post_delete' post.pk %}">삭제</a>
{% endif %}
templates / tube / form.html 추가
<form action="{% url 'tube:tag_new' %}" method="post">
{% csrf_token %}
<p>태그 추가하기</p>
{{ tag_form.as_p }}
<input type="submit" value="추가">
</form>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="확인">
</form>
templates / tube / post_confirm_delete.html 추가
<h1>글 삭제</h1>
<form method="post">
{% csrf_token %}
<p>글을 삭제하시겠습니까?</p>
<input type="submit" value="네!" class="btn btn-primary">
</form>
'Django' 카테고리의 다른 글
Django 프로젝트(1) - Hot🔥Deal Blog (0) | 2023.11.08 |
---|---|
Django와 SQLite3 (0) | 2023.10.23 |
Django 실습(8) RDB(관계형 데이터베이스) 만들어보기 (2) | 2023.10.17 |
Django 템플릿 필터 (1) | 2023.10.17 |
Django CBV - 제네릭 뷰 (Generic View) (1) | 2023.10.16 |