Re:ゼロから始める文系プログラマ

未経験がプログラミングを通して人生を変える

【Django】簡単なSNSアプリを開発する⑥ ~CRUDモデルの作成~


スポンサードリンク
 

f:id:ShotaNukumizu_1000:20211205071412p:plain

おはようございます。Shotaです。

今回も前回に引き続き、DjangoSNSアプリを開発するための方法を解説していきます。

今回はCRUDモデルの作成について解説します。

▼前回の記事はコチラ

shotanukumizu-1000.hatenablog.com

▼初回の記事はコチラ

shotanukumizu-1000.hatenablog.com



CRUDモデルとは


実装


投稿内容の詳細表示(Read)

まずはtemplate/index.htmlに以下のコードを書いてください。

{% extends 'base.html' %}

{% block title %}
<title>投稿内容</title>
{% endblock title %}

{% block header %}
<div class="container is-fullhd">
    <div class="notification is-primary">
        <h1><i class="fas fa-globe"></i>Sample SNS Application</h1> 
        <h3>~仕事の仲間と気軽に交流を楽しみましょう!!~</h3>
    </div>
</div>
{% endblock header %}

{% block content %}
{% if user.is_authenticated %}
<div class="container">
    {% for item in object_list %}
    <div class="card">
        <h5 class="card-header">投稿日 {{ item.created_at }}</h5>
        <h5 class="card-header">投稿者 {{ item.name }}</h5>
        <div class="card-body">
            <span class="tag is-{{ item.tag }} is-large">
                <h5 class="card-title">{{ item.title }}</h5>
            </span>
            <!---追加------>
            <a href="{% url 'detail' item.pk %}" class="button is-primary is-light"><i class="fas fa-info-circle"></i>詳細</a>
        </div>
    </div>
    {% endfor %}
    <a href="{% url 'logout' %}" class="button is-link">ログアウト</a>
</div>
{% else %}
please login.
<a href="{% url 'login' %}">ログイン</a>
{% endif %}
{% endblock content %}

次に、_app/views.pyにアクセスして以下のコードを書いてください。

▼書くコード

def detailfunc(request, pk):
  item = get_object_or_404(ArticleModel, pk=pk)
  return render(request, 'detail.html', {'item': item})

▼全体

#_app/views.py
from .models import ArticleModel

from django.db import IntegrityError
from django.shortcuts import render, redirect

from django.contrib.auth import authenticate, login, logout

from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required 


def signupfunc(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        try:
            user = User.objects.create_user(username, '', password)
            return render(request, 'login.html', {})
        except IntegrityError:
            return render(request, 'signup.html', {'error': 'このユーザはすでに登録されています'})
    return render(request, 'signup.html', {})


def loginfunc(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(request, username=username, password=password)

        if user is not None:
            login(request, user)
            return redirect('index')
        
        else:
            return render(request, 'login.html', {})
    
    return render(request, 'login.html', {})


@login_required
def index_func(request):
    object_list = ArticleModel.objects.all()
    return render(request, 'index.html', {'object_list': object_list})

def logoutfunc(request):
    logout(request)
    return redirect('login')

#追加
def detailfunc(request, pk):
  item = get_object_or_404(ArticleModel, pk=pk)
  return render(request, 'detail.html', {'item': item})

次に、詳細ページにアクセスできるように以下のコードを書いてください。

#_app/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index_func, name='index'),
    path('signup/', views.signupfunc, name='signup'),
    path('login/', views.loginfunc, name='login'),
    path('logout/', views.logoutfunc, name='logout'),
    path('detail/<int:pk>', views.detailfunc, name='detail'), #追加
]

次に、DjangoのテンプレートにMarkdownを反映させるための処理を書きます。

_appフォルダの下にtemplatetagsフォルダを作成して、markdown_extras.pyを作成し以下のコードを書いてください。

#_app/templatetags/markdown_extras.py
from django import template
from django.template.defaultfilters import stringfilter

import markdown as md

register = template.Library()

@register.filter
@stringfilter
def markdown(val):
    return md.markdown(val, extensions=['markdown.extensions.fenced_code'])

これが終了したら、以下のコードを書いてデータベースに反映させてください。

$ python manage.py makemigrations
$ python manage.py migrate

あとは、templatesフォルダにdetail.htmlを作成して以下のコードを書いてください。

<!----templates/detail.html------>
{% extends 'base.html' %}
{% load markdown_extras %}

{% block static %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/styles/github.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/languages/plaintext.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
{% endblock static %}

{% block title %}
<title>記事の詳細</title>
<hr>
{% endblock title %}


{% block content %}
{% if user.is_authenticated %}
<div class="notification is-{{ item.tag }}">
    <h2>{{ item.title }}</h2>
    <hr>
    <h4>投稿者:{{ item.name }}</h4>
</div>

<p>画像</p>
<hr>
<img src="{{ item.image.url }}" alt="投稿画像">

<p>内容</p>
<p>{{ item.content|markdown|safe  }}</p>

<a href="{% url 'index' %}" class="button is-primary is-outlined">戻る</a>
{% else %}
Please Login
{% endif %}
{% endblock content %}

highlightjsをインストールしましょう。以下のコードを入力する(htmlのhead要素に)だけで簡単に実装できます。

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/styles/github.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/languages/plaintext.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>


投稿内容の作成(Create)

次に、記事を作成する機能を実装していきます。

まずはtemplates/index.htmlに記事を作成するためのページにアクセスするためのボタンを作成しましょう。

{% extends 'base.html' %}

{% block title %}
<title>投稿内容</title>
{% endblock title %}

{% block header %}
<div class="container is-fullhd">
    <div class="notification is-primary">
        <h1><i class="fas fa-globe"></i>Sample SNS Application</h1> 
        <h3>~仕事の仲間と気軽に交流を楽しみましょう!!~</h3>
    </div>
</div>
{% endblock header %}

{% block content %}
{% if user.is_authenticated %}
<div class="container">
    <!---追加------>
    <a href="{% url 'create' %}" class="button is-link is-large"><i class="fas fa-plus"></i>投稿する</a>
    {% for item in object_list %}
    <div class="card">
        <h5 class="card-header">投稿日 {{ item.created_at }}</h5>
        <h5 class="card-header">投稿者 {{ item.name }}</h5>
        <div class="card-body">
            <span class="tag is-{{ item.tag }} is-large">
                <h5 class="card-title">{{ item.title }}</h5>
            </span>
            <a href="{% url 'detail' item.pk %}" class="button is-primary is-light"><i class="fas fa-info-circle"></i>詳細</a>
        </div>
    </div>
    {% endfor %}
    <a href="{% url 'logout' %}" class="button is-link">ログアウト</a>
</div>
{% else %}
please login.
<a href="{% url 'login' %}">ログイン</a>
{% endif %}
{% endblock content %}

次に、_app/views.pyにアクセスして以下のコードを書いてください。

▼追加するコード

class ArticleCreate(CreateView):
    template_name = 'create.html'
    model = ArticleModel
    fields = {'title', 'content', 'image', 'name', 'tag'}
    success_url = reverse_lazy('index')

▼全体

#_app/views.py
from .models import ArticleModel

from django.urls import reverse_lazy

from django.db import IntegrityError
from django.shortcuts import get_object_or_404, render, redirect

from django.contrib.auth import authenticate, login, logout

from django.views.generic import CreateView, UpdateView

from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required


def signupfunc(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        try:
            user = User.objects.create_user(username, '', password)
            return render(request, 'login.html', {})
        except IntegrityError:
            return render(request, 'signup.html', {'error': 'このユーザはすでに登録されています'})
    return render(request, 'signup.html', {})


def loginfunc(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(request, username=username, password=password)

        if user is not None:
            login(request, user)
            return redirect('index')
        
        else:
            return render(request, 'login.html', {})
    
    return render(request, 'login.html', {})

@login_required
def index_func(request):
    object_list = ArticleModel.objects.all()
    return render(request, 'index.html', {'object_list': object_list})

def logoutfunc(request):
    logout(request)
    return redirect('login')

def detailfunc(request, pk):
  item = get_object_or_404(ArticleModel, pk=pk)
  return render(request, 'detail.html', {'item': item})

#追加
class ArticleCreate(CreateView):
    template_name = 'create.html'
    model = ArticleModel
    fields = {'title', 'content', 'image', 'name', 'tag'}
    success_url = reverse_lazy('index')

次に、ルーティングを有効にするため_app/urls.pyにアクセスして以下のコードを書いてください。

#_app/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index_func, name='index'),
    path('signup/', views.signupfunc, name='signup'),
    path('login/', views.loginfunc, name='login'),
    path('logout/', views.logoutfunc, name='logout'),
    path('detail/<int:pk>', views.detailfunc, name='detail'),
    path('create/', views.ArticleCreate.as_view(), name='create'), #追加
]

最後に、templatesフォルダにアクセスしてcreate.htmlを作成し、以下のコードを書いてください。

{% extends 'base.html' %}

{% block title %}
<title>投稿の作成</title>
{% endblock title %}

{% block header %}
<h1>投稿の作成</h1>
<hr>
{% endblock header %}

{% block content %}
{% if user.is_authenticated %}
<form method="POST" enctype="multipart/form-data">{% csrf_token %}
    <p>title: <input type="text" name="title"></p>
    <div class="form-floating">
        <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea2"
            style="height: 200px" name="content"></textarea>
        <label for="floatingTextarea2">Contents</label>
    </div>
    <p><input type="file" name="image"></p>
    <input type="hidden" name="name" value="{{ user.username }}">
    <select name="tag" id="choice">
        <option value="danger">重要</option>
        <option value="warning">連絡</option>
        <option value="info">イベント</option>
        <option value="success">その他</option>
    </select>
    <input type="submit" value="作成する" class="button is-success is-light">
</form>
{% else %}
Please Login
{% endif %}
{% endblock content %}

これで投稿を作成する機能を実装できました。


投稿内容の編集(Update)

まずは、_app/views.pyで以下のコードを書いてください。

#_app/views.py
from .models import ArticleModel

from django.urls import reverse_lazy

from django.db import IntegrityError
from django.shortcuts import get_object_or_404, render, redirect

from django.contrib.auth import authenticate, login, logout

from django.views.generic import CreateView, UpdateView

from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required


def signupfunc(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        try:
            user = User.objects.create_user(username, '', password)
            return render(request, 'login.html', {})
        except IntegrityError:
            return render(request, 'signup.html', {'error': 'このユーザはすでに登録されています'})
    return render(request, 'signup.html', {})


def loginfunc(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(request, username=username, password=password)

        if user is not None:
            login(request, user)
            return redirect('index')
        
        else:
            return render(request, 'login.html', {})
    
    return render(request, 'login.html', {})

@login_required
def index_func(request):
    object_list = ArticleModel.objects.all()
    return render(request, 'index.html', {'object_list': object_list})

def logoutfunc(request):
    logout(request)
    return redirect('login')

def detailfunc(request, pk):
    item = get_object_or_404(ArticleModel, pk=pk)
    return render(request, 'detail.html', {'item': item})


class ArticleCreate(CreateView):
    template_name = 'create.html'
    model = ArticleModel
    fields = {'title', 'content', 'image', 'name', 'tag'}
    success_url = reverse_lazy('index')

#追加
class ArticleUpdate(UpdateView):
    template_name = 'update.html'
    model = ArticleModel
    fields = {'title', 'content', 'image', 'name', 'tag'}
    success_url = reverse_lazy('index')

次に、ルーティングを設定するために以下のコードを書いてください。

#_app/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index_func, name='index'),
    path('signup/', views.signupfunc, name='signup'),
    path('login/', views.loginfunc, name='login'),
    path('logout/', views.logoutfunc, name='logout'),
    path('create/', views.ArticleCreate.as_view(), name='create'),
    path('update/<int:pk>', views.ArticleUpdate.as_view(), name='update'), #追加
    path('detail/<int:pk>', views.detailfunc, name='detail'), 
]

最後に、templatesフォルダにアクセスしてdetail.htmlを作成し、以下のコードを書いてください。

{% extends 'base.html' %}

{% block title %}
<title>投稿の編集</title>
<hr>
{% endblock title %}

{% block content %}
{% if user.is_authenticated %}
<form method="POST" enctype="multipart/form-data">{% csrf_token %}
    <p>title: <input type="text" name="title"></p>
    <div class="form-floating">
        <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea2" style="height: 200px"
            name="content"></textarea>
        <label for="floatingTextarea2">Contents</label>
    </div>
    <p><input type="file" name="image"></p>
    <input type="hidden" name="name" value="{{ user.username }}">
    <select name="tag" id="choice">
        <option value="danger">重要</option>
        <option value="warning">連絡</option>
        <option value="info">イベント</option>
        <option value="success">その他</option>
    </select>
    <input type="submit" value="update" class="button is-success is-light">
</form>
{% else %}
Please Login
{% endif %}
{% endblock content %}

これで投稿記事を編集する機能は完成です。


投稿内容の削除(Delete)

最後に投稿内容を削除する機能を実装しましょう。

_app/views.pyにアクセスして、以下のコードを書いてください。

▼追加するコード

def deletefunc(request, pk):
    item = ArticleModel.objects.delete(pk=pk)
    return redirect('index')

▼全体

#_app/views.py
from .models import ArticleModel

from django.urls import reverse_lazy

from django.db import IntegrityError
from django.shortcuts import get_object_or_404, render, redirect

from django.contrib.auth import authenticate, login, logout

from django.views.generic import CreateView, UpdateView

from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required


def signupfunc(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        try:
            user = User.objects.create_user(username, '', password)
            return render(request, 'login.html', {})
        except IntegrityError:
            return render(request, 'signup.html', {'error': 'このユーザはすでに登録されています'})
    return render(request, 'signup.html', {})


def loginfunc(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(request, username=username, password=password)

        if user is not None:
            login(request, user)
            return redirect('index')
        
        else:
            return render(request, 'login.html', {})
    
    return render(request, 'login.html', {})

@login_required
def index_func(request):
    object_list = ArticleModel.objects.all()
    return render(request, 'index.html', {'object_list': object_list})

def logoutfunc(request):
    logout(request)
    return redirect('login')

def detailfunc(request, pk):
    item = get_object_or_404(ArticleModel, pk=pk)
    return render(request, 'detail.html', {'item': item})

def deletefunc(request, pk):
    item = ArticleModel.objects.delete(pk=pk)
    return redirect('index')


class ArticleCreate(CreateView):
     template_name = 'create.html'
     model = ArticleModel
     fields = {'title', 'content', 'image', 'name', 'tag'}
     success_url = reverse_lazy('index')


class ArticleUpdate(UpdateView):
     template_name = 'update.html'
     model = ArticleModel
     fields = {'title', 'content', 'image', 'name', 'tag'}
     success_url = reverse_lazy('index')

次に、ルーティングを有効にするため_app/urls.pyにアクセスして以下のコードを書いてください。

#_app/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index_func, name='index'),
    path('signup/', views.signupfunc, name='signup'),
    path('login/', views.loginfunc, name='login'),
    path('logout/', views.logoutfunc, name='logout'),
    path('create/', views.ArticleCreate.as_view(), name='create'),
    path('update/<int:pk>', views.ArticleUpdate.as_view(), name='update'),
    path('detail/<int:pk>', views.detailfunc, name='detail'),
    path('delete/<int:pk>', views.deletefunc, name='delete'), #追加
]

あとは、template/index.htmlに記事を削除するためのボタンを付けて終了です。

{% extends 'base.html' %}

{% block title %}
<title>投稿内容</title>
{% endblock title %}

{% block header %}
<div class="container is-fullhd">
    <div class="notification is-primary">
        <h1><i class="fas fa-globe"></i>Sample SNS Application</h1> 
        <h3>~仕事の仲間と気軽に交流を楽しみましょう!!~</h3>
    </div>
</div>
{% endblock header %}

{% block content %}
{% if user.is_authenticated %}
<div class="container">
    <a href="{% url 'create' %}" class="button is-link is-large"><i class="fas fa-plus"></i>投稿する</a>
    {% for item in object_list %}
    <div class="card">
        <h5 class="card-header">投稿日 {{ item.created_at }}</h5>
        <h5 class="card-header">投稿者 {{ item.name }}</h5>
        <div class="card-body">
            <span class="tag is-{{ item.tag }} is-large">
                <h5 class="card-title">{{ item.title }}</h5>
            </span>
            <a href="{% url 'detail' item.pk %}" class="button is-primary is-light"><i class="fas fa-info-circle"></i>詳細</a>
            {% if user.username == item.name %}
            <a href="{% url 'update' item.pk %}" class="button is-success is-light"><i class="fas fa-pen"></i>編集する</a>
            <!------追加-------->
            <a href="{% url 'delete' item.pk %}" class="button is-danger is-light"><i class="fas fa-trash-alt"></i>削除する</a>
            {% endif %}
        </div>
    </div>
    {% endfor %}
    <a href="{% url 'logout' %}" class="button is-link">ログアウト</a>
</div>
{% else %}
please login.
<a href="{% url 'login' %}">ログイン</a>
{% endif %}
{% endblock content %}

これで簡単なSNSアプリが完成しました!


おわりに

今回の記事では、DjangoCRUDモデルを実装するための方法を解説しました。

今回の記事をもって、これでDjangoで簡単なSNSアプリを開発できるようになりました。

今回までの計6回の記事を参考に、Djangoでログイン機能付きのアプリやCRUDモデルの実装、SNSアプリケーションの仕組みについて理解を深めていただければ幸いです。

完成したソースコードは以下のリンクにアクセスすれば誰でも見られます。よければ参考にしてみてください。

github.com

今回の記事はこれで終了です。