Django를 이용해 간단 Blog 만들기

이동건·2021년 4월 5일
0

Django

목록 보기
2/4

장고를 이용해 간단한 블로그를 제작해 본다.
앞 부분 세팅은 https://velog.io/@ddengkun/django 와 같다.

개발 환경은 centos7 이다.

우선 python3를 설치한다.

[root@dg-django ~]# yum install -y python3

그 후 django를 실습할 디렉토리를 생성해 준다.

[root@dg-django ~]# cd /root/
[root@dg-django ~]# mkdir django
[root@dg-django ~]# cd django/

그 안에서 가상 환경을 생성해 준다.

[root@dg-django django]# python3 -m venv env
[root@dg-django django]# ls
env

env를 이용해 가상환경을 activate 시켜준다.

[root@dg-django django]# source env/bin/activate

그 후 django와 djangorestframework를 설치해 준다.


(env) [root@dg-django django]# yum install -y django 
(env) [root@dg-django django]# pip install -y djangorestframework

여기까지 같다.

블로그를 만들기 위해 mysite를 startproject로 생성해 준다.

(env) [root@dg-django-blog django]# django-admin startproject mysite
(env) [root@dg-django-blog django]# ls
env  mysite
(env) [root@dg-django-blog django]# cd mysite/
(env) [root@dg-django-blog mysite]# ls
manage.py  mysite

상위 디렉토리 이름과 겹치므로 상위 디렉토리 이름을 blog로 변경해 주었다.

(env) [root@dg-django-blog mysite]# cd ..
(env) [root@dg-django-blog django]# ls
env  mysite
(env) [root@dg-django-blog django]# mv mysite django-blog
(env) [root@dg-django-blog django]# ls
blog  env

이제 settings를 변경해 준다.

(env) [root@dg-django-blog mysite]# cd /root/django/django-blog/mysite
(env) [root@dg-django-blog mysite]# vi settings.py

django 버전 3.1 이상을 사용한다면 아래의 세팅을 사용하기 위해선
import os
를 from pathlib import Path 아래에 작성해 주어야 한다.

이번에는 sqlite3를 그대로 사용할 것이기 때문에 database 부분은 넘어가도록 한다.

이번에는 templates를 사용할 것이기 때문에 TEMPLATES를 수정해 준다.

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',
            ],
        },
    },
]

스태틱 파일을 수동으로 지정해 주기 위해 STATICFILES_DIRS도 추가해 준다.

STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]   #추가

타임존을 서울로, 언어를 한글로 변경해 준다.

LANGUAGE_CODE = 'ko-kr'
TIME_ZONE = 'Asia/Seoul'

파일 업로드 기능을 위해 미디어 루트도 추가로 설정해 준다.

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

기본 테이블을 생성해 준다.

(env) [root@dg-django-blog django-blog]# cd /root/django/django-blog
(env) [root@dg-django-blog django-blog]# python manage.py migrate

만약 역기서 sqlite 버전 에러가 나타난다면 다음의 코드를 실행해 준다.

wget https://kojipkgs.fedoraproject.org//packages/sqlite/3.8.11/1.fc21/x86_64/sqlite-devel-3.8.11-1.fc21.x86_64.rpm

wget https://kojipkgs.fedoraproject.org//packages/sqlite/3.8.11/1.fc21/x86_64/sqlite-3.8.11-1.fc21.x86_64.rpm

sudo yum install sqlite-3.8.11-1.fc21.x86_64.rpm sqlite-devel-3.8.11-1.fc21.x86_64.rpm

다시 python manage.py migrate를 하면 다음과 같이 나타난다.

(env) [root@dg-django-blog django-blog]# python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK

데이터베이스 테이블을 만들지 않았는데도 migrate를 하는 이유는 장고는 개발 시 그룹 테이블이 반드시 필요하기 때문이다..
사용자 및 권한 그룹 테이블을 만들기 위해서 실행한 것이다.

실행 결과로 다음과 같이 db.sqlite3 파일이 생성된 걸 볼 수 있다

(env) [root@dg-django-blog django-blog]# ls
db.sqlite3  manage.py  mysite

다음은 Admin 사이트에 로그인하기 위한 관리자를 만들어 본다.

(env) [root@dg-django-blog django-blog]# python manage.py createsuperuser
Username (leave blank to use 'root'): admin
Email address: 자기 이메일 입력
Password:
Password (again):
Superuser created successfully.

우선 북마크 앱을 만든다.

(env) [root@dg-django-blog django-blog]# python manage.py startapp bookmark
(env) [root@dg-django-blog django-blog]# cd bookmark/
(env) [root@dg-django-blog bookmark]# ls
admin.py  apps.py  __init__.py  migrations  models.py  tests.py  views.py

앱을 추가했으니 애플리케이션을 설정 파일에 넣는다.

(env) [root@dg-django-blog mysite]# cd /root/django/django-blog/mysite
(env) [root@dg-django-blog mysite]# vi settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'bookmark.apps.BookmarkConfig', # 추가
]

테이블을 정의한다.
북마크 앱은 Bookmark 테이블 하나만 필요하다.
models.py에 정의한다.

(env) [root@dg-django-blog bookmark]# cd /root/django/django-blog/bookmark
(env) [root@dg-django-blog bookmark]# vi models.py

from django.db import models

class Bookmark(models.Model):
    title = models.CharField(max_length=100, blank=True, null=True)
    url = models.URLField('url', unique=True)

    def __str__(self):
        return self.title

장고에서는 테이블을 하나의 클래스로 정의하고, 테이블의 컬럼은 클래스의 변수로 매핑한다. 즉, ORM 기술을 따른다.
테이블 클래스는 django.db.models.Model 클래스를 상속받아 정의하고, 각 클래스 변수의 타입도 장고에서 미리 정의해 둔 필드 클래스를 사용한다.

이제 models.py 파일에서 정의한 테이블도 Admin 사이트에 보이도록 등록한다.

(env) [root@dg-django-blog bookmark]# vi admin.py

from django.contrib import admin
from bookmark.models import Bookmark

class BookmarkAdmin(admin.ModelAdmin):
    list_display = ('title', 'url')

admin.site.register(Bookmark, BookmarkAdmin)

BookmarkAdmin 클래스는 Bookmark 클래스가 Admin 사이트에서 어떤 모습으로 보여줄지를 정의한다.
Bookmark 내용을 보여줄 때, title과 url을 화면에 출력하라고 지정한 것.
그리고 admin.site.register()를 이용해 Bookmark와 BookmarkAdmin 클래스를 등록했다.

아직은 model을 변경해 테이블 정의까지만 한 것이므로 이를 데이터베이스에 실재로 반영해 주어야 한다.

(env) [root@dg-django-blog django-blog]# cd /root/django/django-blog

(env) [root@dg-django-blog django-blog]# python manage.py makemigrations
Migrations for 'bookmark':
  bookmark/migrations/0001_initial.py
    - Create model Bookmark
    
(env) [root@dg-django-blog django-blog]# python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, bookmark, contenttypes, sessions
Running migrations:
  Applying bookmark.0001_initial... OK

마이그레이션 정보는 디렉토리별로 존재한다. makemigration으로 bookmark/migration 디렉토리 하위에 마이그레이션 파일들이 생기고, 이 마이그레이션 파일들을 이용해 migrate로 데이터베이스에 테이블을 만든 것이다.

이제 서버를 실행해 본다.
다음과 같이 runserver를 실행하면 브라우저에 localhost:8000/admin을 입력하는 것으로 admin 화면에 접근할 수 있다.

(env) [root@dg-django-blog django-blog]# python manage.py runserver 0.0.0.0:8000
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
April 05, 2021 - 14:30:37
Django version 3.1.7, using settings 'mysite.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.

다음과 같이 로그인 화면이 나타나는 걸 볼 수 있다.

아까 슈퍼유저 생성으로 생성한 아이디 비밀번호로 로그인 해 준다.

그럼 이렇게 나타나는 걸 볼 수 있다.

add를 클릭하면 다음과 같이 models.py 파일에 정의한 테이블 모습을 UI로 확인할 수 있다.

내가 만든 모델이 등록됐음과 테이블의 필드가 나타나는 걸 확인했다.

이제 URLconf를 정의한다.

(env) [root@dg-django-blog mysite]# cd /root/django/django-blog/mysite
(env) [root@dg-django-blog mysite]# vi urls.py

from django.contrib import admin
from django.urls import path
from django.conf.urls import url

from bookmark.views import *

urlpatterns = [
    path('admin/', admin.site.urls),

    url(r'^bookmark/', BookmarkLV.as_view(), name='index'),
    url(r'^bookmark/(?P<pk>\d+)/$'), BookmarkDV.as_view(), name='detail'),
]

관련 뷰를 임포트하고 url을 설정한다.
뷰 클래스를 BookmarkLV와 BookmarkDV로 설정 (아직 제작 안함) 그리고 url 패턴 이름을 각각 index와 detail로 설정했다.

이제 뷰를 정의한다.
어떤 제네릭 뷰를 사용할 것인가가 중요한 안건으로 여기서는 리스트뷰와 디테일 뷰를 사용하도록 한다.

(env) [root@dg-django-blog bookmark]# cd /root/django/django-blog/bookmark
(env) [root@dg-django-blog bookmark]# vi views.py

from django.views.generic import ListView, DetailView
from bookmark.models import Bookmark

class BookmarkLV(ListView) :
    model = Bookmark

class BookmarkDV(DetailView) :
    model = Bookmark

테이블 조회를 위해 모델을 임포트 한다.

이제 템플릿을 작성한다.
bookmark/templates/bookmark 디렉터리에 두어야 하므로 새로 디렉터리를 생성한다.

(env) [root@dg-django-blog bookmark]# cd /root/django/django-blog/bookmark/templates/bookmark
(env) [root@dg-django-blog bookmark]# vi bookmark_list.html

<!DOCTYPE html>
<html>
<head>
<title>Django Bookmark List</title>
</head>

<body>

<div id="container">

    <h1>Bookmark List</h1>

    <ul>
        {% for bookmark in object_list %}
            <li><a href="{% url 'detail' bookmark.id %}">{{ bookmark }}</a></li>
        {% endfor %}
    </ul>

</div>

</body>
</html>

이번엔 detail 템플릿을 작성한다.

(env) [root@dg-django-blog bookmark]# vi bookmark_detail.html

<!DOCTYPE html>
<html>
<head>
<title>Django Bookmark Detail</title>
</head>

<body>

<div id="container">

    <h1>{{ object.title }}</h1>

    <ul>
        <li>URL: <a href="{{ object.url }}">{{ object.url }}</a></li>
    </ul>

</div>

</body>
</html>

이제 확인한다.
runserver를 다시 실행한 후 admin으로 로그인 해 준다.

그리고 다음과 같이 샘플 데이터를 넣는다.

그리고 http://localhost:8000/bookmark/ 로 들어가면

다음처럼 나타나는 걸 볼 수 있다.

이어서 블로그를 만들어 본다.

역시 startapp을 통해 blog를 생성한다.

(env) [root@dg-django-blog django-blog]# python manage.py startapp blog

그리고 블로그 앱에 대한 정보를 settings.py에 등록한다.

(env) [root@dg-django-blog mysite]# cd /root/django/django-blog/mysite
(env) [root@dg-django-blog mysite]# vi settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'bookmark.apps.BookmarkConfig',
    'blog.apps.BlogConfig',
]

이어서 모델을 작성한다.

(env) [root@dg-django-blog blog]# cd /root/django/django-blog/blog
(env) [root@dg-django-blog blog]# vi models.py

from django.db import models
from django.urls import reverse

class Post(models.Model):
    title = models.CharField('TITLE', max_length=50)
    slug = models.SlugField('SLUG', unique=True, help_text='one word for title alias.')
    description = models.CharField('DESCRIPTION', max_length=100, blank=True, help_text='simple description text.')
    content = models.TextField('CONTENT')
    create_date = models.DateTimeField('Create Date', auto_now_add=True)
    modify_date = models.DateTimeField('Modify Date', auto_now=True)

    class Meta:
        verbose_name = 'post'
        verbose_name_plural = 'posts'
        db_table  = 'blog_posts'
        ordering  = ('-modify_date',)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('blog:post_detail', args=(self.slug,))

    def get_previous_post(self):
        return self.get_previous_by_modify_date()

    def get_next_post(self):
        return self.get_next_by_modify_date()

여기서 필드 속성 외에 필요한 파라미터가 있으면 Meta 내부 클래스로 정의한다.
이어서 admin.py도 수정한다.

(env) [root@dg-django-blog blog]# vi admin.py

from django.contrib import admin
from blog.models import Post

# Register your models here.

class PostAdmin(admin.ModelAdmin):
    list_display  = ('title', 'modify_date')
    list_filter   = ('modify_date',)
    search_fields = ('title', 'content')
    prepopulated_fields = {'slug': ('title',)}

admin.site.register(Post, PostAdmin)

slug 필드는 title 필드를 이용해 미리 채워지도록 하였다.
데이터베이스에 반영해주기 위해 makemigration과 migrate를 해 준다.

(env) [root@dg-django-blog django-blog]# python manage.py makemigrations
Migrations for 'blog':
  blog/migrations/0001_initial.py
    - Create model Post
(env) [root@dg-django-blog django-blog]# python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, blog, bookmark, contenttypes, sessions
Running migrations:
  Applying blog.0001_initial... OK

이제 runserver를 하고 admin으로 접속해 본다.

다음과 같이 post를 추가할 수 있는 걸 확인할 수 있다.

이제 URLconf를 작성한다.

ROOT_URLCONF와 APP_URLCONF 두 개로 나누어 작성한다.

mysite의 urls.py를 수정한다.

from django.conf.urls import include, url
from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'^bookmark/', include('bookmark.urls')),
    url(r'^blog/', include('blog.urls')),
]

북마크앱에 urls.py를 추가해 준다.

from django.conf.urls import url
from bookmark.views import BookmarkLV, BookmarkDV

app_name = 'bookmark'

urlpatterns = [
    url(r'^$', BookmarkLV.as_view(), name='index'),
    url(r'^(?P<pk>\d+)/$', BookmarkDV.as_view(), name='detail'),
]

이제 blog에도 urls.py를 추가해 준다.

from django.conf.urls import url
from blog.views import *

app_name = 'blog'

urlpatterns = [

    # Example: /
    url(r'^$', PostLV.as_view(), name='index'),

    # Example: /post/ (same as /)
    url(r'^post/$', PostLV.as_view(), name='post_list'),

    # Example: /post/django-example/
    url(r'^post/(?P<slug>[-\w]+)/$', PostDV.as_view(), name='post_detail'),

    # Example: /archive/
    url(r'^archive/$', PostAV.as_view(), name='post_archive'),

    # Example: /2021/
    url(r'^(?P<year>\d{4})/$', PostYAV.as_view(), name='post_year_archive'),

    # Example: /2021/apr/
    url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', PostMAV.as_view(), name='post_month_archive'),

    # Example: /2021/apr/05/
    url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/$', PostDAV.as_view(), name='post_day_archive'),

    # Example: /today/
    url(r'^today/$', PostTAV.as_view(), name='post_today_archive'),
]

이제 뷰를 작성한다.
리스트, 디테일 뿐 아니라 여기서는 아카이브 뷰가 필요로 한다.
연도별, 월별, 일별 포스트를 찾아주어야 하기 때문이다.

from django.views.generic import ListView, DetailView
from django.views.generic.dates import ArchiveIndexView, YearArchiveView, MonthArchiveView
from django.views.generic.dates import DayArchiveView, TodayArchiveView

from blog.models import Post

# Create your views here.

#--- ListView
class PostLV(ListView) :
    model = Post
    template_name = 'blog/post_all.html'
    context_object_name = 'posts'
    paginate_by = 2

#--- DetailView
class PostDV(DetailView) :
    model = Post

#--- ArchiveView
class PostAV(ArchiveIndexView) :
    model = Post
    date_field = 'modify_date'

class PostYAV(YearArchiveView) :
    model = Post
    date_field = 'modify_date'
    make_object_list = True

class PostMAV(MonthArchiveView) :
    model = Post
    date_field = 'modify_date'

class PostDAV(DayArchiveView) :
    model = Post
    date_field = 'modify_date'

class PostTAV(TodayArchiveView) :
    model = Post
    date_field = 'modify_date'

마지막으로 템플릿을 작성한다.

blog/templates/blog/디렉토리를 만든 후 작성한다.

우선 post_all.html이다.

<h1>Blog List</h1>

{% for post in posts %}
    <h2><a href='{{ post.get_absolute_url }}'>{{ post.title }}</a></h2>
    {{ post.modify_date|date:"N d, Y" }}
    <p>{{ post.description }}</p>
{% endfor %}

<br/>

<div>
    <span>
        {% if page_obj.has_previous %}
            <a href="?page={{ page_obj.previous_page_number }}">PreviousPage</a>
        {% endif %}
      
        Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
      
        {% if page_obj.has_next %}
            <a href="?page={{ page_obj.next_page_number }}">NextPage</a>
        {% endif %}
    </span>

</div>

그 다음 post_detail.html을 만든다.

<h2>{{ object.title }}</h2>

<p class="other_posts">
    {% if object.get_previous_by_modify_date %}
    <a href="{{ object.get_previous_post.get_absolute_url }}" title="View previous post">&laquo;--{{ object.get_previous_post }}</a>
    {% endif %}

    {% if object.get_next_by_modify_date %}
    | <a href="{{ object.get_next_post.get_absolute_url }}" title="View next post">{{ object.get_next_post }}--&raquo;</a>
    {% endif %}
</p>

<p class="date">{{ object.modify_date|date:"j F Y" }}</p>
<br/>

<div class="body">
    {{ object.content|linebreaks }}
</div>

다음은 post_archive.html을 작성한다.

<h1>Post Archives until {% now "N d, Y" %}</h1>
<ul>
    {% for date in date_list %}
    <li style="display: inline;">
        <a href="{% url 'blog:post_year_archive' date|date:'Y' %}">Year-{{ date|date:"Y" }}</a></li>
    {% endfor %}
</ul>
<br/>

<div>
    <ul>
        {% for post in object_list %}
        <li>{{ post.modify_date|date:"Y-m-d" }}&nbsp;&nbsp;&nbsp;
        <a href="{{ post.get_absolute_url }}"><strong>{{ post.title }}</strong></a></li>
        {% endfor %}
    </ul>
</div>

이어서 post_archive_year.html을 작성한다.

<h1>Post Archives for {{ year|date:"Y" }}</h1>

<ul>
    {% for date in date_list %}
    <li style="display: inline;">
        <a href="{% url 'blog:post_month_archive' year|date:'Y' date|date:'b' %}">{{ date|date:"F" }}</a></li>
    {% endfor %}
</ul>
<br/>

<div>
    <ul>
        {% for post in object_list %}
        <li>{{ post.modify_date|date:"Y-m-d" }}&nbsp;&nbsp;&nbsp;
        <a href="{{ post.get_absolute_url }}"><strong>{{ post.title }}</strong></a></li>
        {% endfor %}
    </ul>
</div>

다음은 post_archive_month.html을 작성한다.

<h1>Post Archives for {{ month|date:"N, Y" }}</h1>

<div>
    <ul>
        {% for post in object_list %}
        <li>{{ post.modify_date|date:"Y-m-d" }}&nbsp;&nbsp;&nbsp;
        <a href="{{ post.get_absolute_url }}"><strong>{{ post.title }}</strong></a></li>
        {% endfor %}
    </ul>
</div>

post_archive_day를 작성한다.

<h1>Post Archives for {{ day|date:"N d, Y" }}</h1>

<div>
    <ul>
        {% for post in object_list %}
        <li>{{ post.modify_date|date:"Y-m-d" }}&nbsp;&nbsp;&nbsp;
        <a href="{{ post.get_absolute_url }}"><strong>{{ post.title }}</strong></a></li>
        {% endfor %}
    </ul>
</div>

이제 Admin에서 데이터를 입력한 후 저장한다.

localhost:8000/blog에 들어가면 다음처럼 나타나는 걸 볼 수 있다.

localhost:8000/blog/archive에 들어오면 다음과 같이 나타난다.

날짜별로 검색할 수 있는 걸 볼 수 있다.

profile
코드를 통한 세계의 창조

0개의 댓글