์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- KDT_TIL
- Spark SQL
- Docker
- DataFrame Hint
- Spark Partitioning
- ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ
- Spark ์ค์ต
- Salting
- Kubernetes
- CI/CD
- ๋น ๋ฐ์ดํฐ
- Kafka
- off heap memory
- Airflow
- mysql
- backfill
- colab
- redshift
- etl
- Spark Caching
- Speculative Execution
- aws
- spark executor memory
- SQL
- Spark
- k8s
- disk spill
- topic
- Dag
- AQE
- Today
- Total
JUST DO IT!
[TIL]KDT_20230427 ๋ณธ๋ฌธ
๐ KDT WEEK 4 DAY 4 TIL
- User ์ถ๊ฐ ๋ฐ ๊ด๋ฆฌ
- postman
๐ฅ User
์ ๋ฒ ํ์ต์์ admin์ ์์ฑํ ๋, createsuperuser ๋ผ๋ ๋ช ๋ ์ด๋ฅผ ํตํด shell์์ admin์ ๋ง๋ค ์๊ฐ ์์๋ค.
์ด admin์ ํ๋์ User Model์ ํด๋นํ๋๋ฐ, ์ด๋ django.contrib.auth.models์ ๋ฏธ๋ฆฌ ์ ์๋์ด ์๋ ๋ชจ๋ธ์ด๋ค.
์ด๋ฏธ ๋ชจ๋ธ์ด ์์ผ๋ฏ๋ก, ์ด๋ฅผ Question ๋ชจ๋ธ์ ์ธ๋ํค๋ก ๋ฃ์ด๋ณด์๋ค.
User๋ชจ๋ธ์ด auth์ ์กด์ฌํ๋ฏ๋ก auth.User ํํ๋ก ์์ฑํด์ผ ํ๋ค.
์ฌ๊ธฐ์ related_name='questions' ์ ๊ธฐ๋ฅ์..
์ ๋ฒ์ shell์์ ํ๋์ question ์ค๋ธ์ ํธ์ ํด๋นํ๋ choice๋ค์ ๊ฐ์ ธ์ค๋ ค๋ฉด.. ๋ช ๋ น์ด๋
question.choice_set.all()
์ ์ฌ์ฉํด์ผ ํ๋ค.
ํ์ง๋ง related_name์ ์ ๋ ๊ฒ ์ด๋ฆ์ผ๋ก ์ ์ํด์ฃผ๋ฉด,
user.questions.all()
์ผ๋ก๋ ๊ฐ์ ๋์์ ํ๋ฏ๋ก, _set ๋ถ๋ถ์ ์๋ตํ ์๊ฐ ์๋ค!
์ด๋ ๊ฒ question ๋ชจ๋ธ์ ์์ ํ์์ผ๋ฏ๋ก migration์ ์์ง๋ง์!
User Page
DRF๋ก QuestionList์ QuestionDetail ํ์ด์ง๋ฅผ ๊ตฌํํ๋ ๊ฒ์ฒ๋ผ, User Page๋ ์์ฑํด๋ณด์.
๋ฐฉ๋ฒ๊ณผ ์์๋ ๋์ผํ๋ค. Serializer ์์ฑ โถ views.py ์์ฑ โถ urls.py ์์ฑ
UserSerializer
โถ polls_api/serializers.py
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
questions = serializers.PrimaryKeyRelatedField(many=True, queryset=Question.objects.all())
class Meta:
model = User
fields = ['id', 'username', 'questions']
QuestionSerializer์ ๋ค๋ฅด๊ฒ ํ์ํ ๋ถ๋ถ์ด ํ ์ค ๋ ์๋ค.
UserSerializer์์๋ 'questions' ํ๋๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด questions๋ฅผ ์์์ ์ ์ํด์ค ํ์๊ฐ ์๋ค!
user.questions.all() ์ด๋question.choice_set.all() ์ query๋ฌธ์ ์ดํด๋ณด๋ฉด,
ํญ์ WHERE๋ฌธ์์ ์ธ๋ํค๋ฅผ ์ฌ์ฉํ ๋ชจ๋ธ์์ ์ธ๋ํค๋ฅผ ์ฌ์ฉ๋ฐ์ ๋ชจ๋ธ๋ก id๋ฅผ ์ฐพ์๊ฐ๋๋ก ๋์ด์๋ค.
ex) user.questions.all()์ ๊ฒฝ์ฐ, WHERE "polls_question"."owner_id" = 1
๋ฐ๋ผ์ 'questions' ํ๋๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด์๋ questions๋ฅผ ๋จผ์ ์ ์ํด์ฃผ์ด์ผ ํ๋ค!
UserList, UserDetail Page View
โถ polls_api/views.py
from 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
generics์์ ํ์ํ ๊ธฐ๋ฅ์ ์ ํํ๊ณ ์์๋ฐ์์ค๋ค. ๋จ์ ์ถ๋ ฅ์ด๋ผ๋ฉด ํด๋น APIView๋ง์ผ๋ก๋ ์ถฉ๋ถํ๋ค.
โถ polls_api/urls.py
urlpatterns = [
path('users/', UserList.as_view(),name='user-list'),
path('users/<int:pk>/', UserDetail.as_view()),
]
urls.py์ ํด๋น path์ ์ค์ ํด์ฃผ๋ฉด ๋์ด๋ค.
id๊ฐ ์๋ pk๋ฅผ ์ฌ์ฉํ๋ค๋ ๊ฑธ ์์ง๋ง์.
User ์์ฑํ๊ธฐ
๋ฏธ๋ฆฌ ๋ง๋ค์ด์ ธ ์๋ UserCreationForm๊ณผ Serializer์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๊ฐ๊ฐ ์๊ฐํ๊ฒ ๋ค.
1. Form์ ์ด์ฉํ๋ ๋ฐฉ๋ฒ
โถ polls/views.py
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') # Redirection
template_name = 'registration/signup.html'
SignupView๋ผ๋ ํ์ด์ง๋ฅผ ๋ง๋ค๊ธฐ ์ํด views.py์ ํจ์๋ฅผ ํ๋ ๋ง๋ค์ด์ฃผ์๋ค.
generic์์ ์ง์ํ๋ ์ ์ ์์ฑ Form์ ๊ฐ์ ธ๋ค ์ธ ๊ฒ์ด๋ฏ๋ก, ์์ฑ์ ํ์ํ ๋ณ์๋ค์ ์ ์ธํ์ฌ ์ฌ์ฉํ๋ค.
- UserCreationForm : ์ฅ๊ณ ์์ ์ง์ํ๋ ๊ธฐ๋ณธ ํ์๊ฐ์ ํผ
- reverse_lazy : url์ name์ ํตํด Redirectionํ ๋ ์ฌ์ฉํจ. ๋์ ์๋ฃ ํ ํด๋น name์ url๋ก ์ด๋
์ ํ ํ๋ฆฟ์ผ๋ก ๊ฐ์ ธ์ค๊ฒ ๋ค๊ณ ์ ์ธํ signup.html์ ์์ฑํด๋ณด์.
โถ templates/registration/signup.html
<h2>ํ์๊ฐ์
</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }} <!-- User Creation Form ์๋ -->
<button type="submit">๊ฐ์
ํ๊ธฐ</button>
</form>
{{form.as_p}} ๋ form์ ์ ์ํ ๋ชจ๋ธ ํ๋์ ํด๋นํ๋ html ์ฝ๋๋ฅผ ์๋์ผ๋ก ์์ฑํ๋ค.
โถ polls/urls.py
urlpatterns = [
path('signup/', SignupView.as_view(), )
]
ํด๋น path๋ฅผ ์ถ๊ฐํ์ฌ ํ์๊ฐ์ ํ์ด์ง์ url์ ์์ฑํ๋ค.
์ด ํ์ด์ง์์๋ ํจ์ค์๋์ ์ ํ ์ฌํญ ๋ฑ์ ๊ธฐ๋ฅ๋ ์๋์ผ๋ก ์ง์ํ๋ค.
2. Serializer๋ฅผ ์ด์ฉํ๋ ๋ฐฉ๋ฒ
โถ polls_api/serializers.py
# JSON์ ํตํ API ๊ตฌํ์ ํ๊ณ ์๊ธฐ ๋๋ฌธ์, ์ฌ์ฉ์ ๋ฑ๋ก๋ Serializer๋ฅผ ์ฌ์ฉํ๋๊ฒ ์ข๋ค
# UserSerializer๋ฅผ ์ฌ์ฉํ์ง ์์ ์ด์ ๋ ์๋ ํจ์๋ค์ ๋ชจ๋ ๊ตฌํํด์ผ ํ๊ธฐ ๋๋ฌธ์ด๋ค.
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']
- ๋ช ๊ฐ์ง ํจ์๋ฅผ ์ง์ ๊ตฌํํด์ผ ํ๊ธฐ ๋๋ฌธ์ ์ด๋ฏธ ๊ตฌํ๋ UserSerializer๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ์๋ก ๋ง๋ค์๋ค.
- password์ 2์ฐจ ๊ฒ์ฆ์ ์ํด password2๊น์ง ์ ์ํ๊ณ , validate ํจ์๋ฅผ ํตํด ์ผ์น๊ฒ์ฆ์ ๋์ํ๋ค.
- password2์ ์กด์ฌ๋ก, ๊ธฐ์กด ModelSerializer์ create๊ฐ ์ ๋๋ก ๋์ํ์ง ์์ผ๋ฏ๋ก createํจ์๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉํด์ค๋ค.
โถ polls_api/views.py
from polls_api.serializers import RegisterSerializer
class RegisterUser(generics.CreateAPIView):
serializer_class = RegisterSerializer
views.py์ ๊ตฌํํ RegisterSerializer๋ฅผ ์ฌ์ฉํ์ฌ view๋ฅผ ์ ์ํ๊ณ ,
urls.py์ ์ถ๊ฐํ๋ค.
path('register/', RegisterUser.as_view()),
User Login, Logout
์ถ๊ฐ๋ก ๋ก๊ทธ์ธ, ๋ก๊ทธ์์ ๊ธฐ๋ฅ๊น์ง ๊ตฌํํ๋ ค๋ฉด.. ์ด๊ฒ๋ DRF์์ ์ง์ํ๋ ๊ธฐ๋ฅ์ด ์์ด ๊ฐํธํ๊ฒ ๊ตฌํ์ด ๊ฐ๋ฅํ๋ค.
๋จผ์ urls.py์ ํ๋์ path๋ฅผ ์ถ๊ฐํ๋ค.
path('api-auth/', include('rest_framework.urls'))
rest_framework.urls์ ์ฐพ์๊ฐ์ ๋ค์ฌ๊ฐ๋ณด๋ฉด, ์ด๋ป๊ฒ ๋ก๊ทธ์ธ๊ณผ ๋ก๊ทธ์์์ด ๊ตฌํ๋๋์ง๋ ์ ์ ์๋ค!
๊ทธ๋ฆฌ๊ณ mysite/settings.py์
LOGIN_REDIRECT_URL = reverse_lazy('question-list')
LOGOUT_REDIRECT_URL = reverse_lazy('question-list')
ํด๋น ์ด๋ฆ์ ๋ณ์๋ก ๋ฆฌ๋ค์ด๋ ์ ์ ๊ตฌํํด์ฃผ๋ฉด, ๋ก๊ทธ์ธ๊ณผ ๋ก๊ทธ์์ ์ดํ ํด๋น ํ์ด์ง๋ก ์ด๋ํ๋ค.
์์ฑํ Question์ Owner ํ ๋นํ๊ธฐ
๋ก๊ทธ์ธ์ด ๋ ์ํ์์ ์์ ์์ฑ๋ถ๋ฅผ ํตํด Question ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ฉด, Owner๋ฅผ ํ ๋นํด์ฃผ๋๋ก ๋ง๋ค์ด๋ณธ๋ค.
โถ polls_api/serializers.py
class QuestionSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = Question
fields = ['id', 'question_text', 'pub_date','owner'] # ModelSerializer๋ฅผ ํตํด ์๋์ผ๋ก ์์ฑ
ModelSerializer ๋๋ถ์ owner๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ ์ฝ๋ค.
fields์ owner ์์ฑ์ ์ถ๊ฐํด์ฃผ๊ณ , owner๋ ์์ฑํ ๋ ์ ํํ๊ฑฐ๋ ์ ๋ ฅ๋๋๊ฒ ์๋๋ฏ๋ก Readonly์ต์ ๋ ๋ถ์ฌ์ค๋ค.
โถ polls_api/views.py
class QuestionList(generics.ListCreateAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly] # ๋ก๊ทธ์ธ๋์ด์์ง ์์ผ๋ฉด readonly
def perform_create(self, serializer):
serializer.save(owner=self.request.user) # ์ด๋ฏธ ๋ก๊ทธ์ธ๋ ์ฌ์ฉ์๋ฅผ ์ง์ ํจ, owner๊ฐ readonly์ฌ๋ .save() ๋์์๋ ๋ฌด์์ด๋ ๋ค ๋จ
perform_createํจ์๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉํด์, ํ์ฌ ๋ก๊ทธ์ธ๋ user๊ฐ owner๊ฐ ๋๋๋ก ๊ตฌํํด์ค๋ค.
์ค์ํ ์ ์, .save()๋ readonly ์ต์ ์ ๋ฌด์ํ๊ณ ๋ค์ด์จ ๊ฐ์ ํ ๋นํด์ค ๋ค๋ ๊ฒ์ด๋ค. id๋ ๊ฐ๋ฅํ๋ค!
๋ํ ๋ก๊ทธ์์ ์ํ์ผ ๋, ์ฆ user ์ ๋ณด๊ฐ ์์ ๋๋ ์ ๋ ฅ๋ฐ์ง ์์์ผ ํ๋ฏ๋ก permission_classes๋ฅผ ์์ฒ๋ผ ์ ์ํด์ค๋ค.
์ถ๊ฐ๋ก user๋ ์๊ธฐ๊ฐ ๋ง๋ question๋ง ์์ ์ด ๊ฐ๋ฅํด์ผ ํ ๊ฒ์ด๋ค.
๋ฐ๋ผ์ permissions.py๋ฅผ ๋ง๋ค์ด ์ ์ํด์ค๋ค.
โถ polls_api/permissions.py
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS: # SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS') read_only๋ ํญ์ ํ์ฉ
return True
return obj.owner == request.user # ์ค๋ธ์ ํธ์ ์์ ์๊ฐ ์์ฒญํ ์ฌ์ฉ์ ์ธ๊ฐ? ๋ง์ผ๋ฉด True
์ด๋ ๊ฒ ๋ฐ๋ก permission.py๋ฅผ ๋ง๋ค์ด์ ๋ด๊ฐ ํ์ํ๋ ๊ธฐ๋ฅ์ ํจ์๋ก ๊ตฌํํด๋๊ณ ..
โถ polls_api/views.py
class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]
permission์ ์ฌ์ฉํ๋ views.py์ ํด๋น ํ์ด์ง permission_classes์ ์์ฒ๋ผ ๊ตฌํํ ํจ์๋ฅผ ์ถ๊ฐํด์ฃผ๋ฉด ๋๋ค!
๐ฆ ๊ฐ๋จํ postman ์ฌ์ฉํ๊ธฐ
์์์ ๊ตฌํํ permission์ฒ๋ผ ์ ํ์ ์ธ ์ํฉ์์๋ง CRUD๋ฑ์ ๊ธฐ๋ฅ์ด ๊ฐ๋ฅํ๋๋ก ๋ง๋ค์๋ค๋ฉด,
๋ค๋ฅธ ์ํฉ์์ CRUD๋ฅผ ์์ฒญํ์ ๋ ์ด๋ค ์๋ต์ด ๋์ค๋์ง ์ฅ๊ณ ์ฌ์ดํธ๋ง์ผ๋ก๋ ์๊ธฐ๊ฐ ํ๋ค๋ค.
์ด๋ฐ ๋ค์ํ ์ํฉ์ ํ์ธํ๊ธฐ ์ํด ์ฌ์ดํธ์ ํน์ ์์ฒญ์ ๋ณด๋ด๊ณ ์๋ต์ ๋ฐ์๋ณผ ์ ์๋ Postman ํ๋ซํผ์ ์ฌ์ฉํด๋ณธ๋ค.
PUT์ ์ฌ์ฉํด question_id = 12 ํด๋นํ๋ ๊ฐ์ฒด์ update ์์ฒญ์ ๋ณด๋ด๋ณด๋ฉด..
์ญ์ ๋ก๊ทธ์ธ์ด ๋์ด์์ง ์์ ์ค๋ฅ๊ฐ ๋ํ๋๋ค.
๊ทธ๋ฌ๋ฉด Postman์์ ๋ก๊ทธ์ธ ์ ๋ณด๋ฅผ ๋๊ธฐ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น?
์ฌ์ดํธ์ ๊ฐ๋ฐ์๋๊ตฌ>Applicationํญ>Cookies๋ฅผ ํ์ธํด๋ณด๋ฉด ํ์ํ name๊ณผ value๋ฅผ ์ ์ ์๋ค.
sessionid๊ฐ ๋ก๊ทธ์ธ์ ๋ณด, csrftoken๋ ๋์จ๋ค.
์ด๊ฑธ Postman์ ๋ฃ์ด ์ฌ์๋ํด๋ณด์.
csrftoken์ ์์ฒ๋ผ ๋๋ฒ ์ฌ์ฉํ๊ฒ ๋๋ค.
'TIL' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[TIL]KDT_20230501 (0) | 2023.05.01 |
---|---|
[TIL]KDT_20230428 (0) | 2023.04.28 |
[TIL]KDT_20230426 (0) | 2023.04.26 |
[TIL]KDT_20230425 (0) | 2023.04.25 |
[TIL]KDT_20230424 (0) | 2023.04.24 |