지금까진 글을 올리는 기능이나 모든 콘텐츠 생성 및 변경 기능을 admin이 갖고있었다.
하지만 콘텐츠(글) 작성,수정이나 읽는 기능은 일반 사용자에게도 부여해야한다.
따라서 이번 챕터에선 콘텐츠에 관한 권한 부여를 할것이다.
상단 메뉴바에 add,change를 만들것이다.
화면 설계는
Post를 작성하는 Post_form.html
Post를 수정할때 우선 포스트change목록으로 들어가는 post_change_list.html
Post를 수정할때는 Post 작성하는 post_form.html을 같이 쓴다.
Post를 삭제할때는 삭제버튼 누르면, Post_confirm_delete로 넘어가면서 삭제 확인화면이 뜬다.
장고에서는 제네릭 뷰를 이용해서 컨텐츠별 편집 권한을 부여한다.
모델링은 따로 필요업지만 블로그와 북마크 각각에 권한이 필요하므로 아래와같이 작성해야한다.
from django.contrib.auth.models import User
owner = models.ForeignKey(User,on_delete=models.CASCADE,blank=True,null=True)
bookmark와 user의 관계는 N:1관계이므로 외래키를 이용했다.
blog에는 위의 bookmark에서 작성문에 추가로 아래를 추가한다.
from django.utils.text import slugify
def save(self,*args,**kwargs):
self.slug = slugify(self.title,allow_unicode=True)
super().save(*args,**kwargs)
Slug필드를 자동으로 채우기 위해 slugfy()함수를 임포트했다. url에 slugfy에서 입력한 양식이 저장된다.
#Example: /bookmark/add/
path('add/',views.BookmarkCreateView.as_view(),name='add',),
#Example: /bookmark/change/
path('change/',views.BookmarkChangeLV.as_view(),name='change'),
#Example: /bookmark/99/update/
path('<int:pk>/update/',views.BookmarkUpdateView.as_view(),name='update',),
#Example: /bookmark/99/delete/
path('<int:pk>/delete/',views.BookmarkDeleteView.as_view(),name='delete',),
URL을 추가하고 수정하는 과정에선 blog와 bookmark가 같다고 봐도 무방하다.
bookmark.views먼저 보자
from django.views.generic import ListView, DetailView
from bookmark.models import Bookmark
from django.views.generic import CreateView,UpdateView,DeleteView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from djangoProject.views import OwnerOnlyMixin
class BookmarkLV(ListView):
model = Bookmark
class BookmarkDV(DetailView):
model = Bookmark
class BookmarkCreateView(LoginRequiredMixin,CreateView):
model = Bookmark
fields = ['title','url']
success_url = reverse_lazy('bookmark:index')
def form_valid(self, form):
form.instance.owner = self.request.user
return super().form_valid(form)
class BookmarkChangeLV(LoginRequiredMixin,ListView):
template_name = 'bookmark/bookmark_change_list.html'
def get_queryset(self):
return Bookmark.objects.filter(owner= self.request.user)
class BookmarkUpdateView(OwnerOnlyMixin,DetailView):
model = Bookmark
success_url = reverse_lazy('bookmark:index')
class BookmarkDeleteView(OwnerOnlyMixin,DetailView):
model = Bookmark
success_url = reverse_lazy('bookmark:index')
blog/views.py
bookmark/view와 구조적으로 차이가 없다.필드의 차이정도만 있다.
djangoProject.py(blog,bookmark의 상위 폴더)
class OwnerOnlyMixin(AccessMixin):
raise_exception = True
permission_denied_message = "Onwer Only can update/delete the object"
def dispatch(self,request,*args,**kwargs):
obj = self.get_object()
if request.user !=obj.owner:
return self.handle_no_permission()
return super().dispatch(request,*args,**kwargs)
해당 폴더에서 homeView와 회원가입 view, 그리고 OwenerOnlyMixin 클래스를 갖고있다. AccessMixin 임포트를 통해 뷰 진입 단계에서 적절한 권한을 가졌는지 가려준다.
bookmarkChangeLV와 PostChangeLV는 ListVIew를 상속받으므로 티폴트 템플릿명은 xxx_list.html이다.이미 사용하므로 template_name에서 지정해준다.
base.html
<div class="dropdown-menu">
<a class="dropdown-item" href="{% url 'bookmark:add' %}">Bookmark</a>
<a class="dropdown-item" href="{% url 'blog:add' %}">Post</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="">Album</a>
<a class="dropdown-item" href="">Photo</a>
</div>
<div class="dropdown-menu">
<a class="dropdown-item" href="{% url 'bookmark:change' %}">Bookmark</a>
<a class="dropdown-item" href="{% url 'blog:change' %}">Post</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="">Album</a>
<a class="dropdown-item" href="">Photo</a>
</div>
각각 드랍다운 메뉴를 누르면 add/change할것들이 나온다.
bookmark/bookmark_form.html
<h1>Bookmark Create/Update - {{user}}</h1>
<p class="font-italic">This is a creation or update form for your bookmark.</p>
{% if form.errors %}
<div class="alert alert-danger">
<div class="font-weight-bold">Wrong! Please correct the error(s) below.</div>
{{ form.errors }}
</div>
{% endif %}
bookmark/add/ 링크가 들어오면 북마크 폼을 만들어내는 화면으로 렌더링한다.
데이터 폼은 post방식으로 전달된다.
bookmark_change_list.html
<tbody>
{% for item in object_list %}
<tr>
<td>{{ item.title }}</td>
<td>{{ item.url }}</td>
<td>{{ item.owner }}</td>
<td><a href="{% url 'bookmark:update' item.id %}">Update</a></td>
<td><a href="{% url 'bookmark:delete' item.id %}">Delete</a></td>
</tr>
{% endfor %}
</tbody>
바디 부분에서 컨텍스트변수를 받는다. bookmark object list 가 각 항목을 돌면서 출력해줄것이다. object의 필드들과 update,delete로 나눠서 각 부분을 실행시킬수있도록 링크를 달아주었다.
bookmark_confirm_delete.html
<form action="." method="post">{% csrf_token %}
<p>Are you sure you want to delete "{{ object }}" ?</p>
<input type="submit" value="Confirm" class="btn btn-danger btn-sm" />
</form>
삭제 폼 또한 따로 만들어서 Post방식을 이용해 delete가 완수될수 있도록 한다.
Template설계과정에서 Bookmark와 blog는 필드정도의 차이만 있고 구조적으로 차이가 없다. 따라서 설명을 생략하려한다.
403.html
{% extends "base.html" %}
{% block title %}403.html{% endblock %}
{% block content %}
<h1>Permission Denied (403)</h1>
<br>
<div class="alert alert-danger">
<div class="font-weight-bold">{{ exception }}</div>
</div>
{% endblock content %}
403.html은 djangoProject/views.py의 OwnerOnlyMinxin클래스에서 사용하는 템플릿파일이다. OwnerOnlyMixin이 403익셉션을 발생시키면, 장고 기본 핸들러인 permission_denied()메서드가 렌더링을 통해 클라이언트를 보낸다.