Yaji_S’s diary

プログラミング学習のアウトプットブログ

【Django】ページネーションの実装について

表示内容が多い場合、内容物を分割して表示するページネーションという方法が一般的に使われると思います。今回はDjangoでのページネーションの導入について書いていきます。

まず汎用ビューを使っている場合は以下のように簡単に実装できるそうです。
例としてはBlogモデルを例にしてます。
view.py

from django.views.generic import ListView
from .models import Blog

class BlogistView(Listview):
    model = Blog
    paginate_by = 5
 (内容物の表示数。今回は5件/1ページ)

汎用ビューを使わない場合は以下の関数で実装すできます。
例としては自分で投稿したブログ内容の一覧を想定しています。
view.py

~
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
~

~
class MyPageView( UserPassesTestMixin, LoginRequiredMixin):
    def paginate_queryset(request, queryset, count):
        paginator = Paginator(queryset, count)
        page = request.GET.get('page')
        try:
            page_obj = paginator.page(page)
        except PageNotAnInteger:
            page_obj = paginator.page(1)
        except EmptyPage:
            page_obj = paginator.page(paginator.num_pages)
        return page_obj

    def myblog_list(request):
        user = request.user
        blogs = Blog.objects.filter(custom_user_id=user.id).order_by('post_date')
        page_obj = paginate_queryset(request, blogs, 5)  #(blogs(DBから取得した内容物)を5件/1ページ表示する設定)

        context = {
            'user': user,
            'blog_page': page_obj.object_list,
            'page_obj': page_obj,
        }
        return render(request, 'myblog.html', context)

テンプレートとしては以下の内容で実装しております。

〜〜
<div class="pagenation">ページ</div>
    <ul class="pagination">
        {% if page_obj.has_previous %}
            <li class="page-item">
                <a class="page-link" href="?page={{ page_obj.previous_page_number }}">
                    <span aria-hidden="true">&laquo;</span>
                </a>
            </li>
        {% endif %}
        
        {% for num in page_obj.paginator.page_range %}
            {% if page_obj.number == num %}
                <li class="page-item active"><a class="page-link" href="#!">{{ num }}</a></li>
            {% else %}
                <li class="page-item"><a class="page-link" href="?page={{ num }}">{{ num }}</a></li>
            {% endif %}
        {% endfor %}
    
        {% if page_obj.has_next %}
            <li class="page-item">
                <a class="page-link" href="?page={{ page_obj.next_page_number }}">
                    <span aria-hidden="true">&raquo;</span>
                </a>
            </li>
        {% endif %}
    </ul>
</div>

実装の結果の様子は以下の写真のようになります。
f:id:Yaji_S:20210123231724p:plain

テンプレートについては他にも種類があるそうなので、
好きなデザインのページネーションを導入ができそうです。

【Django】メッセージフレームワークのフロントエンドについて

前回メッセージフレームワークについて実装しましたが、前回の状態では、
文字だけの表示となってしまう為、今回はBootstrapを使った見た目を調整とメッセージに閉じる機能について書いていきます。
前回のtemplate内容

{% if messages %} {% for message in messages %}
 {{ message }}
{% endfor %} {% endif %} 

1.view側のメッセージタイプとtemplate側のclassについて

essages.info(self.request, "メッセージ内容")
messages.success(self.request, "メッセージ内容")
messages.warning(self.request, "メッセージ内容。")
messages.error(self.request, "メッセージ内容")
messages.debug(self.request, "メッセージ内容")
 
上から順に
<div class="alert alert-{{ message.tags }}"> 
のレンダリングが
<div class="alert alert-info"> →Bootstrapでのデザイン有り
<div class="alert alert-success"> →Bootstrapでのデザイン有り
<div class="alert alert-warning"> →Bootstrapでのデザイン有り
<div class="alert alert-error"> →Bootstrapでのデザイン無し
<div class="alert alert-debug"> →Bootstrapでのデザイン無し

このままではerrorとdebugはBootstrapのデザインが適用できないので、

既にあるBootstrapのデザインに適用できるよう、

classを別のものに置き換えるという方法で、デザインを適用させます。

下が今回の方法のイメージです。

デザイン適用のイメージ
messages.error(self.request, "メッセージ内容")
 ↓
<div class="alert alert-danger"> →Bootstrapでのデザイン有り
 
messages.debug(self.request, "メッセージ内容")
 ↓
<div class="alert alert-dark"> →Bootstrapでのデザイン有り


2.setting.pyの追加
setting.pyの下の方に以下の内容を追加

......
MESSAGE_TAGS = {
 messages.ERROR: 'danger',
 messages.DEBAG: 'dark',
}

これでerrorのメッセージもBootstrapのデザインが適用できます。
実装後は下のような感じです。

f:id:Yaji_S:20201222171447p:plain

3.閉じるボタンの実装
メッセージを閉じる場合のXボタンを付けるためには、templateに以下のbuttonタグを追加すれば実装できます。

{% if messages %}
 {% for message in messages %}
  <div class="alert alert-{{ message.tags }}">
   <div class="message-body">
    <div class=""> {{ message }} 
    <button type="button" class="close" data-dismiss="alert" aria-label="Close">
     <span aria-hidden="true">&times;</span>
    </button>
    </div>
   </div>
  </div>
 {% endfor %}
{% endif %}


実装後のイメージ(サインイン時のmessages.successの場合)
f:id:Yaji_S:20201220215942p:plain

【Django】メッセージフレームワークの実装について

アプリの投稿などアクションの結果をメッセージとして表示させる為の機能として、メッセージフレームワークというものがあります。今回はメッセージフレームワークの実装について書いていきます。

 

1.setting.pyの確認・設定

setting.py

INSTALLED_APPS = [
...
'django.contrib.messages',
...
]
 
MIDDLEWARE = [
...
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
#今回は下記のMESSAGE_STORAGEの設定ではSessionMiddlewareを上に設定する必要あり
...
]
 
TEMPLATES = [
{
...
'OPTIONS': {
'context_processors': [
...
'django.contrib.messages.context_processors.messages',
],
},
},
]
 
MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
 

INSTALLED_APPS、MIDDLEWAREはデフォルトの状態で上記のようになっています。

MESSAGE_STORAGEの設定はパフォーマンスに優れたCookieStorageやCookieStoragenとSessionStorageの適位切替の設定(FallbackStorage、デフォルト設定)がありますが、一部うまく表示されないことがあるそうです。

 

2.view.pyの設定

viewでは主にmessagesのインポートと関数にメッセージ文を組み込みます。 

#messagesのインポート
from django.contrib import messages
 
#メッセージのタイプは以下の通りタイプによってテンプレートのhtmlのclassが変わります。
messages.info(self.request, "メッセージ内容")
messages.success(self.request, "メッセージ内容")
messages.warning(self.request, "メッセージ内容。")
messages.error(self.request, "メッセージ内容")
messages.debug(self.request, "メッセージ内容")
 

 

自分の使用例

view.py
from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin
from django.views.generic import UpdateView
from django.contrib import messages
from .forms import UserUpdateForm 
 
 
class UserUpdateView(UpdateView, UserPassesTestMixin, LoginRequiredMixin):
model = CustomUser
form_class = UserUpdateForm
template_name = 'user_info.html'
success_url = '/'

def form_valid(self, form):
form.instance.custom_user = self.request.user
messages.info(self.request, "プロフィールを更新しました。")
return super().form_valid(form) #formのrequestのメッセージとしてreturn
 

 

 また、messages.success(self.request, "メッセージ内容")の内容であれば、

以下のSuccessMessageMixinのインポートとsuccess_message = "メッセージ内容"で実装できます。以下はログイン時のメッセージ導入の場合。

view.py
 
from django.contrib.messages.views import SuccessMessageMixin
from django.contrib.auth.views import LoginView
 
class LogInView(SuccessMessageMixin, LoginView):
template_name = 'account/index.html'
success_url = '/userpage'
success_message = "ログインしました。"

汎用view(django.views.generic)であれば、CreateView, UpdateView, DeleteViewなどで使えるそうです(自分では未検証)。

 

3.templateファイル

{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}"> #メッセージのタイプによってclass名が変わる
<div class="message-body">
{{ message }}
</div>
</div>
{% endfor %}
{% endif %}

実装の様子 

字が小さいですがメッセージの導入が完了しました。

このままでは見た目がアレなので、次回はフロントエンド側について書いていきます。

 

 

 

 

 

 

【Django】フロントエンド向けライブラリについて(Sass processor, widget tweaks)

デフォルトのDjangoのフロントエンドを実装する際、Sass(SCSS)使用、Djangoテンプレート変数のフォーム({{ form }})の細かなスタイルングが出来ず、色々調べました。

ライブラリーおかげで上記の問題が解決できたので、使用したライブラリーをまとめてみました。

 

1. Sass processor

Sass processorはSass(SCSS)をコンパイルする為のライブラリーです。CSSを使うならSassとしてコーディングする方がメリットがあるので、必要となると思います。(rails, lalavelでは標準であるみたいです。)

 

①ライブラリのインストール

pip install libsass django-compressor django-sass-processor

 

②setting.pyに追加

setting.py
 
....
INSTALLED_APPS = [
....
'sass_processor',
 
]
...
STATIC_URL = '/static/'

 

③テンプレートファイル(base.htmlなどがベター)に読み込みのためのコードを記述

base.html
 
<!doctype html>
{% load static %}
{% load sass_tags %}
<html lang="ja">
<head>
...
<link rel="stylesheet" href='{% sass_src "temp/styles/styles.sass" %}' type="text/css">
...

以上でCSSと同じようにSass・SCSSを使えるようになります。

 

2.widget tweaks

widget tweaksはテンプレートファイルで使う{{ from }}にclass属性を付与し、CSSなどでスタイリングしたり、textareaのcolsやrowの設定を簡単にしたりします。

 

①ライブラリのインストール

pip install django-widget-tweaks

 

②setting.pyに追加

setting.py
 
INSTALLED_APPS = [
...
 
'widget_tweaks',
]

 

③テンプレートファイルに読み込みのためのコードを記述

base.html
{% load widget_tweaks %}
 
使用例
....
<form method='POST' role="form" action="" class="post-form">
{% csrf_token %}
<div>{{ form.title|add_class:'クラス名' }}</div> *htmlのタグにクラス名を付与
<div>{{ form.memo|attr:"cols:100"|attr:"rows:3" }}</div> *textareaのcols,rowsの指定
<div class="form-bottom">
<button type="submit" class="btn btn-primary">メモ投稿!</button>
</div>
</form>
... 

class属性を指定できることでフィールドごとの細かなスタイリングやbootstrapなども活用できるので、使い勝手が良いと思います。

 

【Django】テンプレートでのモデルの値の取得

Djangoで汎用ビュー(ListView 、DetailViewなど)を用いた場合は簡単にモデルの値をテンプレに使用できますが、今回は汎用ビューを使用しない場合でのモデルの値を取得する方法を投稿します。

今回はブログ投稿サイトで自分の投稿した管理ページの実装を想定し、ブログの情報(title,post__date)を取得するといった内容です。

app名/views.py
 
class MyPostBlogView(UserPassesTestMixin, LoginRequiredMixin):
def get_myblog(request, user_id):
  user = request.user
myblogs = PostBlog.objects.filter(user_id=user.id) #自分の投稿内容だけ取得
context = {
'user': user,
'myblogs':myblogs
}
return render(request, 'blog/mypage.html', context)

 

取得した値のテンプレートファイル部分
  
<div class="post-list">
<h1>{{ user.username }}さんの投稿一覧</h1>
{% for blog in myblogs %}
<div class="post_content">
<h2>投稿タイトル:{{ blog.title }}</a></h2>
<p>投稿日:{{ blog.post_date }}</p>
</div>
{% endfor %}
</div>

 

汎用ビューは便利ですが、複数のモデルの取得をする実装なんかには使うのが難しい(自分の力量不足かもしれません)ので、色々な方法の実装を身に付けて、選択肢を増やしておくことが必要だと感じました。

【Django】プルダウンメニュー選択の実装

Djangoの入力フォームにてプルダウンメニューでの選択表示を実装について書きます。

プルダウンメニューを実装する方法として2つの方法で実践しています。

1.models.pyでの実装

2.forms.pyでの実装

 

1.models.pyでの実装

プルダウンメニューでの選択表示をcategory_choiceと定義して、対象のタプル内にchoices=category_choiceを記述。

app名/models.py
 
from django.db import models
 
category_choice = (
('', '選択肢から選んでください'),
('0', '健康'),
('1', 'ビジネス'),
('2', '学習'),
 
('3', 'アウトドア・旅行'),
('4', 'その他'),
)

class Post(models.Model):
category = models.CharField(
verbose_name='カテゴリー', choices=category_choice, max_length=255)
....

 

2.forms.pyでの実装

プルダウンメニューでの選択表示をgender_choiceと定義して、Fieldクラスで、forms.ChoiceFieldを記述し、タプル内にchoices=gender_choiceを記述。

app名/forms.py
 
from django import forms
 
gender_choice = (
('0', '未設定'),
('1', '男性'),
('2', '女性'),
)

class SignUpForm(UserCreationForm):
 
gender = forms.ChoiceField(required=False, choices=gender_choice, label='性別')
....

 

1,2の両者ともあとはテンプレートファイル側で通常通りformを実装でOKとなります。

テンプレートファイル一部抜粋
 
<form method="POST" role="form" action="" class="signup-form">
{% csrf_token %}
{{form|crispy}} # Crispy Formsを使用
 
<button type="submit" class="button">Sign up</button>
</form>

個人的にはruby on railsのactive_hashよりも手軽に実装できるような感じます。

【Django】datetimeと現在時間のデフォルト値設定

ToDoリストのようなアプリを作っておりますが、リストの登録の際、期限設定の入力を毎回手入力しないよう、期限の月のデフォルト値として現在の月を設定しようと考えました。

 

まずは現在の時間の取得ですが、Pythonの標準ライブラリである、datetimeモジュールをインポートし、現在の時刻を"date_string"として定義します。

import datetime
date_string = datetime.datetime.now() #現時刻の取得
 
date_string.year #現在の西暦年の取得
date_string.month #今月の取得
date_string.day #今日の日付取得 

また、時間、分、秒も取得が可能です。

 

次にデフォルト値の設定ですが、こちらは先ほど取得した"date_string"から今月の値を取得しています。

アプリ名.models.py
 
from django.db import models
import datetime
date_string = datetime.datetime.now()

class モデル名(models.Model):
year = models.IntegerField(verbose_name='目標期限(年)', default=date_string.year)
month = models.IntegerField(verbose_name='目標期限(月末)', default=date_string.month)

これで初期値として現在の時間を取得が可能となります。

初期状態でイメージは以下のようになります。