Day 12

TIL - Django

๐Ÿ“‹ย ๊ณต๋ถ€ ๋‚ด์šฉ

Django์˜ ๋””์ž์ธ ํŒจํ„ด

MVC

๊ตฌ์„ฑ ์š”์†Œ๋ฅผ Model-View-Controller๋กœ ๊ตฌ๋ถ„ํ•˜๋Š” ๋””์ž์ธ ํŒจํ„ด
๊ฐ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋‹ค๋ฅธ ์š”์†Œ์—๊ฒŒ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š์•„์•ผ ํ•จ

Model

  • ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ–๊ณ , ์ฒ˜๋ฆฌํ•˜๋Š” ๋กœ์ง์„ ๊ฐ€์ง

View

  • ์š”์ฒญ์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ๋ฅผ ํ™”๋ฉด์— ๋ณด์—ฌ์ฃผ๋Š” ์—ญํ• . ์œ ์ €์™€์˜ ์ธํ„ฐํŽ˜์ด์Šค

Controller

  • Model๊ณผ View๋ฅผ ์ด์–ด์ฃผ๋Š” ์—ญํ• 

MTV

Model-Template-View MVC ํŒจํ„ด์— ๋Œ€์‘๋˜๋Š” Django์˜ ๋””์ž์ธ ํŒจํ„ด

Model

  • DB์— ์ €์žฅ๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์˜๋ฏธํ•˜๋ฉฐ, ํด๋ž˜์Šค ํ•˜๋‚˜๊ฐ€ table ํ•˜๋‚˜์— ๋Œ€์‘๋จ

Template

  • MVC ํŒจํ„ด์˜ View ์—ญํ• ์œผ๋กœ, ์œ ์ €์—๊ฒŒ ๋ณด์—ฌ์ง€๋Š” ํ™”๋ฉด์„ ์˜๋ฏธ

View

  • MVC ํŒจํ„ด์˜ Controller ์—ญํ• ์œผ๋กœ, ์š”์ฒญ์— ๋”ฐ๋ฅธ ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜๋ฉฐ ๊ฒฐ๊ณผ๋ฅผ Template์œผ๋กœ ๋ Œ๋”๋งํ•˜๋ฉฐ ์‘๋‹ตํ•˜๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›์Œ

+URLConf

  • URL pattern์„ ์ •์˜ํ•˜๊ณ  URL๊ณผ View๋ฅผ ๋งคํ•‘ํ•จ

View

  • polls/views.py

  • import

1
2
3
4
5
from .models import *
from django.http import HttpResponse, HttpResponseRedirect #, Http404
from django.shortcuts import render, get_object_or_404
from django.urls import reverse
from django.db.models import F
  • index page
1
2
3
4
5
6
7
8
9
# ์›น ์„œ๋ฒ„๋Š” ์š”์ฒญ์— ์‘๋‹ตํ•˜๋Š” ์—ญํ• ์ด๊ธฐ ๋•Œ๋ฌธ์—, ์š”์ฒญ์„ ๋ฐ›๊ณ  ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ธฐ๋Šฅ์ด ํ•„์š”
def index(request):
    # '-pub_date' : pub_date ๊ธฐ์ค€ "์—ญ์ˆœ"์œผ๋กœ
    latest_question_list = Question.objects.order_by("-pub_date")[:5]

    # context = {"{Template ๋ณ€์ˆ˜(?) ์ด๋ฆ„}": ๊ฐ’}
    context = {"questions": latest_question_list}
    # request์— ๋Œ€ํ•ด context๋ฅผ ๋„˜๊ฒจ 'polls/index.html' template๋ฅผ ๋ Œ๋”๋งํ•œ๋‹ค.
    return render(request, "polls/index.html", context)
  • detail page
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def detail(request, question_id):
    """ Http404๋ฅผ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    """
    # Django์—์„œ ์ œ๊ณตํ•˜๋Š” 404 Shortcut
    question = get_object_or_404(Question, pk=question_id)
    return render(request, "polls/detail.html", {"question": question})
  • vote page
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    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": f"์„ ํƒ์ด ์—†์Šต๋‹ˆ๋‹ค. id={request.POST['choice']}",
            },
        )
    else:
        # ๋™์‹œ์— ๋‘ ์„œ๋ฒ„์—์„œ ๊ฐ™์€ ์„ ํƒ์„ ํ•œ ๊ฒฝ์šฐ, ๊ฐ’์„ ์ œ๋Œ€๋กœ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•ด
        # db์—์„œ ๊ฐ’์„ ์ฝ์–ด ๊ณ„์‚ฐ ์ง„ํ–‰
        selected_choice.votes = F("votes") + 1  # F : django db
        selected_choice.save()
    # vote template๋ฅผ renderํ•˜์ง€ ์•Š๊ณ , result page๋กœ redirect
    return HttpResponseRedirect(reverse("polls:result", args=(question_id,)))
  • result page
1
2
3
def result(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, "polls/result.html", {"question": question})

Template

  • polls/templates/polls/ ํด๋” ๋‚ด์— .html ํŒŒ์ผ์œผ๋กœ ์ž‘์„ฑ

  • index.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{% if questions %}
<ul>
	{% for question in questions %}
	<li>
		<a href="{% url 'polls:detail' question.id %}">
			{{question.question_text}}
		</a>
	</li>
	{% endfor %}
</ul>
{% else %}
<p>no questions</p>
{% endif %} {% comment %} template์—์„œ๋Š” ์ธ๋ฑ์‹ฑ์— . ์‚ฌ์šฉ questions[0] : X
questions.0 : O appname ์„ค์ •ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ : url 'detail' appname ์„ค์ •ํ•œ ๊ฒฝ์šฐ :
url 'polls:detail' '{app_name : name}' in urls.py {% endcomment %}
  • detail.html : Using form to get input from User
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<form action={% url 'polls:vote' question.id %} method='post'>
  {# django์—์„œ ์ž๋™์œผ๋กœ ํ† ํฐ ์ƒ์„ฑ #}
  {% 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}}">
    <lable for="choice{{ forloop.counter }}">
      {{ choice.choice_text }}
    </lable>
    <br>
  {% endfor %}
  <input type="submit" value="Vote">
</form>
  • result.html
1
2
3
4
5
6
<h1>{{ question.question_text }}</h1>
<br />
{% for choice in question.choice_set.all %}
<lable> {{ choice.choice_text }} -- {{ choice.votes }} </lable>
<br />
{% endfor %}

URL Config

  • polls/urls.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from django.urls import path
from . import views

app_name = "polls" # app_name์ด ์„ค์ •๋œ ๊ฒฝ์šฐ, polls:detail, polls:vote ๋“ฑ app_name์„ ํ•„์ˆ˜์ ์œผ๋กœ ํฌํ•จํ•˜์—ฌ ํ˜ธ์ถœํ•ด์•ผ ํ•จ

urlpatterns = [
    path("", views.index, name="index"),
    # <int:question_id>/ : ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•˜๋Š” ์ฃผ์†Œ๋ฅผ ํ†ตํ•ด question_id๊ฐ€ ์ „๋‹ฌ๋จ
    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"),
]

Customizing Admin page

  • ๊ฐ™์ด ๊ฐœ๋ฐœํ•˜๋Š” ๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•˜๋Š” ๋‚ด๋ถ€ User๋ฅผ ์œ„ํ•ด Admin Page๋ฅผ ์ ์ ˆํ•˜๊ฒŒ ์กฐ์ž‘ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋ฐฐ์šด๋‹ค.

  • polls/models.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Question(models.Model):
    # verbose_name ์œผ๋กœ ๋„˜๊ฒจ์ค€ ๋ฌธ์ž์—ด์ด admin page์—์„œ ์ œ๋ชฉ์œผ๋กœ ํ‘œ์‹œ๋œ๋‹ค.
    question_text = models.CharField(max_length=200, verbose_name="์งˆ๋ฌธ")
    pub_date = models.DateTimeField(auto_now_add=True, verbose_name="์ƒ์„ฑ์ผ")

    # admin page display๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๋ฐฉ์‹์„ ๋ณ€๊ฒฝํ•œ๋‹ค.
    @admin.display(boolean=True, description="์ตœ๊ทผ์ƒ์„ฑ(ํ•˜๋ฃจ๊ธฐ์ค€)")
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
    ...
  • polls/admin.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from django.contrib import admin
from .models import *

# StackedInline, TabularInline
# Question admin page์—์„œ ์—ฐ๊ฒฐ๋œ Choice๋ฅผ Inline ํ˜•ํƒœ๋กœ ์ถœ๋ ฅํ•˜๊ธฐ ์œ„ํ•œ class
class ChoiceInline(admin.TabularInline):
    model = Choice
    extra = 2  # ์ถ”๊ฐ€ ๋“ฑ๋ก ์นธ

# Question admin page customizing
class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        ("์ƒ์„ฑ์ผ", {"fields": ["pub_date"], "classes": ["collapse"]}),  # collapse : ์ˆจ๊น€์ฒ˜๋ฆฌ
        ("์งˆ๋ฌธ ์„น์…˜", {"fields": ["question_text"]}),
    ]
    list_display = ["question_text", "pub_date", "was_published_recently"]
    readonly_fields = ["pub_date"] # cannot modify.
    inlines = [ChoiceInline]
    list_filter = ["pub_date"]  # filter records by pub_date
    search_fields = ["question_text", "choice__choice_text"]

# register Question to admin site with customized page "QuestionAdmin"
admin.site.register(Question, QuestionAdmin)

๐Ÿ‘€ย CHECK

(์–ด๋ ต๊ฑฐ๋‚˜ ์ƒˆ๋กญ๊ฒŒ ์•Œ๊ฒŒ ๋œ ๊ฒƒ ๋“ฑ ๋‹ค์‹œ ํ™•์ธํ•  ๊ฒƒ๋“ค)

  • vscode django template ํฌ๋งคํŒ…

  • django template ์ฃผ์„ ์ฒ˜๋ฆฌ

    1
    2
    3
    4
    
    {% comment %}
    ์ฃผ์„ ๋‚ด์šฉ
    {% endcomment %}
    {# ํ•œ ์ค„ ์ฃผ์„ #}
    

โ— ๋Š๋‚€ ์ 

์˜ค๋Š˜์€ ์ œ์ผ ๋ง˜์— ๋“ค์ง€ ์•Š๋Š” TIL ์ธ ๊ฒƒ ๊ฐ™๋‹ค. ์ผ๋‹จ ์ •๋ฆฌ๋„ ์ œ๋Œ€๋กœ ์•ˆ๋๊ณ , ์šฉ์–ด๋„ ์ œ๋Œ€๋กœ ๊ธฐ์–ต์„ ๋ชปํ–ˆ๋‹ค. ๊ฐ•์˜๋ฅผ ๋‹ค์‹œ ๋ณด๋ฉด์„œ ๊ฐ•์‚ฌ๋‹˜์ด ์‚ฌ์šฉํ•˜๋Š” ์šฉ์–ด๋“ฑ์„ ์ฒดํฌํ•˜๊ณ  ์ž‘์„ฑํ–ˆ์œผ๋ฉด ์ข‹์•˜๊ฒ ์ง€๋งŒ ์‹œ๊ฐ„์ด ๋น ๋“ฏํ•˜๊ธฐ๋„ ํ•˜๊ณ  ์ง‘์ค‘๋„ ์ž˜ ์•ˆ๋ผ์„œ ์ œ๋Œ€๋กœ ๋ชป ์ ์—ˆ๋‹ค. ๋‚ด์ผ ์ค‘์— ์‹œ๊ฐ„์ด ๋‚˜๋ฉด ์˜ค๋Š˜ ๊ฐ•์˜๋ฅผ ๋‹ค์‹œ ๋“ค์œผ๋ฉด์„œ ์ˆ˜์ •์„ ์ข€ ํ•˜๊ณ ์‹ถ๋‹ค. ๋‚ด์ผ ๋ชปํ•˜๋”๋ผ๋„ ์ฃผ๋ง์—๋Š” ๊ผญ ํ•  ์ƒ๊ฐ์ด๋‹ค.

์–ด์ œ ์˜ค๋Š˜ ๋ฐฐ์šด ๋‚ด์šฉ๋“ค์€ ์•„๋ฌด๋ž˜๋„ ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋ฉด์„œ ์ง์ ‘ ๋” ์ฐพ์•„๋ณด๊ณ  ์‚ฌ์šฉํ•ด๋ด์•ผ ์ œ๋Œ€๋กœ ์ดํ•ดํ•  ๊ฒƒ ๊ฐ™๋‹ค. View์™€ Template, URL Config๊ฐ€ ํ•˜๋Š” ์—ญํ• ์€ ์ดํ•ด๋„ ๊ฐ€๊ณ  ํ™œ์šฉํ•ด์„œ ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค์–ด๋ณด๋Š”๊ฒƒ๋„ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๊ฐ๊ฐ์˜ ํ•„๋“œ๋‚˜ ํ™œ์šฉ ๊ฐ€๋Šฅํ•œ ๋ฉ”์†Œ๋“œ ๋“ฑ ๊นŠ๊ฒŒ ๋“ค์–ด๊ฐ€๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด ๋ง์ด๋‚˜ ๊ธ€๋กœ ํ’€์–ด์„œ ์„ค๋ช…ํ•˜๋Š”๊ฒŒ ๋„ˆ๋ฌด ์–ด๋ ต๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ•™๊ต๋ฅผ ๋‹ค๋‹ˆ๋ฉด์„œ ์˜์–ด๋กœ ๋œ term์„ ๋” ์ž์ฃผ ์ ‘ํ•ด์„œ ๊ทธ๋Ÿฐ๊ฐ€.. TIL์„ ์ ์„ ๋•Œ์—๋„ ํ•œ๊ตญ์–ด๋กœ ์„ค๋ช…ํ•˜๊ธฐ ์• ๋งคํ•˜๊ณ  ์–ด๋ ค์šด ์šฉ์–ด๋“ค์€ ๋ฌด์กฐ๊ฑด ์˜์–ด๋กœ ์ ๋Š” ์Šต๊ด€์ด ์žˆ๋‹ค.

์š”์ฆ˜ ๊ฐ•์˜๋“ค์€ ์‹ค์Šต ์œ„์ฃผ๋ผ ๊ทธ๋Ÿฐ์ง€ TIL์„ ์ฝ”๋“œ์™€ ์ฃผ์„ ์œ„์ฃผ๋กœ๋งŒ ์ ๋Š” ๊ฒฝํ–ฅ์ด ์žˆ๋Š”๋ฐ, ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ์ ์–ด๋ณด๊ณ  ์‹ถ๋‹ค. ์˜ˆ์ „์— ์ฐพ์•„๋‘” TIL ์ฐธ๊ณ  ๋งํฌ์™€, ๋ถ€ํŠธ์บ ํ”„๋ฅผ ์ฐธ๊ฐ€์ž๋“ค ๋ธ”๋กœ๊ทธ๋ฅผ ๋ณด๋ฉด์„œ ์ž˜ ์“ด TIL๋“ค์„ ์ฐธ๊ณ ํ•ด๋ด์•ผ๊ฒ ๋‹ค.

Hugo๋กœ ๋งŒ๋“ฆ
Jimmy์˜ Stack ํ…Œ๋งˆ ์‚ฌ์šฉ ์ค‘