Django 기초实战教程

本教程使用的环境是Python 3.10.8 ,Django 4.1.3

시작하기

# Django 설치
pip install django

# Django 버전 확인
py -m django --version

# 프로젝트 생성
mkdir project
django-admin startproject blog project

# 생성된 프로젝트 구조
# project/blog 디렉토리
#    __init__.py
#    asgi.py
#    settings.py
#    urls.py
#    wsgi.py
# manage.py

# 개발 서버 실행
py manage.py runserver 8000

디렉토리 구조 설명

  • manage.py: Django 프로젝트 관리용 커맨드라인 도구입니다.
  • blog/init.py: 빈 파일로, 해당 디렉토리가 Python 패키지임을 나타냅니다.
  • blog/settings.py: Django 프로젝트의 환경설정 파일입니다.
  • blog/urls.py: URL 라우팅을 정의하는 파일입니다.
  • blog/asgi.py: ASGI 호환 웹 서버를 위한 진입점입니다.
  • blog/wsgi.py: WSGI 호환 웹 서버를 위한 진입점입니다.

URL 추가하기

# blog 디렉토리에 views.py 파일 생성
from django.shortcuts import render,HttpResponse 

def greeting(request):    
    return HttpResponse("안녕하세요!")

# blog/urls.py 수정
from django.contrib import admin
from django.urls import path
from . import views

urlpatterns = [
    path('greeting/', views.greeting),
]

앱 추가하기

py manage.py startapp article

article 앱이 생성되고 article 디렉토리에 admin.py, apps.py, models.py, tests.py, views.py, init.py 파일이 포함됩니다.

# blog/settings.py의 INSTALLED_APPS에 article 추가
INSTALLED_APPS = [
     ...
    'article'
]

# article/views.py에 새 뷰 추가
from django.shortcuts import render,HttpResponse 

def index(request):
    return HttpResponse('article/index')

# article/urls.py 파일 생성
from django.urls import path
from . import views

urlpatterns = [
    path('index', views.index, name="index"),
]

# blog/urls.py 수정
from django.urls import include

urlpatterns = [
    ...
    path('article/', include('article.urls')),
]

모델 생성하기

Django에서는 관계형 데이터베이스의 테이블을 클래스로 표현합니다. 각 레코드는 해당 클래스의 인스턴스가 되며, 객체 지향 방식으로 데이터베이스를 조작할 수 있습니다.

# article/models.py
from django.db import models

class Article(models.Model):
    """게시글"""
    subject = models.CharField(max_length=100)
    body = models.CharField(max_length=1000)
    written_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        """모델의 문자열 표현 반환"""
        return self.subject

# 모델을 기반으로 마이그레이션 생성
py manage.py makemigrations

# 데이터베이스에 마이그레이션 적용
py manage.py migrate article

# article/views.py에 목록 및 생성 뷰 추가
from article.models import Article

def listing(request):
    articles = Article.objects.all()
    article_names = map(str, articles)
    return HttpResponse("<p>" + " ".join(article_names) + "</p>")

def write(request):
    article = Article(subject='새글', body='본문 내용입니다')
    article.save()
    return HttpResponse("<p>새글이 등록되었습니다</p>")

# article/urls.py에路径 추가
urlpatterns = [
    ...
    path('listing', views.listing, name="listing"),
    path('write', views.write, name="write"),
]

템플릿 사용하기

# blog/settings.py의 TEMPLATES 설정 수정
import os

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

# article/views.py의 listing 함수 수정
def listing(request):
    articles = Article.objects.all()
    article_names = map(str, articles)
    return render(request, 'article/listing.html', {
        'data': ' '.join(article_names),
        'count': len(articles)
    })

# project/templates 디렉토리 생성 후 article/listing.html 파일 생성
<p>외부 템플릿: {{data}}</p>
<p>개수: {{count}}</p>

# article/templates/article/listing.html 파일도 생성 가능
# INSTALLED_APPS 순서대로 템플릿을 검색하므로同名 파일이 있으면 충돌 발생
<p>내부 템플릿: {{data}}</p>
<p>개수: {{count}}</p>

# http://127.0.0.1:8000/article/listing 접근 시
# project/templates/article/listing.html이优先사용됨

템플릿 문법 활용

# article/views.py의 listing 함수 수정
def listing(request):
    articles = Article.objects.all()
    return render(request, 'article/listing.html', {'articles': articles})

# templates/article/listing.html 생성
{% if articles %}
    {% for item in articles %}
        {% if forloop.first %}
<div style="color: red">
  <p>번호: {{item.id}}</p>
  <p>제목: {{item.subject}}</p>
  <p>내용: {{item.body}}</p>
  <p>작성일: {{item.written_at}}</p>
</div>
        {% elif forloop.last %}
<div style="color: blue">
  <p>번호: {{item.id}}</p>
  <p>제목: {{item.subject}}</p>
  <p>내용: {{item.body}}</p>
  <p>작성일: {{item.written_at}}</p>
</div>
        {% else %}
<div style="color: green">
  <p>번호: {{item.id}}</p>
  <p>제목: {{item.subject}}</p>
  <p>내용: {{item.body}}</p>
  <p>작성일: {{item.written_at}}</p>
</div>
        {% endif %}
    {% endfor %}
<p>총 {{articles|length}}개의 글</p>
<p>{{ value|default:'데이터なし' }}</p>
{% else %}
데이터가 없습니다
{% endif %}

정적 파일 설정

# blog/settings.py에 정적 파일 경로 설정
STATIC_URL = 'static/'
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

# project/static 디렉토리 생성
# 사용할 정적 파일(예: bootstrap) 여기에 배치

# templates/layout.html 파일 생성
{% load static %}

<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="{% static 'plugins/bootstrap/css/bootstrap.min.css' %}" />
    <script src="{% static 'plugins/bootstrap/js/bootstrap.bundle.min.js' %}"></script>
    <title>Document</title>
  </head>
  <body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
      <div class="container-fluid">
        <a class="navbar-brand" href="#">블로그</a>
        <div class="collapse navbar-collapse">
          - [Home](#)
- [Link](#)
        </div>
      </div>
    </nav>
    <div class="container-fluid mt-3">
      {% block content %}
      <p>환영합니다</p>
      {% endblock %}
    </div>
  </body>
</html>

# templates/article/listing.html을heritance로 수정
{% extends "layout.html" %}
{% block content %}
{% if articles %}
<div class="row">
  {% for item in articles %}
  <div class="col-3">
    <div class="card">
      <div class="card-header">{{item.id}}</div>
      <div class="card-body">
        <h5 class="card-title">{{item.subject}}</h5>
        <p class="card-text">{{item.body}}</p>
      </div>
    </div>
  </div>
  {% endfor %}
</div>
{% else %}
<div class="alert alert-danger" role="alert">데이터가 없습니다</div>
{% endif %}
{% endblock %}

폼과 데이터 저장

# templates/article/write.html 생성
{% extends "layout.html" %}
{% block content %}
<div class="row">
  <div class="col-6">
    <div class="card">
      <div class="card-header">글쓰기</div>
      <form action="/article/write" method="post">
        {% csrf_token %}
        <div class="card-body">
          <div class="input-group mb-3">
            <span class="input-group-text">제목</span>
            <input type="text" class="form-control" name="subject" />
          </div>
          <div class="input-group mb-3">
            <span class="input-group-text">내용</span>
            <textarea class="form-control" name="body"></textarea>
          </div>
        </div>
        <div class="card-footer">
          <button type="submit" class="btn btn-primary">저장</button>
        </div>
      </form>
    </div>
  </div>
  {% if saved_article %}
  <div class="col-6">
    <div class="card">
      <div class="card-header">{{saved_article.id}}</div>
      <div class="card-body">
        <h5 class="card-title">{{saved_article.subject}}</h5>
        <p class="card-text">{{saved_article.body}}</p>
      </div>
    </div>
  </div>
  {% endif %}
</div>
{% endblock %}

# article/views.py에 쓰기 뷰 추가
def write(request):
    if request.POST:
        subject = request.POST['subject']
        body = request.POST['body']
        article = Article(subject=subject, body=body)
        article.save()
        return render(request, 'article/write.html', {'saved_article': article})
    return render(request, 'article/write.html')

폼 데이터 검증

# article/forms.py 파일 생성 (forms.py는 직접 생성 필요)
from django import forms

class ArticleForm(forms.Form):
    subject = forms.CharField(max_length=100)
    body = forms.CharField(max_length=1000)

# article/views.py의 write 함수 수정
def write(request):
    if request.POST:
        form = ArticleForm(request.POST)
        if form.is_valid():
            subject = form.cleaned_data['subject']
            body = form.cleaned_data['body']
            article = Article(subject=subject, body=body)
            article.save()
            return render(request, 'article/write.html', {'saved_article': article})
        else:
            return render(request, 'article/write.html', {'validation_errors': form.errors})
    return render(request, 'article/write.html')

# templates/article/write.html의 오류 표시 영역
{% if validation_errors %}
<div class="alert alert-warning" role="alert">
  {% for error in validation_errors.values %}
    {{error}}
  {% endfor %}
</div>
{% endif %}

Django Admin 활용

# article/models.py에 추가 모델 정의
class Author(models.Model):
    name = models.CharField(max_length=200)
    age = models.IntegerField(default=0)
    email = forms.EmailField()
    
    def __str__(self):
        return self.name

class Category(models.Model):
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    name = models.CharField(max_length=50)
    
    def __str__(self):
        return self.name

# 마이그레이션 생성 및 적용
py manage.py makemigrations
py manage.py migrate

# 관리자 계정 생성
py manage.py createsuperuser

# article/admin.py 수정
from django.contrib import admin
from article.models import Article, Author, Category

class CategoryInline(admin.TabularInline):
    model = Category
    
class AuthorAdmin(admin.ModelAdmin):
    list_display = ('name', 'age', 'email')
    inlines = [CategoryInline]
    search_fields = ('name', 'email')
    fieldsets = (
        ['기본 정보', {
            'fields': ('name', 'email'),
        }],
        ['추가 정보', {
            'classes': ('collapse',),
            'fields': ('age',),
        }]
    )

admin.site.register(Author, AuthorAdmin)
admin.site.register([Article, Category])

자주 사용하는 명령어

# Django 설치
python -m pip install Django

# 버전 확인
python -m django --version

# 프로젝트 생성
django-admin startproject myproject

# 개발 서버 실행
python manage.py runserver
python manage.py runserver 8080
python manage.py runserver 0.0.0.0:8000

# 앱 생성
python manage.py startapp myapp

# MySQL 클라이언트 설치
python -m pip install mysqlclient

# 데이터베이스 마이그레이션
python manage.py migrate
python manage.py makemigrations myapp
python manage.py sqlmigrate myapp 0001

# 관리자 사용자 생성
python manage.py createsuperuser

# Django 소스 코드 위치 확인
python -c "import django; print(django.__path__)"

태그: Django python web framework MVC Template Engine

7월 23일 01:12에 게시됨