DRF - Auth

Nam Eun-Ji·2022년 3월 20일
0

Django REST Framework

목록 보기
3/4

Setting

django-admin startproject profileapi
python manage.py startapp profiles

settings.py

INSTALLED_APPS = [
    ...,
    'rest_framework',
    'profiles'
]

MEDIA_URL = "/media/"
MEDIA_ROOT = "uploads"

urls.py

from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls)
]

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

profiles.models.py

from django.db import models
from django.contrib.auth.models import User


class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    bio = models.CharField(max_length=240, blank=True)
    city = models.CharField(max_length=30, blank=True)
    avatar = models.ImageField(null=True, blank=True)

    def __str__(self):
        # return self.user.username  # error!
        return str(self.user)


class ProfileStatus(models.Model):
    user_profile = models.ForeignKey(Profile, on_delete=models.CASCADE)
    status_content = models.CharField(max_length=240)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name_plural = "statuses"

    def __str__(self):
        return str(self.user_profile)

⚠️ ERROR
AttributeError: 'Profile' object has no attribute 'username'
난 분명 강의와 똑같이 self.user.user_name 를 작성했다. 그런데 왜 에러가 나는 것인가... 상속받으면 그대로 부모 속성에 접근할 수 있는 것 아니었나? 다시 선언해주어야하는건가? 정확한 원인을 못 찾고 아래와 같이 변경해주었다.

    def __str__(self):
        return str(self.user)

profiles.admin.py

from django.contrib import admin
from profiles.models import Profile, ProfileStatus


admin.site.register(Profile)
admin.site.register(ProfileStatus)

profiles.signals.py

from django.contrib.auth.models import User
from django.db.models.signals import post_save  # User 객체가 생성되면 시그널 정보를 전달
from django.dispatch import receiver  # 시그널 정보를 받는 리시버
from profiles.models import Profile


@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
    print("Created : ", created)
    if created:  # 새롭게 객체 생성되면 True, 아니면 False
        Profile.objects.create(user=instance)

profiles.apps.py

from django.apps import AppConfig


class ProfilesConfig(AppConfig):
    name = 'profiles'

    def ready(self):
        import profiles.signals

profiles.init.py

default_app_config = "profiles.apps.ProfilesConfig"

profiles.api.serializers.py

from rest_framework import serializers
from profiles.models import Profile, ProfileStatus


class ProfileSerializer(serializers.ModelSerializer):
    user = serializers.StringRelatedField(read_only=True)
    avatar = serializers.ImageField(read_only=True)

    class Meta:
        model = Profile
        fields = "__all__"


class ProfileAvatarSerializer(serializers.ModelSerializer):

    class Meta:
        model = Profile
        fields = ("avatar",)


class ProfileStatusSerializer(serializers.ModelSerializer):
    user_profile = serializers.StringRelatedField(read_only=True)

    class Meta:
        model = ProfileStatus
        fields = "__all__"





session을 이용한 인증

Auth와 관련된 모듈 설치 및 setting

pip install django-rest-auth
pip install requests : 테스트를 하기 위한 모듈

settings.py
Session Authentication과 Token Authentication 이용을 위해서 관련 내용을 추가해준다.

INSTALLED_APPS = [
	...,
    'rest_framework.authtoken',
    'rest_auth',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    ]
}

위와 같이 설정해주고 auth, authtoken 등 관련 모델 생성을 위해 migrate migrate해준다.
python manege.py migrate
migrate하고 admin에 가보면 token이 추가됨을 확인할 수 있다.


urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path("api/", include("profiles.api.urls")),
    path("api-auth/", include("rest_framework.urls")),
    path("api/rest-auth/", include("rest_auth.urls"))
]



Login

profiles.api.views.py

from rest_framework import generics
from rest_framework.permissions import IsAuthenticated
from profiles.models import Profile
from profiles.api.serializers import ProfileSerializer


class ProfileList(generics.ListAPIView):
    queryset = Profile.objects.all()
    serializer_class = ProfileSerializer
    permission_classes = [IsAuthenticated]

profiles.api.urls.py

from django.urls import path
from profiles.api.views import ProfileList

urlpatterns = [
    path("profiles/", ProfileList.as_view(), name="profile-list")
]

token 발급

clients.token-auth-test1.py
테스트를 하기 위한 파일이다.
아래 코드는 토큰이 정상적으로 발급이 되는지 확인하기 위한 코드로 post 요청을 보낸다.

import requests


def client():
    credentials = {"username": "admin", "password": "admin"}
    response = requests.post("http://127.0.0.1:8000/api/rest-auth/login/", data=credentials)

    print(f"Status Code : {response.status_code}")
    print(response.json())


if __name__ == "__main__":
    client()

결과

Status Code : 200
{'key': 'cf12b6bba6311e50144a9868d5dd1aeac0708143'}

토큰 미 전달 시

import requests


def client():
    response = requests.get("http://127.0.0.1:8000/api/profiles/")

    print(f"Status Code : {response.status_code}")
    print(response.json())


if __name__ == "__main__":
    client()
# 결과
Status Code : 403
{'detail': 'Authentication credentials were not provided.'}

토큰 전달시

import requests


def client():
    # 토큰 전달 시 서버 응답 확인하기
    headers = {"Authorization": "Token cf12b6bba6311e50144a9868d5dd1aeac0708143"}
    response = requests.get("http://127.0.0.1:8000/api/profiles/", headers=headers)

    print(f"Status Code : {response.status_code}")
    print(response.json())


if __name__ == "__main__":
    client()
Status Code : 200
[{'id': 1, 'user': 'admin', 'avatar': 'http://127.0.0.1:8000/media/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2022-02-26_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_1.41.32_QnrV8ei.png', 'bio': 'Site Administrator', 'city': 'Testland2'}, {'id': 2, 'user': 'random', 'avatar': None, 'bio': '', 'city': ''}]





Sign up

setup

pip install django-allauth

settings.py
아래와 같이 추가 작성한 후 migrate해준다.

INSTALLED_APPS = [
    'django.contrib.sites',

    'allauth',
    'allauth.account',
    'allauth.socialaccount'

    'rest_auth.registration'
    ...
]

SITE_ID = 1
ACCOUNT_EMAIL_VERIFICATION = "none"
ACCOUNT_EMAIL_REQUIRED = (True)

urls.py

urlpatterns = [
    ...
    path("api/rest-auth/registration/", include("rest_auth.registration.urls"))
]

sign up test

clients.token-auth-test2.py

import requests

def client():
    credentials = {
        "username": "resttest",
        "password1": "changeme111",
        "password2": "changeme222",
        "email": "rest@test.com",
    }
    response = requests.post("http://127.0.0.1:8000/api/rest-auth/registration/", data=credentials)

    print(f"Status Code : {response.status_code}")
    print(response.json())


if __name__ == "__main__":
    client()

보통 회원가입할 때 패스워드를 두번 기입하기 때문에 키를 password1, password2 이렇게 나눠 받는 듯 하다. 그래서 뭣도 모르고 패스워드를 각각 다르게 기입했더니 아래와 같은 메세지가 왔다.

Status Code : 400
{'non_field_errors': ["The two password fields didn't match."]}

패스워드를 동일하게 맞춰주고 보냈더니

Status Code : 201
{'key': 'df251b3cdf084cbbb150dc440872494e5fcb0c0a'}

마지막으로 profile 테스트

import requests

def client():
	# 위에서 전달받은 토큰으로 변경
    headers = {"Authorization": "Token df251b3cdf084cbbb150dc440872494e5fcb0c0a"}
    response = requests.get("http://127.0.0.1:8000/api/profiles/", headers=headers)

    print(f"Status Code : {response.status_code}")
    response_data = response.json()
    print(response_data)


if __name__ == "__main__":
    client()
[{'id': 1, 'user': 'admin', 'avatar': 'http://127.0.0.1:8000/media/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2022-02-26_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_1.41.32_QnrV8ei.png', 'bio': 'Site Administrator', 'city': 'Testland2'}, {'id': 2, 'user': 'random', 'avatar': None, 'bio': '', 'city': ''}, {'id': 3, 'user': 'resttest', 'avatar': None, 'bio': '', 'city': ''}, {'id': 4, 'user': 'resttest2', 'avatar': None, 'bio': '', 'city': ''}]
profile
한 줄 소개가 자연스러워지는 그날까지

0개의 댓글