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__"
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"))
]
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")
]
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': ''}]
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"))
]
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': ''}]