setting.pyadmin이 있어서 admin 기능을 사용 가능했던 것
auth 은 user를 만들고(createsuperuser) 로그인 할 수 있던 것
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
...
Django Shell>>> from django.contrib.auth.models import User
>>> User
<class 'django.contrib.auth.models.User'>
>>> User._meta.get_fields()
(<ManyToOneRel: admin.logentry>, <django.db.models.fields.AutoField: id>, <django.db.models.fields.CharField: password>, <django.db.models.fields.DateTimeField: last_login>, <django.db.models.fields.BooleanField: is_superuser>, <django.db.models.fields.CharField: username>, <django.db.models.fields.CharField: first_name>, <django.db.models.fields.CharField: last_name>, <django.db.models.fields.EmailField: email>, <django.db.models.fields.BooleanField: is_staff>, <django.db.models.fields.BooleanField: is_active>, <django.db.models.fields.DateTimeField: date_joined>, <django.db.models.fields.related.ManyToManyField: groups>, <django.db.models.fields.related.ManyToManyField: user_permissions>)
>>> User.objects.all() # 현재 있는 user
<QuerySet [<User: admin>, <User: admin2>]>polls/models.pyclass Question(models.Model):
question_text = models.CharField(max_length=200, verbose_name='질문')
pub_date = models.DateTimeField(auto_now_add=True, verbose_name='생성일')
owner = models.ForeignKey('auth.User', related_name='questions', on_delete=models.CASCADE, null=True)
...cmd필드 값 추가한 것 업데이트 (migrate)
(django-venv) C:\Users\mool8\devcourse_week4\mysite>python manage.py makemigrations
Migrations for 'polls':
polls\migrations\0002_alter_question_pub_date_alter_question_question_text.py
- Alter field pub_date on question
- Alter field question_text on question
(django-venv) C:\Users\mool8\devcourse_week4\mysite>python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
Applying polls.0002_alter_question_pub_date_alter_question_question_text... OK
Django Shellchoice를 가져오는 것과 같은 동작
>>> from django.contrib.auth.models import User
>>> from polls.models import *
>>> user = User.objects.first()
>>> user.questions.all()
<QuerySet []>
>>> print(user.questions.all().query)
SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date", "polls_question"."owner_id" FROM "polls_question" WHERE "polls_question"."owner_id" = 1
>>> user.id
1
user.questions.all() ??
vs
question.choice_set.all() ??
question은 FK로써 Choice 모델에 속하기 때문
user는 FK로써 Question 모델에 속한다
- 즉, User에서 Question 모델을 불러올 때 ‘questions’를 사용한다
- related_name으로 지정했기에
class Question(models.Model):
...
owner = models.ForeignKey('auth.User', related_name='questions', on_delete=models.CASCADE, null=True)
class AlbumSerializer(serializers.ModelSerializer): tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True) class Meta: model = Album fields = ['album_name', 'artist', 'tracks']
- Arguments:
queryset- The queryset used for model instance lookups when validating the field input. Relationships must either set a queryset explicitly, or setread_only=True.many- If applied to a to-many relationship, you should set this argument toTrue.allow_null- If set toTrue, the field will accept values ofNoneor the empty string for nullable relationships. Defaults toFalse.pk_field- Set to a field to control serialization/deserialization of the primary key's value. For example,pk_field=UUIDField(format='hex')would serialize a UUID primary key into its compact hex representation.
polls_api/serializers.py
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
# 여러개 의 question을 가지게 된다 (many=True)
questions = serializers.PrimaryKeyRelatedField(many=True, queryset=Question.objects.all())
class Meta:
model = User
fields = ['id', 'username','questions']
polls_api/views.pyfrom django.contrib.auth.models import User
from polls_api.serializers import UserSerializer
class UserList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
polls_api/urls.pyfrom django.urls import path
from .views import *
urlpatterns = [
path('question/', QuestionList.as_view(), name='question-list'),
path('question/<int:pk>/', QuestionDetail.as_view()),
path('users/', UserList.as_view(),name='user-list'),
path('users/<int:pk>/', UserDetail.as_view()),
]

django에서 제공하는 from을 이용해서 회원가입 form 만들기
polls/views.py
회원가입 view, 템플릿과 연결
UserCreationForm: '유저이름'과 '패스워드', '패스워드 확인' 필드를 제공하며 회원가입 폼을 자동으로 생성
객체 생성에 관련된 기능을 제공하는 CreateView를 상속 받는다
reverse_lazy('user-list') : url 패턴의 이름을 통해 url을 역으로 찾아주는 함수
- path('users/', UserList.as_view(), name='user-list')
- 즉, 회원가입에 성공했을 때 'user-list’ 뷰로 리디렉션
```bash
>>> from django.urls import reverse_lazy
>>> reverse_lazy('user-list')
'/rest/users/'
```
from django.views import generic
from django.urls import reverse_lazy
from django.contrib.auth.forms import UserCreationForm
class SignupView(generic.CreateView):
form_class = UserCreationForm
success_url = reverse_lazy('user-list')
template_name = 'registration/signup.html'
polls/templates/registration/signup.html
{{ form.as_p }}: UserCreationForm을 렌더링한 결과를 출력
<h2>회원가입</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">가입하기</button>
</form>
polls/urls.py
from django.urls import path
from . import views
from .views import *
app_name = 'polls'
urlpatterns = [
path('',views.index, name='index'),
path('<int:question_id>/', views.detail, name='detail'),
path('<int:question_id>/vote/', views.vote, name='vote'),
path('<int:question_id>/result/', views.result, name='result'),
path('signup/', SignupView.as_view(), )
]
회원가입 페이지

rest framework에서 제공하는 serializer를 이용해서 회원가입 form 만들기
API 서버로 구현
polls_api/urls.py
from django.urls import path, include
from .views import *
urlpatterns = [
...
path('register/', RegisterUser.as_view())
]
polls_api/serializers.py
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'password']
extra_kwargs = {'password': {'write_only':True}}
polls_api/views.py
class RegisterUser(generics.CreateAPIView):
serializer_class = RegisterSerializer

→ "detail": "Method \"GET\" not allowed."
polls_api/views.pyclass RegisterUser(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = RegisterSerializer 



polls_api/serializers.pyvalidatos = [validate_password] 를 추가함으로써 간단한 password로는 회원가입할 수 없다
from django.contrib.auth.password_validation import validate_password
class RegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only = True, required = True,
validators = [validate_password])
class Meta:
model = User
fields = ['username', 'password']
extra_kwargs = {'password': {'write_only':True}}

polls_api/serializers.pyvalidate() 를 통해 password와 확인용 password가 일치하지 않으면 에러를 발생시킨다
create: password2는 원래 User에 없었으므로 만들어준다
class RegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only = True, required = True,
validators = [validate_password])
password2 = serializers.CharField(write_only = True, required = True)
def validate(self, attrs):
if attrs['password'] != attrs['password2']:
raise serializers.ValidationError({"password":"두 패스워드가 일치하지 않습니다."})
return attrs
def create(self, validated_data):
user = User.objects.create(username=validated_data['username'])
user.set_password(validated_data['password'])
user.save()
return user
class Meta:
model = User
fields = ['username', 'password', 'password2']
extra_kwargs = {'password': {'write_only':True}}



mysite/settings.py로그인, 로그아웃 버튼을 눌렀을 때 리디렉션할 url 지정
from django.urls import reverse_lazy
LOGIN_REDIRECT_URL = reverse_lazy('question-list')
LOGOUT_REDIRECT_URL = reverse_lazy('question-list')
polls_api/urls.pyrest framework가 제공하는 로그인 페이지 연결
from django.urls import path,include
from .views import *
urlpatterns = [
...
path('api-auth/', include('rest_framework.urls'))
]
polls_api/serializers.pyclass QuestionSerializer(serializers.ModelSerializer):
class Meta:
model = Question
fields = ['id', 'question_text', 'pub_date', 'owner'] 
class QuestionSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = Question
fields = ['id', 'question_text', 'pub_date', 'owner']polls_api/views.py사용자가 잘 지정되도록 설정하기
perform_create(): save할 때 owner를 user로 설정하도록
class QuestionList(generics.ListCreateAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
def perform_create(self, serializer):
serializer.save(owner=self.request.user)

로그아웃 한 경우, 질문을 POST(생성)하려고 하면 오류가 난다
→ 로그인된 상태에서만 무언가를 만들 수 있도록 해야한다
class QuestionList(generics.ListCreateAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
# 로그인된 상태에서만 무언가를 만들 수 있게 된다 (Create)
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
# 로그인된 상태에서만 무언가를 만들 수 있게 된다 (Create)
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
→ 로그아웃했을 때는 form이 보이지 않게 된다

polls_api/views.pypermission_classes에 IsOwnerOrReadOnly 를 추가한다
class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
# 로그인된 상태에서만 무언가를 만들 수 있게 된다 (Create)
permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]

class QuestionList(generics.ListCreateAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
# 로그인된 상태에서만 무언가를 만들 수 있게 된다 (Create)
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
QuestionList ← generics.ListCreatieAPIView ← mixins.CreateModelMixin
- ListCreateAPIView 클래스에서는 post라는 메소드가 구현이 되어 있다. 이를 상속받음으로써 따로 QuestionList에서 정의하지 않아도 사용 가능하다
- 따라서 post 입력이 들어오면 ListCreateAPIView에 정의된 post를 실행하고, 그 안에서는 create를 정의하는데 이건 ListCreateAPIView가 상속받은 CreateModelMixin이라는 클래스에 정의된 메소드다.
- 따라서 QuestionList에서 CreateModelMixin에 있는 perform_crate()를 오버라이드해서 사용할 수 있다
그렇다면.. 우리는 아래와 같은 owner는 ReadOnlyField로 정의했는데 어떻게 owner를 지정할 수 있는 것일까?
class QuestionSerializer(serializers.ModelSerializer):
owner = serializers.**ReadOnlyField**(source='owner.username')
>>> from polls_api.serializers import QuestionSerializer
>>> question_serializer = QuestionSerializer(data={"question_text" : "some text", "owner" : "someone"})
>>> question_serializer.is_valid()
True
>>> question_serializer.validated_data
{'question_text': 'some text'}
>>> question = question_serializer.save(id=10000)
>>> question.id
10000
>>> question.question_text
'some text'
POSTMAN은 RESTful API 테스트를 위한 플랫폼으로, 다양한 HTTP 요청을 보내고 응답 결과를 쉽게 확인할 수 있도록 도와줍니다. 또한, API 요청과 응답 결과를 저장하고 공유할 수 있는 기능도 제공합니다.
- API 개발에 많이 사용된다


PUT하는 주체가 수정한 user라는 정보가 없으므로 에러 발생
status code: 403은 접근할 수 없는 대상이라는 것

session: 웹 서버와 브라우저 간의 상호작용을 통해 사용자의 상태 정보를 저장하고 관리한다
로그인된 상태에서는 아래처럼 sessionid가 존재

로그아웃하면 sessionid X
sessionid를 통해서 로그인이 되어있는지 아닌지를 볼 수 있다




polls_api/serializers.pyUserSerializer에 Question을 PrimaryKeyRelatedField로 정의한다
questions의 필드로 포함되어 있는 questions의 PK, 즉 id가 보이게 된다
class UserSerializer(serializers.ModelSerializer):
# 여러개 의 question을 가지게 된다 (many=True)
questions = serializers.PrimaryKeyRelatedField(many=True, queryset=Question.objects.all())
...

polls_api/serializers.pyUserSerializer에 Question을 StringRelatedField로 정의한다
question 모델의 str() 메소드에 정의된 대로 보이게 된다
class UserSerializer(serializers.ModelSerializer):
questions = serializers.StringRelatedField(many=True, read_only=True)
...

polls_api/serializers.pyUserSerializer에 Question을 SlugRelatedField로 정의한다
question에 있는 필드 중 하나를 골라 보여줄 수 있다
slug_field 로 원하는 필드를 지정한다
class UserSerializer(serializers.ModelSerializer):
questions = serializers.SlugRelatedField(many=True, read_only=True, slug_field='pub_date')
...

polls_api/serializers.pyUserSerializer에 Question을 HyperlinkedRelatedField로 정의한다
특정 view로 이동하는 하이퍼링크를 보여준다
view_name 으로 원하는 view를 지정할 수 있다
class UserSerializer(serializers.ModelSerializer):
questions = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='question-detail')
...


polls_api/serializers.py하나의 question는 여러 개의 choice 객체를 가질 수 있다
QuestionSerializer에 ChoiceSerializer를 불러와 보여줄 수 있다
class QuestionSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
choices = ChoiceSerializer(many=True, read_only=True)
class Meta:
model = Question
fields = ['id', 'question_text', 'pub_date', 'owner', 'choices']
