apscheduler

: Django 프로젝트를 진행하며 스케쥴링이 필요하여 사용하게 되었다. 초기 crontab을 찾아보았으나, windows에서 사용불가능 하여 apscheuler를 활용하였다.

  • 설치
pip install django-apscheduler
  • settings.py
    : apscheduler를 추가한다.
INSTALLED_APP = [
    'django_apscheduler',
]
  • 적용하기
from apscheduler.schedulers.background import BackgroundScheduler
scheduler = BackgroundScheduler()

@scheduler.scheduled_job('cron',second='*/5')
def function01():
    ...

scheduler.start()

출처: https://velog.io/@lim1231/Django-Scheduler

'Python > Django' 카테고리의 다른 글

Django - User  (0) 2024.03.15
Django - Serializers, CRUD  (0) 2024.03.15
Django - View, template, form  (0) 2024.03.15
Django - 설치 및 기본 세팅  (2) 2024.03.15

User

Model의 Class에 소유권 추가하기

: Model의 각 클래스에 소유권을 추가하고 싶은 경우에 사용한다. 예제에서는 각 질문에 대하여 소유자를 확인 하도록 하는 필드를 생성하였다.

  • 외래키로 auth.User를 사용
    • user_id를 통해서 question을 가져올 수 있도록 하기위해 related_name을 사용해준다.
    • user가 제거되는 경우 question도 같이 제거되어야 하기 때문에 CASCADE를 적용한다.
owner = models.ForeignKey('auth.User',related_name='questions',on_delete=models.CASCADE, null =True)

사용자 Serializer 제작.

: User를 활용하여 다양한 동작을 하기 위해서는 통신이 필요하다. 이 때 사용되는 Serializer를 생성하고 View에 등록해준다.

# serializer.py
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
    # user의 pk를 통해서 여러개의 questions를 갖는다는 것을 명시.
    # question을 통해서 가져오기 때문에 다음처럼 사용
    questions = serializers.PrimaryKeyRelatedField(many=True, queryset=Question.objects.all())
    
    class Meta:
        model =User
        fields = ['id','username','questions']

#views.py
class UserList(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

 

💡 PrimaryKeyRelatedField에 대한 이해 : PrimaryKeyRelatedField는 기본 키를 사용하여 관계의 대상을 나타낼 수 있다. 이에 예시는 아래 코드와 같다.

class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

# 결과
{
    'album_name': 'Undun',
    'artist': 'The Roots',
    'tracks': [
        89,
        90,
        91,
        ...
    ]
}

참고 : https://www.django-rest-framework.org/api-guide/serializers/ (Django REST framework api guide)

User의 생성 - Form을 사용

: from django.contrib.auth.forms import UserCreationForm 을 활용하여 구현해준다.

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
# reverse_lazy를 사용하여 다른 Path로 전송시킬 수 있다.
    success_url = reverse_lazy('user-list')
    template_name = 'registration/signup.html'

User의 생성 - Serializer을 사용

1. serializers.py에 Serializer 클래스를 생성한다.

class RegisterSerializer(serializers.ModelSerializer):
	class Meta:
		model = User
		fields = ['username','password']
		extra_kwargs = {'password' : {'write_only':True}}
	

 

2. Views.py에 view생성해주기

from polls_api.serializers import RegisterSerializer

class RegisterUser(generics.CreateAPIView):
    serializer_class = RegisterSerializer

 

3. urls에 등록하기

path('register/', RegisterUser.as_view()),
  • Password-validatior 만들기
# 간단한 패스워드 막기
from django.contrib.auth.password_validation import validate_password

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
    
# 정상적으로 패스워드가 동일한 경우에 save를 해준다.
    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']

권한 관리

: Question에 owner 필드를 넣어주었지만, 여전히 로그인 하지 않은경우, 소유자가 아닌 다른 계정의 경우에도 편집이 가능하다. 따라서 적절한 권한부여를 통해 이를 막아야한다.

  • api-auth path추가하기
# urls.py
path('api-auth/', include('rest_framework.urls'))
  • 로그인과 로그아웃 시 연결될 페이지를 설정하기
# settings.py에서 작업한다.
from django.urls import reverse_lazy
LOGIN_REDIRECT_URL = reverse_lazy('question-list')
LOGOUT_REDIRECT_URL = reverse_lazy('question-list')
  • QuestionSerializer를 변경하여 question 생성 시에 소유자 필드를 무조건 현재 로그인된 사용자로 변경
class QuestionSerializer(serializers.ModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')

    class Meta:
        model = Question
        fields = ['id', 'question_text', 'pub_date', 'owner']
  • 로그인 한 사용자만 수정하게 만드는 경우 rest_framework의 permission를 활용한다.
# Views.py
from rest_framework import generics,permissions
class QuestionList(generics.ListCreateAPIView):
	...
	permission_classes = [permissions.IsAuthenticatedOrReadOnly]
	def perform_create(self, serializer):
				# save 요청시에는 어떤 값이는 문제 없이 넣을 수 있다.
        serializer.save(owner=self.request.user) 
  • 추가로 Owner == user인 경우에만 API 요청이 가능하도록 하려면 다음과 같이 permission 코드를 작성한다.
from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True
        
        return obj.owner == request.user

 

request.method가 SAFE_METHODS ('GET', 'HEAD', 'OPTIONS')에 포함되거나,
obj의 user가 로그인 된 user와 같으면 API의 요청 결과를 볼 수 있다.

'Python > Django' 카테고리의 다른 글

Django - apscheduler  (0) 2024.03.15
Django - Serializers, CRUD  (0) 2024.03.15
Django - View, template, form  (0) 2024.03.15
Django - 설치 및 기본 세팅  (2) 2024.03.15

Serializers

Serialize (직렬화) ↔ Deserialize

: 객체를 연속적인 데이터로 변환하는 것. JSON등의 데이터로 변환한다.

  • 시리얼라이저 생성 시에 ModelSerializer를 통해 생성해주면 create, update 메서드의 별도 생성없이 구현 가능합니다.
# polls_api/serializers.py
from rest_framework import serializers
from polls.models import Question

class QuestionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Question
        fields = ['id','question_text','pub_date','is_something','average_score','json_field']

CRUD 

CRUD: Create(생성), Read(읽기), Update(갱신), Delete(삭제)

Django의 CRUD는 직접 구현하는 방법도 있으나, Mixin,generic과 같은 보다 편리한 메서드를 활용하여 구현도 가능하다.

API_VIEW를 활용한 CRUD

다음은 간단한 GET 메서드를 구현하는 방법이다.

from rest_framework.decorators import api_view # 메소드 정의 시 사용
# 만약 여러 메서드를 사용하고 싶은 경우 @api_view(['GET','POST']) 다음과 같이 리스트로 넣어준다.
@api_view()
def question_list(request):
    questions = Question.objects.all()
    serializer = QuestionSerializer(questions ,many = True) # 여러개의 인스턴스를 줄 때에 many를 사용
    return Response(serializer.data)

Generic

이런 CRUD를 이미 구현하여 보다 쉽고 코드를 간결하게 해준다. rest_framework의 generics를 활용하는 방식이다.

from rest_framework import generics

class QuestionList(generics.ListCreateAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer

class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer

이렇게 적어주기만해도 CRUD가 모두 정상 작동한다.
-> generics에서 이미 구현해두었기 때문이다. 또한 각 기능을 별도로 설정 가능하다.
'RetrieveUpdateDestroyAPIView'에서 Destroy만 빼면 제거 기능만 제외한 설정이 가능하다.

에러 참고

초기 수행과정에서 rest_framework template가 정상 작동하지 않는 경우가 있었다. 다음 블로그를 참고하여 해결하였다.
https://velog.io/@ssongji/Django-API-기본-페이지-표출-시-오류-TemplateDoesNotExist-at-restquestion

'Python > Django' 카테고리의 다른 글

Django - apscheduler  (0) 2024.03.15
Django - User  (0) 2024.03.15
Django - View, template, form  (0) 2024.03.15
Django - 설치 및 기본 세팅  (2) 2024.03.15

뷰와 템플릿

뷰를 이용해서 HTML코드를 렌더링하기

  1. from django.shortcuts import render 를 추가한다 (/polls/views.py)
  2. HTML파일을 생성한다.
  3. views.py의 함수 ⇒ 페이지에 반환 값을 변경한다.
context = {'questions':latest_question_list}
return render(request, 'polls/index.html',context)

인자는 context처럼 넘겨줄 수 있는데 이 때 HTML에서는 넘겨받은 인자를 {{args}} 처럼 사용한다.

{{question}}

제어문 사용하기

인자가 여러개인 경우 1개를 출력하고자 할 때는 .index의 형태로 사용한다.

{{question.0}}
{{question.1}}
{{question.2}}
  • 반복문으로의 활용은 다음과 같다.
 {% for q in questions%}
    <li>{{q}}</li>
 {% endfor %}
  • IF문은 다음과 같다.
{% if questions %}
<ul>
    ...
</ul>
{% else %}
<p>no questions</p>
{% endif %}

Detail 설정하기

Path에 숫자를 넣어서 그 값을 인식시켜 페이지를 수정하고자 한다.

  1. /polls/urls.py에서 path를 추가한다.
# 수를 입력받아서 question_id로 사용한다.
path('<int:question_id>',views.detail, name = 'detail'),
  1. /polls/views.py에서 render하는 메서드 생성하기
def detail(request,question_id):
    question = Question.objects.get(pk=question_id)
    return render(request, 'polls/detail.html',{'question':question})
  1. detail.html생성 : 인자들을 넘겨 받아서 간단하게 다음과 같이 생성하였다.
# detail.html
<h1>
    {{question.question_text}}
</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{choice.choice_text}}</li>

{% endfor %}

링크를 사용해서 페이지 이동.

  • 태그를 활용하여 이동시키기 -> a태그 사용
  • question.id를 활용해서 href 링크를 걸어주면 된다.
{% for q in questions%}
    <li><a href="/polls/{{q.id}}">{{q.question_text}}</li>
{% endfor %}
  • 코드를 통해서 얻어오는 방법은 urlpattern의 name을 통해서 가져올 수가 있다.
# urls.py
app_name = 'polls' # app_name을 지정한 경우 app_name:name형식으로 불러와야한다.
urlpatterns = [
    path("", views.index, name = 'index'),
    path('<int:question_id>',views.detail, name = 'detail'),
    path("some_url", views.some_url),
]

# index.html
{% for q in questions%}
        <li><a href="{% url 'polls:detail' q.id %}">{{ q.question_text }}</a></li>
{% endfor %}

에러 처리하기

try,except 구문을 사용해도 가능하나 Django에서는 Shortcut을 통해 404 에러메세지를 제공한다.

from django.shortcuts import render,get_object_or_404
# 기존
question = Question.objects.get(pk=question_id)
# 변경
question = get_object_or_404(Question, pk =question_id)

폼과 커스터마이징

: 값을 제출할 용도로 사용한다.

폼을 추가하는 순서에 대해서 간단하게 설명한다.

  1. path를 추가하기. 값이 보내져서 path값이 변경되는 경우 추가해주어야 한다.
# urls.py
path('<int:question_id>/vote/', views.vote, name ='vote'),
  1. views에 메서드 생성
    여기서 주목할 점은 입력받은 값을 통해서 db와 연결하는 부분이다.
    다음 처럼 request.POST를 활용해서 name이 choice인 값을 받을 수 있다.
selected_choice = question.choice_set.get(pk = request.POST['choice'])

# detail.html에서의 name
<input type = 'radio' **name = "choice"**, id = "choice{{forloop.counter}}" value="{{choice.id}}">

# 서버에 값을 저장시키고 싶을 때에는 save를 사용한다.
selected_choice.save()
  1. form 제작하기
    : 아까 제작해두었던 vote path로 값을 post 한다.
<form action="{% url 'polls:vote' question.id %}" method ='post'>

두개의 서버에서 요청하는 경우

F 메서드를 이용해서 DB에서 값을 받고 다시 수행한다.

from django.db.models import F
selected_choice.votes = F('votes') + 1

어드민 페이지 커스터마이징

  • admin.py에서 수정한다.
  • 값을 읽기전용으로만 하고싶은 경우에는 다음처럼 readonly_fields를 걸어준다.
# admin.py
class QuestionAdmin(admin.ModelAdmin): 
    fieldsets = [
        ('질문 섹션',{'fields':['question_text']}),
        ('생성일',{'fields':['pub_date']}),
    ]
    readonly_fields = ['pub_date']
  • question에서 choice option을 관리하는 방법 만들기.
class ChoiceInLine(admin.TabularInline):
    model = Choice
    extra = 3

class QuestionAdmin(admin.ModelAdmin): 
    fieldsets = [
        ('질문 섹션',{'fields':['question_text']}),
        ('생성일',{'fields':['pub_date']}),
    ]
    readonly_fields = ['pub_date']
    inlines = [ChoiceInLine]

  • 숨김처리는 클래스 값을 추가하여 구현한다.
('생성일',{'fields':['pub_date'],'classes':['collapse']}), 

목록을 보기 좋게 만들기.

  • admin에서 목록에 column추가하기 : list_display = ('question_text','pub_date') 이렇게 한줄 추가하면된다.

  • 컬럼의 레이블을 바꿔주고싶으면 verbose_name을 추가한다.
# models.py
class Question(models.Model):
    question_text = models.CharField(max_length=200 , verbose_name='질문')
    pub_date = models.DateTimeField(auto_now_add=True, verbose_name='생성일')

# class로 정의 한 값인 경우
from django.contrib import admin
@admin.display(boolaen=True,description='최근 생성 (하루기준)') # boolean을 하면 아이콘으로 표시된다.
  • 검색 기능

리스트 필터는 list_filter = ['pub_date'] 을 추가하여 사용한다. → 장고가 타입에 맞춰서 옵션들을 제공한다.

검색 필드는 search_fields = ['question_text', 'choice__choice_text'] 를 추가한다. → 리스트를 활용하여 옵션들을 넣을 수 있다.

'Python > Django' 카테고리의 다른 글

Django - apscheduler  (0) 2024.03.15
Django - User  (0) 2024.03.15
Django - Serializers, CRUD  (0) 2024.03.15
Django - 설치 및 기본 세팅  (2) 2024.03.15

+ Recent posts