Django2 (13. Decorator, 페이지 권한 쉽게 설정하기) feat.페스트캠퍼스

min seung moon·2021년 3월 19일
1

Django

목록 보기
29/37

1. 페이지에 대한 권한 설정

  • 로그인을 해야지 접근할 수 있는 Decorator

01. fcuser

  • fcuser에 decorator.py 생성 및 추가
def login_required(function):
    def wrap():
        print('login_required!')
        return function()

    return wrap

  • order의 views.py 안에 있는 orderlist에 decorator 지정(method_decorator)
from django.shortcuts import render, redirect
from .forms import RegisterForm
from django.views.generic.edit import FormView
from django.views.generic import ListView
from .models import Order
# decorator 설정, 클래스에 바로 지정 가능
from django.utils.decorators import method_decorator
from fcuser.decorator import login_required
# Create your views here.


class OrderCreate(FormView):
    # 화면은 따로 구연할 필요 없기 때문에 template_name = 은 생략
    form_class = RegisterForm
    success_url = '/product/'

    # 실패했을 때 redirect하는 함수
    # 숫자형이기 때문에 문자열로 변경해주어야 한다
    def form_invalid(self, form):
        return redirect('/product/'+str(form.product))

    # form을 생성할 때 어떤 인자값을 전달해서 만들건지를 결정하는 함수
    # formview안에서도request를 전달할 수 있게 해주어야 한다!
    def get_form_kwargs(self, **kwargs):
        kw = super().get_form_kwargs(**kwargs)
        kw.update({
            # 기존에 있던 인자 값에다가 request를 포함하겠다!
            'request': self.request
        })
        return kw


# 클래스 뷰에서 URL에 접근했을 클래스 뷰를 호출할 때 dispatch가 호출(실행)된다
# def dispatch()
# decorator를 지정할려면 원래 dispatch 함수를 만들어서 decorator를 지정해야 된다
# 장고안에 있는 method_decorator를 지정하면 바로 지정 가능 하다
@method_decorator(login_required, name='dispatch')
class OrderList(ListView):
    template_name = 'order.html'
    context_object_name = 'order_list'

    def get_queryset(self, **kwargs):
        queryset = Order.objects.filter(
            fcuser__email=self.request.session.get('user'))
        return queryset

  • orderlist에 들어가보기!

    • orderlist에 접근하면 함수가 호출되는데 decorator.py에서 만든 wrap이 호출이 된다
    • 그런데 인자 값의 수가 달라서 문제가 생긴다
  • fcuser의 decorator.py의 wrap에서 다 날려버려서?

# 클래스 뷰에서 URL에 접근했을 클래스 뷰를 호출할 때 dispatch가 호출(실행)된다
# def dispatch()
# decorator를 지정할려면 원래 dispatch 함수를 만들어서 decorator를 지정해야 된다
# 장고안에 있는 method_decorator를 지정하면 바로 지정 가능 하다
@method_decorator(login_required, name='dispatch')
class OrderList(ListView):
    template_name = 'order.html'
    context_object_name = 'order_list'

    def get_queryset(self, **kwargs):
        queryset = Order.objects.filter(
            fcuser__email=self.request.session.get('user'))
        return queryset

    # dispatch는 다양한 인자를 받을 수 있다
    def dispatch(request, *args, **kwargs)
    # 그러면 함수를 호출하게 되면 전부다 wrap에 전달하게 된다

def login_required(function):
    # def dispatch(request, *args, **kwargs)이 전부 날라감~~~
    def wrap():
        print('login_required!')
        # 원래는 넣어줘야 한다
        return function(request, *args, **kwargs)

    return wrap

  • fcuser의 decorator.py에 wrapping한 함수의 인자 값 맞춰주기
def login_required(function):
    # wrapping한 함수와 기존 함수의 인자값을 맞춰 줘야 한다
    def wrap(request, *args, **kwargs):
        print('login_required!')
        # 기존의 함수에게 전달
        return function(request, *args, **kwargs)

    return wrap



  • fcuser에 decorator에서 로그인 확인(session 확인)
from django.shortcuts import redirect

def login_required(function):
    # wrapping한 함수와 기존 함수의 인자값을 맞춰 줘야 한다
    def wrap(request, *args, **kwargs):
        user = request.session.get('user')
        if user is None or not user:
            return redirect('/login')
        return function(request, *args, **kwargs)

    return wrap

  • fcuser의 views.py에 로그아웃 기능 만들기!
from django.shortcuts import render, redirect
from django.views.generic.edit import FormView
from .forms import RegisterForm, LoginForm
# Create your views here.


# index.html 연결 뷰
def index(request):
    return render(request, 'index.html', {'email': request.session.get('user')})


class RegisterView(FormView):
    # html file
    template_name = 'register.html'
    # forms.py에 있는 forms
    form_class = RegisterForm
    # 정상적으로 값이 처리가 되었을 때 url이동
    success_url = '/'


class LoginiView(FormView):
    # html file
    template_name = 'login.html'
    # forms.py에 있는 forms
    form_class = LoginForm
    # 정상적으로 값이 처리가 되었을 때 url이동
    success_url = '/'

    # 유효성 검사가 끝났을 때, 로그인이 정상적으로 되었을 때
    def form_valid(self, form):
        self.request.session['user'] = form.email

        return super().form_valid(form)


def logout(request):
    if 'user' in request.session:
        del(request.session['user'])

    return redirect('/')

  • root에 urls.py에 연결시키기!
from django.contrib import admin
from django.urls import path
from fcuser.views import index, RegisterView, LoginiView, logout
from product.views import ProductList, ProductCreate, ProductDetail
from order.views import OrderCreate, OrderList

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', index),
    # class는 .as_view()를 입력!
    path('register/', RegisterView.as_view()),
    path('login/', LoginiView.as_view()),
    path('product/', ProductList.as_view()),
    path('product/create/', ProductCreate.as_view()),
    # 상세보기 페이지는 주소에 어떤 상품인지 나타낼수 있어야한다!
    # url에 지정을 할 수 있는데, detailView에서 기본적으로 pk라는 값을 사용
    # <int:pk> 숫자형으로 받고 받아온 숫자는 pk라는 변수로 사용
    path('product/<int:pk>/', ProductDetail.as_view()),
    path('order/create/', OrderCreate.as_view()),
    path('order/', OrderList.as_view()),
    path('logout/', logout),
]

  • 로그아웃하고 다시 orderlist에 들어가보기!



  • 로그인 후 다시 orderlist에 들어가보기!


02. 다른 페이지에도 적용하기!

  • 주문 페이지!, create도 할 수 없어야 한다
  • order의 views.pydml ordercreate에도 적용(주문하기)
from django.shortcuts import render, redirect
from .forms import RegisterForm
from django.views.generic.edit import FormView
from django.views.generic import ListView
from .models import Order
# decorator 설정, 클래스에 바로 지정 가능
from django.utils.decorators import method_decorator
from fcuser.decorator import login_required
# Create your views here.


@method_decorator(login_required, name='dispatch')
class OrderCreate(FormView):
    # 화면은 따로 구연할 필요 없기 때문에 template_name = 은 생략
    form_class = RegisterForm
    success_url = '/product/'

    # 실패했을 때 redirect하는 함수
    # 숫자형이기 때문에 문자열로 변경해주어야 한다
    def form_invalid(self, form):
        return redirect('/product/'+str(form.product))

    # form을 생성할 때 어떤 인자값을 전달해서 만들건지를 결정하는 함수
    # formview안에서도request를 전달할 수 있게 해주어야 한다!
    def get_form_kwargs(self, **kwargs):
        kw = super().get_form_kwargs(**kwargs)
        kw.update({
            # 기존에 있던 인자 값에다가 request를 포함하겠다!
            'request': self.request
        })
        return kw


# 클래스 뷰에서 URL에 접근했을 클래스 뷰를 호출할 때 dispatch가 호출(실행)된다
# def dispatch()
# decorator를 지정할려면 원래 dispatch 함수를 만들어서 decorator를 지정해야 된다
# 장고안에 있는 method_decorator를 지정하면 바로 지정 가능 하다
@method_decorator(login_required, name='dispatch')
class OrderList(ListView):
    template_name = 'order.html'
    context_object_name = 'order_list'

    def get_queryset(self, **kwargs):
        queryset = Order.objects.filter(
            fcuser__email=self.request.session.get('user'))
        return queryset






  • product에 views.py에서 productcreate에 적용!(제품등록)
from django.shortcuts import render
from django.views.generic import ListView, DetailView
from django.views.generic.edit import FormView
from .models import Product
from .forms import RegisterForm
from order.forms import RegisterForm as OrderForm
from django.utils.decorators import method_decorator
from fcuser.decorator import login_required
# Create your views here.


class ProductList(ListView):
    model = Product
    template_name = 'product.html'
    # object_list로 사용하기 싫으면 context_object_name으로 변경 가능
    context_object_name = 'product_list'


@method_decorator(login_required, name='dispatch')
class ProductCreate(FormView):
    template_name = 'register_product.html'
    form_class = RegisterForm
    success_url = '/product/'


class ProductDetail(DetailView):
    template_name = 'product_detail.html'
    # 어떤 모델이 아닌 쿼리셋을 지정
    # 필터를 통해서 보여질지 말지도 결정할 수 있다
    queryset = Product.objects.all()
    # template에서 사용할 변수명 지정
    context_object_name = 'product'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # Order의 forms의 생성자 함수!
        # 그리고 order의 views.py에서 request를 처리한다
        context['form'] = OrderForm(self.request)
        return context



03. fcuser에 있는 decorator.py에 admin_required 만들기

  • decorator.py에 admin_required 추가
from django.shortcuts import redirect
# db에 있는 user 정보 갖고오기 위한 모델불러오기
from .models import Fcuser

def login_required(function):
    # wrapping한 함수와 기존 함수의 인자값을 맞춰 줘야 한다
    def wrap(request, *args, **kwargs):
        user = request.session.get('user')
        if user is None or not user:
            return redirect('/login')
        return function(request, *args, **kwargs)

    return wrap

# login_required가 함께 있기 때문에 함수만 전달
#@login_required(function)
def admin_required:
    # wrapping한 함수와 기존 함수의 인자값을 맞춰 줘야 한다
    def wrap(request, *args, **kwargs):
        user = request.session.get('user')
        if user is None or not user:
            return redirect('/login')
        # admin 확인을 위해 model 불러오기
        # 현재 등급이 없기 때문에 만들자!
        user = Fcuser.objects.get(email=user)
        if user.level != 'admin':
            return redirect('/')

        return function(request, *args, **kwargs)

    return wrap

  • fcuser의 models.py에 level(등급) 만들자!
from django.db import models

# Create your models here.


class Fcuser(models.Model):
    email = models.EmailField(verbose_name='이메일')
    password = models.CharField(max_length=64, verbose_name='비밀번호')
    level = models.CharField(max_length=8, verbose_name='등급',
                             # choices, 필드에 들어갈 값을 미리 정의
                             choices=(
                                 ('admin', 'admin'),
                                 ('user', 'user')
                             ))
    register_date = models.DateTimeField(
        auto_now_add=True, verbose_name='등록날짜')

    def __str__(self):
        return self.email

    class Meta:
        db_table = 'fastcampus_fcuser'
        verbose_name = '사용자'
        verbose_name_plural = '사용자'

  • migration해주기
    • 현재 default 값(기존 데이터 베이스에 입력할 값)을 user로 지정
    (fc_env) PS C:\Users\user\Desktop\페스트캠퍼스\DJANGO_실전\fc_django> python   manage.py makemigrations
    You are trying to add a non-nullable field 'level' to fcuser without a default; we can't do 
    Please select a fix:
    1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
    2) Quit, and let me add a default in models.py
    Select an option: user
    Please select a valid option: 1
    Please enter the default value now, as valid Python
    The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
    Type 'exit' to exit this prompt
    >>> 'user'
    Migrations for 'fcuser':
    fcuser\migrations\0002_fcuser_level.py
      - Add field level to fcuser
    (fc_env) PS C:\Users\user\Desktop\페스트캠퍼스\DJANGO_실전\fc_django> python manage.py migrate
    Operations to perform:
    Apply all migrations: admin, auth, contenttypes, fcuser, order, product, sessions
    Running migrations:
    Applying fcuser.0002_fcuser_level... OK
    python manage.py makemigrations
    option 1
    >> 'user'
    python manage.py migrate
  • 그럼 fcuser에 있는 decorator.py의 admin_required가 정상적으로 작동하겠죠!
from django.shortcuts import redirect
# db에 있는 user 정보 갖고오기 위한 모델불러오기
from .models import Fcuser


def login_required(function):
    # wrapping한 함수와 기존 함수의 인자값을 맞춰 줘야 한다
    def wrap(request, *args, **kwargs):
        user = request.session.get('user')
        if user is None or not user:
            return redirect('/login')
        return function(request, *args, **kwargs)

    return wrap

# login_required가 함께 있기 때문에 함수만 전달


def admin_required(function):
    def wrap(request, *args, **kwargs):
        user = request.session.get('user')
        if user is None or not user:
            return redirect('/login')
        # admin 확인을 위해 model 불러오기
        # 현재 등급이 없기 때문에 만들자!
        user = Fcuser.objects.get(email=user)
        if user.level != 'admin':
            return redirect('/')

        return function(request, *args, **kwargs)

    return wrap

  • product의 views.py에 있는 productcreate은 로그인이 아니라 admin제한을 걸어야 한다
from django.shortcuts import render
from django.views.generic import ListView, DetailView
from django.views.generic.edit import FormView
from .models import Product
from .forms import RegisterForm
from order.forms import RegisterForm as OrderForm
from django.utils.decorators import method_decorator
from fcuser.decorator import admin_required
# Create your views here.


class ProductList(ListView):
    model = Product
    template_name = 'product.html'
    # object_list로 사용하기 싫으면 context_object_name으로 변경 가능
    context_object_name = 'product_list'


@method_decorator(admin_required, name='dispatch')
class ProductCreate(FormView):
    template_name = 'register_product.html'
    form_class = RegisterForm
    success_url = '/product/'


class ProductDetail(DetailView):
    template_name = 'product_detail.html'
    # 어떤 모델이 아닌 쿼리셋을 지정
    # 필터를 통해서 보여질지 말지도 결정할 수 있다
    queryset = Product.objects.all()
    # template에서 사용할 변수명 지정
    context_object_name = 'product'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # Order의 forms의 생성자 함수!
        # 그리고 order의 views.py에서 request를 처리한다
        context['form'] = OrderForm(self.request)
        return context

  • 한번 확인하기!



  • 암호환한 비밀번호 때문에 길이 초과했네요ㅎㅎ fcuser의 models.py에서 수정하기!(64 -> 128)
from django.db import models

# Create your models here.


class Fcuser(models.Model):
    email = models.EmailField(verbose_name='이메일')
    password = models.CharField(max_length=128, verbose_name='비밀번호')
    level = models.CharField(max_length=8, verbose_name='등급',
                             # choices, 필드에 들어갈 값을 미리 정의
                             choices=(
                                 ('admin', 'admin'),
                                 ('user', 'user')
                             ))
    register_date = models.DateTimeField(
        auto_now_add=True, verbose_name='등록날짜')

    def __str__(self):
        return self.email

    class Meta:
        db_table = 'fastcampus_fcuser'
        verbose_name = '사용자'
        verbose_name_plural = '사용자'

  • 다시 user -> admin으로 변경




profile
아직까지는 코린이!

0개의 댓글