今天的實作內容主要根據教學網站進行。
在Day22時,我們實作了身份驗證功能,部分功能要求使用者必須登入網站才可使用;Day23整合社群登入,提供使用者可選擇以Google帳號進行登入。
截至Day23的內容,我們開發了小說追蹤功能,所有登入者都可以將小說加入追蹤;並提供Book List,所有的登入者都可以看到目前追蹤的小說。
今天我們將實作權限管理,透過權限管理,讓不同的登入者可以使用不同的功能,並在Book List提供僅該登入者追蹤的小說。
我們將使用Django內建的User和Group,透過對於Group授予權限、並將User分配到該Group,來達到此目的。
為了記錄每個使用者各自追蹤的小說,我們新增了User_Book來記錄使用者和小說之間的關係。
class User_Book(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
book = models.ForeignKey(Book, on_delete=models.SET_NULL, null=True)
notify = models.BooleanField(default=True)
class Meta:
ordering = ['book']
unique_together = [['book', 'user']]
Django的signal模組,可支援物件中信息的傳遞。使用此模組,當接收到User建立時,create_user_profile()會將該User加入'Track Members'群組。
from django.contrib.auth.models import User, Group
from django.db.models.signals import post_save
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
instance.groups.add(Group.objects.get(name='Track Members'))
Django提供了很多預設權限可以做選擇,在教學網站中也提供了客製權限的方法。
當我們想要用'can_track'這個權限來控制使用者是否可使用「加入追蹤」功能,只要於Model中的Meta設定permissions,再重新migrate資料庫,就會自動新的權限。
class User_Book(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
book = models.ForeignKey(Book, on_delete=models.SET_NULL, null=True)
notify = models.BooleanField(default=True)
class Meta:
ordering = ['book']
unique_together = [['book', 'user']]
permissions = (('can_track', 'Can Track'),)
但依照此方法,每個權限需要與Model進行綁定,這樣會導致該權限會被Model展開,在實際操作時會容易混亂且不方便管理。
舉例來說,對於加入追蹤功能而言,需要同時有Author、Book、User_Book三個Model的權限,若在三個類別各自的Meta下做permission設定,在Group的管理畫面上會出現:
對於此問題,可以建立一個空的類別,讓客製權限統一集中在這邊管理。
class RightsSupport(models.Model):
class Meta:
# No database table creation or deletion operations will be performed for this model.
managed = False
# disable "add", "change", "delete" and "view" default permissions
default_permissions = ()
permissions = ( ('can_track', 'Can Track'), )
登入http://127.0.0.1:8000/admin/,新增「Track Member」Group,並授予「track | rights support | Can Track」權限。
在template中,使用者的權限儲存在「perms」變數中,可以透過此變數來檢查使用者是否有特定權限:
{% if perms.track.can_track %}
<p>有此功能權限。</p>
{% else %}
<p>無此功能權限。</p>
{% endif %}
根據view是function類型、或class類型,在權限管理上有不同的做法。
使用合成方法,加入LoginRequiredMixin類別,以設定permission_required屬性;並修改get_queryset()方法,僅查詢當前user加入追蹤的資料。
from django.contrib.auth.mixins import LoginRequiredMixin
class BookListView(LoginRequiredMixin, generic.ListView):
permission_required = ('track.can_track')
model = Book
template_name = 'book_list.html'
def get_queryset(self):
return Book.objects.filter(user_book__user=self.request.user, istrack=True)
使用修飾詞進行權限檢查。
但使用@permission_required()時,若使用者無權限,會自動導到登入畫面,對於已登入的使用者來說此流程比較詭異。 (此問題尚未完成替代方法)
from django.contrib.auth.decorators import login_required, permission_required
@login_required
@permission_required('track.can_track')
def TrackBook(request):
pass