스터디 목표
장고를 이용하여 블로그 애플리케이션을 완성한다.
- mysite/blog/static/css/blog.css
body {
margin:0;
padding:0;
font-family:helvetica, sans-serif;
}
a {
color:#00abff;
text-decoration:none;
}
h1 {
font-weight:normal;
border-bottom:1px solid #bbb;
padding:0 0 10px 0;
}
h2 {
font-weight:normal;
margin:30px 0 0;
}
#content {
float:left;
width:60%;
padding:0 0 0 30px;
}
#sidebar {
float:right;
width:30%;
padding:10px;
background:#efefef;
height:100%;
}
p.date {
color:#ccc;
font-family: georgia, serif;
font-size: 12px;
font-style: italic;
}
.pagination {
margin:40px 0;
font-weight:bold;
}
label {
float:left;
clear:both;
color:#333;
margin-bottom:4px;
}
input, textarea {
clear:both;
float:left;
margin:0 0 10px;
background:#ededed;
border:0;
padding:6px 10px;
font-size:12px;
}
input[type=submit] {
font-weight:bold;
background:#00abff;
color:#fff;
padding:10px 20px;
font-size:14px;
text-transform:uppercase;
}
.errorlist {
color:#cc0033;
float:left;
clear:both;
padding-left:10px;
}
.comment {
padding:10px;
}
.comment:nth-child(even) {
background:#efefef;
}
.comment .info {
font-weight:bold;
font-size:12px;
color:#666;
}
- mysite/blog/templates/pagination.html
<div class = "pagination">
<span class = "step-links">
{% if page.has_previous %}
<a href = "?page={{ page.previous_page_number }}">Previous</a>
{% endif %}
<span class="current">
Page {{ page.number }} of {{ page.paginator.num_pages }}.
</span>
{% if page.has_next %}
<a href = "?page={{ page.next_page_number }}">Next</a>
{% endif %}
</span>
</div>
- mysite/blog/templates/blog/base.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<title>{% block title %}{% endblock %}</title>
<link href = "{% static 'css/blog.css' %}" rel = "stylesheet">
</head>
<body>
<div id = "content">
{% block content %}
{% endblock %}
</div>
<div id = "sidebar">
<h2>My blog</h2>
<p>This is my blog.</p>
</div>
</body>
</html>
- mysite/blog/templates/blog/post/detail.html
{% extends "blog/base.html" %}
{% block title %}{{ post.title }}{% endblock %}
{% block content %}
<h1>{{ post.title }}</h1>
<p class = "date">
Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body|linebreaks }}
<p>
<a href="{% url 'blog:post_share' post.id %}">
Share this post
</a>
</p>
{% endblock %}
- mysite/blog/templates/blog/post/list.html
{% extends "blog/base.html" %}
{% block title %} My blog {% endblock %}
{% block content %}
<h1>My blog</h1>
{% for post in posts %}
<h2>
<a href = "{{ post.get_absolute_url }}">
{{ post.title }}
</a>
</h2>
<p class = "date">
Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body | truncatewords:30 | linebreaks }}
{% endfor %}
{% include "pagination.html" with page=page_obj %}
{% endblock %}
- mysite/blog/templates/blog/post/share.html
{% extends "blog/base.html" %}
{% block title %}Share a post{% endblock %}
{% block content %}
{% if sent %}
<h1>E-mail successfully send</h1>
<p>
"{{ post.title }}" was successfully send to {{ form.cleaned_data.to }}.
</p>
{% else %}
<h1>Share "{{ post.title }}" by e-mail</h1>
<form method="post">
{{ form.as_p }}
{% csrf_token %}
<input type="submit" value="Send e-mail">
</form>
{% endif %}
{% endblock %}
from django.contrib import admin
from .models import Post
@admin.register(Post)
class PostAdmin(admin.ModelAdmin) :
list_display = ['title', 'slug', 'author', 'publish', 'status']
list_filter = ['status', 'created', 'publish', 'author']
search_fields = ['title', 'body']
prepopulated_fields = {'slug' : ('title',)}
raw_id_fields = ['author']
date_hierarchy = 'publish'
ordering = ['status', 'publish']
from django import forms
class EmailPostForm(forms.Form) :
name = forms.CharField(max_length = 25)
email = forms.EmailField()
to = forms.EmailField()
comments = forms.CharField(required = False, widget = forms.Textarea)
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
class PublishedManager(models.Manager) :
def get_queryset(self) :
return (super().get_queryset()
.filter(status = Post.Status.PUBLISHED))
class Post(models.Model) :
class Status(models.TextChoices) :
DRAFT = 'DF', 'Draft'
PUBLISHED = 'PB', 'Published'
title = models.CharField(max_length = 250)
slug = models.SlugField(max_length = 250, unique_for_date = 'publish')
author = models.ForeignKey(User,
on_delete = models.CASCADE,
related_name = 'blog_posts')
body = models.TextField()
publish = models.DateTimeField(default = timezone.now)
created = models.DateTimeField(auto_now_add = True)
updated = models.DateTimeField(auto_now = True)
status = models.CharField(max_length = 2,
choices = Status.choices,
default = Status.DRAFT)
objects = models.Manager()
published = PublishedManager()
class Meta :
ordering = ['-publish']
indexes = [
models.Index(fields = ['-publish']),
]
def __str__(self) :
return self.title
def get_absolute_url(self) :
return reverse('blog:post_detail',
args=[self.publish.year,
self.publish.month,
self.publish.day,
self.slug])
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.PostListView.as_view(), name = 'post_list'),
path('<int:year>/<int:month>/<int:day>/<slug:post>/', views.post_detail, name = 'post_detail'),
path('<int:post_id>/share/', views.post_share, name = 'post_share')
]
from django.shortcuts import render, get_object_or_404
from .models import *
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.views.generic import ListView
from .forms import EmailPostForm
from django.core.mail import send_mail
def post_list(request) :
post_list = Post.published.all()
pagintor = Paginator(post_list, 3)
page_number = request.GET.get('page', 1)
try :
posts = pagintor.page(page_number)
except PageNotAnInteger :
posts = pagintor.page(1)
except EmptyPage :
posts = pagintor.page(pagintor.num_pages)
return render(request, 'blog/post/list.html',{'posts' : posts})
def post_detail(request, year, month, day, post) :
post = get_object_or_404(Post,
status = Post.Status.PUBLISHED,
slug = post,
publish__year = year,
publish__month = month,
publish__day = day)
return render(request, 'blog/post/detail.html', {'post':post})
class PostListView(ListView) :
"""
Alternative post list view
"""
queryset = Post.published.all()
context_object_name = 'posts'
paginate_by = 3
template_name = 'blog/post/list.html'
def post_share(request, post_id) :
post = get_object_or_404(Post, id = post_id, status = Post.Status.PUBLISHED)
sent = False
if request.method == 'POST' :
form = EmailPostForm(request.POST)
if form.is_valid() :
cd = form.cleaned_data
post_url = request.build_absolute_uri(post.get_absolute_url())
subject = f"{cd['name']} recommends you read" \
f"{post.title}"
message = f"Read {post.title} at {post_url}\n\n" \
f"{cd['name']}\'s comments: {cd['comments']}"
send_mail(subject, message, 'gim88028@gmail.com', [cd['to']])
sent = True
else :
form = EmailPostForm()
return render(request, 'blog/post/share.html', {'post':post, 'form':form, 'sent':sent})
ALLOWED_HOSTS = []
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'admin@gmail.com'
EMAIL_HOST_PASSWORD = '0000 0000 0000 0000'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"blog.apps.BlogConfig"
]
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls', namespace = 'blog')),
]
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls', namespace = 'blog')),
]
실행 결과
Terminal에 python manage.py runserver 명령을 입력하여 출력 결과를 확인한다.

