DevCourse TIL Day2 Week4

김태준·2023년 4월 25일
0

Data Enginnering DevCourse

목록 보기
13/93
post-thumbnail

🎈 View & Templates

장고에서는 모델(Question, Choice)를 통해 DB 정보를 저장하고 읽어오면,

  • View : 앞서 읽어온 정보를 활용!
  • Template : html을 활용하여 데이터를 웹 사이트에 잘 표시하도록 도와주는 도구!

template활용을 위해선, 만들어 놓은 app에 새 폴더를 만들고 views.py에 있는 함수명과 동일한 이름을 가진 html 파일을 생성해 해당 내용을 작성해주고, views.py에는 return에 해당 html을 작성해 처리해준다.

💡 Template에서의 제어문 활용

html에선 반복문을 다음과 같이 %를 활용하여 나타낸다.

<ul>
<!-- 반복문, 조건문 등의 제어문은 중괄호 1개 -->
{% for question in questions %}
  		<!-- 변수 내용 html에 입력할 경우 중괄호 2개 -->
        <li>{{question}}</li>
    {% endfor %}
</ul>
{% else %}
<p>no questions</p>
{% endif %}

💡 detail page 만들기

웹 상에서 클릭 후 연결되는 페이지 만드는 방법!

  • detail.html template을 만들어주고 views.py에서 detail함수를 생성해준다.
  • app/숫자 에 형태로 path가 들어오는 경우 처리해주는 코드를 urls.py에서 작성해준다.
    < 예시는 다음과 같다 >
<!-- detail.html -->
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
# app/views.py
# 특정 Question 오브젝트를 가져오기 위한 과정이 필요
def detail(request, question_id):
	# Question모델에서 id 값이 detail 뷰의 인자로 전달된 question_id와 일치하는 객체가져와 변수로 저장
    question = Question.objects.get(pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})
# app/urls.py
urlpatterns = [
    path('', views.index, name='index'),
    path('some_url', views.some_url),
    path('<int:question_id>/', views.detail, name='detail'),
]

💡 detail 페이지에 링크 다는 방법

앞서 만든 detail 페이지에 링크 다는 방법!
즉, template에서 a태그(url태그)를 통해서 링크를 거는 방법이다.

<!-- templates/index.html -->
{% if questions %}
<ul>
    {% for question in questions %}
  		<!-- url app_name:name -->
    	<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>       
    {% endfor %}
<ul>
{% else %}
<p>no questions</p>
{% endif %}
# app_name/urls.py
from django.urls import path 
from . import views  

app_name = 'polls'

urlpatterns = [     
    path('', views.index, name='index'),
    path('some_url', views.some_url), 
    path('<int:question_id>/', views.detail, name='detail'),     
]

만일, app_name 을 수정한다면 다음과 같이 수정한다.
urls.py
app_name = 'questions'
path( , name = 'question_detail')

index.html
url 불러올 때 : url 'questions:qustion_detail'

🎈 404 Error 해결하기

간혹 url 이동 시 404 page not found를 발견할 수 있다.
이를 처리하는 방법!`

from models.py import *
from django.http import HttpResponse
from django.http import Http404
from django.shortcuts import render , get_object_or_404
...
def detail(request, question_id):
    """
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    """
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})

🎈 Forms (Error 방어 포함한)

앞서 만든 question, choice 클래스를 활용해 목록이 아닌 선택 가능한 form 형태로 만드는 방법
마찬가지로 appname/views.py , appname/urls.py, template/detail.html에서 숫정이 필요하다.

<!-- 폼 생성 -->
<form action = "{% url 'polls:vote' question.id %}" method = 'post'>
    <!-- token : 서버에서 그려준 폼에서만 값 제출 가능토록 함 (방어용)  -->
    {% csrf_token %}
    <h1>{{ question.question_text }}</h1>
    <!-- 선택하지 않은 부분에 대해 예외 처리 -->
    {% if error_message %}
    <p><strong>{{ error_message }}</strong></p>
    {% endif %}
    <!-- 선택 옵션으로 변경 -->
    {% for choice in question.choice_set.all %}
        <input type = 'radio' name = 'choice' id = 'choice{{ forloop.counter }}' value = '{{choice.id}}'>
        <label for="choice{{ forloop.counter }}">
            {{ choice.choice_text }} 
        </label>
        <br>
    {% endfor %}
    <input type = 'submit' value = 'Vote'>
</form> 
# appname/views.py
# Form에서 views.vote 처리해주기
from django.http import HttpResponseRedirect
from django.db.models import F
from django.urls import reverse

def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    # 사용자가 입력한 값: Form에서 입력한 input의 name = choice
    try:
        selected_choice = question.choice_set.get(pk=request.POST['choice'])
    # 에러 뜨면 아래로 처리
    except (KeyError, Choice.DoesNotExist):
        return render(request, 'polls/detail.html', {'question': question, 'error_message': '선택이 없습니다.'})
        # 제출 버튼누르려는 과정에서 삭제하는 문제 시
        return render(request, 'polls/detail.html', {'question': question, 'error_message': f"선택이 없습니다. id={request.POST['choice']}"})
    # 아닌 경우 DB에 해당 결과 저장
    else:
        # 같은 DB 공유하지만 서버가 다를 때 동시 진행 시 VOTES 수에 문제 발생
        # A서버에서도 Votes = 1
        # B서버에서도 Votes = 1 
        # 아래와 같이 문제 발생
        selected_choice.votes += 1
        # 아래로 수정 必
        selected_choice.votes = F('votes') + 1
        selected_choice.save()
        # 모든 처리 완료 후 유저에게 페이지 보내기 (전체목록으로 다시 돌아가기)
        return HttpResponseRedirect(reverse('polls:index'))


# appname/urls.py에 views.note에서 입력 처리하는 path 추가해주기
path('<int:question_id>/vote/', views.vote, name='vote')

🎈 Form 결과 조회 페이지 생성

views.py에 result 함수 생성!

# appname/views.py
from django.shortcuts import get_object_or_404, render
def vote(request, question_id):
...
    else:
        selected_choice.votes = F('votes') + 1
        selected_choice.save()
        **return HttpResponseRedirect(reverse('polls:result', args=(question.id,)))**
# form 결과 페이지 생성
def result(request, question_id):
    question = get_object_or_404(Question, pk = question_id)
    return render(request, 'polls/result.html', {'question':question})
# urls.py에 result 페이지 path 추가
path('<int:question_id>/result/', views.result, name='result')
<!-- appname/result.html 생성 -->
<!-- 각 choice 별 답변과 몇개가 선택되었는지 label로 결과 띄워주기 -->
<h1>{{ question.question_text }}</h1><br>
{% for choice in question.choice_set.all %}

    <label>
        {{ choice.choice_text }} -- {{ choice.votes }}
    </label>
    <br>
{% endfor %}

✍️ Function - Parameter

  • *args : Python 함수에서 사용되는 파라미터(parameter)로, 함수가 호출되고 여러 개의 인자(argument)를 입력받는 상황에서 유연성을 높여주는 기능을 제공합니다. (리스트 활용)
  • **kwargs : Python 함수에서 사용되는 파라미터(parameter)로, kwargs에서 분리된 key값과 value값을 원하는 형식에 넣어 반복문으로 출력합니다. (딕셔너리 활용)

🎈 django admin의 편집 페이지 커스터마이징

django admin에 모델을 추가하기만 하면 모델 내용에 대한 CRUD가 가능한 페이지를 생성할 수 있는 장점이 있다. (CRUD : CREATE, READ, UPDATE, DELETE)

# appname/admin.py

# ADMIN 편집 페이지
# django admin에 모델을 추가하기만 하면 모델 내용에 대해
# CRUD가 가능한 페이지를 생성할 수 있는 장점이 있다.
from .models import Choice, Question

admin.site.register(Choice)

# Choice가 Question내에서 작동하도록 생성
# StackedInline : 각 choice 결과가 상하로 표시
# TabularInline : 각 choice 결과가 좌우로 표시 
class ChoiceInline(admin.TabularInline):
    model = Choice
    # default로 3개 추가해서 표현
    extra = 3

class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        ('질문 섹션', {'fields': ['question_text']}),
        # 숨기고싶은 필드에 classes 지정 
        ('생성일', {'fields': ['pub_date'], 'classes': ['collapse']}),        
    ]
    # 편집 시 수정 X
    readonly_fields = ['pub_date']
    # 앞서 만든 ChoiceInline 추가해 question, choice를 함께 편집가능
    inlines = [ChoiceInline]

admin.site.register(Question, QuestionAdmin)

🎈 django admin의 목록 페이지(question) 커스터마이징

model에서 한 컬럼은 제목, 한 컬럼은 날짜가 나오도록 변경해주는 방법
admin.py와 model.py의 수정작업이 필요하다.

# appname/admin.py
from django.contrib import admin
from .models import Choice, Question

admin.site.register(Choice)

class ChoiceInline(admin.TabularInline):
    model = Choice
    extra = 3


class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        ('질문 섹션', {'fields': ['question_text']}),
        ('생성일', {'fields': ['pub_date'], 'classes': ['collapse']}),        
    ]
    # 보여주고 싶은 컬럼 지정 가능
    list_display = ('question_text', 'pub_date', 'was_published_recently')
    readonly_fields = ['pub_date']
    inlines = [ChoiceInline]
    # 날짜 기준 filter 적용 가능!
    list_filter = ['pub_date']
    # 특정 단어로 search도 가능!
    # (question_text는 question옵션만 가능), choice__choice_text로 choice옵션도 검색으로 찾기 
    search_fields = ['question_text', 'choice__choice_text']
    
admin.site.register(Question, QuestionAdmin)


# appname/models.py
# 보여지는 컬럼명 수정 원하면 model에서 변경 필요!!
import datetime
from django.db import models
from django.utils import timezone
from django.contrib import admin

class Question(models.Model):
	# verbose_name 으로 컬럼명 지정
    question_text = models.CharField(max_length=200, verbose='질문')
    pub_date = models.DateTimeField(auto_now_add=True, verbose='생성일') 
    # 모델에 정의된 field가 아니고 모듈이기에 admin decorator해주기
    # boolean = True : 아이콘 생성 가능
    @admin.display(boolean=True, description='최근생성(하루기준)')
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
    def __str__(self):
        return f'제목: {self.question_text}, 날짜: {self.pub_date}

🎇 정리

어제에 이어 오늘도 장고에 대해 알아보았다.
특히 오늘은 뷰, 탬플릿, 커스터마이징 등 어제 학습한 부분에 대해 좀 더 깔끔하게 웹사이트를 구축하는 방식에 대해 살펴보았고, 이를 통해 url, model,views 등 사용방식과 연관성에 대해서도 알게 되었다.
확실히 어제보다 더 수월하게 배워가는 느낌..!
그러나, 주말에 시간투자를 좀 해서 이번 주차를 무조건 한번 다시 복습 해야할 듯 하다....

profile
To be a DataScientist

0개의 댓글