Pythonメモ torinaブログ

このブログはDjangoとBootstrap4で作成されました
ソースコード

Django、ブログに使っているModel

プログラミング関連 Django
約306日前 2016年5月25日6:38
多くのブログでは、まず記事があり、大カテゴリ、小カテゴリ、タグ、コメント等があります。
私のブログでは、概ね以下のようなModelです。

models.py
from django.db import models


class BigCategory(models.Model):
    """ 大カテゴリー """

    name = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

    def get_latest_post(self):
        result = POST.objects.filter(
            category__parent=self).filter(
            is_publick=True).order_by('-created_at')[:5]
        return result


class SmallCategory(models.Model):
    """ 小カテゴリー """

    name = models.CharField(max_length=255)
    parent = models.ForeignKey(BigCategory)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

    def get_latest_post(self):
        result = POST.objects.filter(
            category=self).filter(
            is_publick=True).order_by('-created_at')[:5]
        return result


class Tag(models.Model):
    """ タグ """

    name = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

    def get_latest_post(self):
        result = POST.objects.filter(
            tag=self).filter(
            is_publick=True).order_by('-created_at')[:5]
        return result


class POST(models.Model):
    """ ブログのポスト """

    title = models.CharField(max_length=255)
    text = models.TextField()
    category = models.ForeignKey(SmallCategory, null=True, blank=True)
    tag = models.ManyToManyField(Tag, blank=True)
    thumnail = models.ImageField(upload_to='images/', null=True, blank=True)
    is_publick = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title


class Comment(models.Model):
    name = models.CharField(max_length=255, blank=True)
    text = models.TextField(blank=True)
    target = models.ForeignKey(POST)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.text[:10]


このModelは、記事投稿は管理画面では以下のように表示されています。


こちらは小カテゴリの追加です。


大カテゴリ...


そして、タグ


まず、コメントです。
targetで、そのコメントはどの記事についているか?を指定します。
class Comment(models.Model):
    name = models.CharField(max_length=255, blank=True)
    text = models.TextField(blank=True)
    target = models.ForeignKey(POST)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.text[:10]


例えばviews.pyでは、以下のようにコメントを登録します。
comment_name = form.cleaned_data['name']
comment_text = form.cleaned_data['message']
comment_target = POST.objects.get(pk=p_id)
comment = Comment(name=comment_name, text=comment_text, target=comment_target)
comment.save()


Coment.objects.all()の他に、POSTから紐づいているコメントを取得できます。
{% for com in post.comment_set.all %}
<p>{{com.text }}</p>
{% endfor %}


次にタグです。POSTからは、ManyToManyで指定されています。つまり、1記事にいくらでもタグが指定できます。
class Tag(models.Model):
    """ タグ """

    name = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

    def get_latest_post(self):
        result = POST.objects.filter(
            tag=self).filter(
            is_publick=True).order_by('-created_at')[:5]
        return result


カテゴリ等にも使用していますが、これはそのタグでの最新5件の記事を表示する、等に使えます。
    def get_latest_post(self):
        result = POST.objects.filter(
            tag=self).filter(
            is_publick=True).order_by('-created_at')[:5]
        return result


他のfilterやorder_byを除けば、以下のようなコードです。
return POST.objects.filter(tag=self)[:5]


get_latest_postは、主にテンプレートから呼び出す用途です。
ManyToManyなので、全てのタグの最新5件は以下のようになります。
{% for tag in post.tag.all %}
  <p>タグ「{{tag.name}}」の最新5件の記事</p>
  {% for latepost in tag.get_latest_post %}
    <p><a href="{% url 'blog:main' latepost.id %}">{{ latepost.title }}</a></p>
  {% endfor %}
{% endfor  %}


これは、/main/100 のようにURLに記事IDを入れて管理する方法の例です。
もし/main/spam記事のような、URLに記事タイトルを入れる例ならば、latepost.idはlatepost.title等になるでしょう。
<a href="{% url 'blog:main' latepost.id %}">{{ latepost.title }}</a>



大カテゴリです。小カテゴリからはForeignKeyで指定されています。
1つの大カテゴリにつき、複数の小カテゴリがあります。
class BigCategory(models.Model):
    """ 大カテゴリー """

    name = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

    def get_latest_post(self):
        result = POST.objects.filter(
            category__parent=self).filter(
            is_publick=True).order_by('-created_at')[:5]
        return result


views.pyで大カテゴリから記事を検索するなら、例えば以下のような例です。
POSTでは小カテゴリを指定しており、小カテゴリから親(parent)へアクセスができます。
POST.objects.filter(category__parent__name="Python")


また、以下のような、大カテゴリから小カテゴリを取得する場合は


views.pyでは大カテゴリのみをテンプレートへ渡しつつ
"category": BigCategory.objects.all(),


テンプレートにて、このようにかけます。
{% for big in category %}
  大カテゴリ名:{{ big.name }}
  {% for small in big.smallcategory_set.all %}
    小カテゴリ名:{{ small.name }}
  {% endfor %}
{% endfor %}


テンプレートからの最新5件は、以下のように
{% for latepost in post.category.parent.get_latest_post %}
  <p><a href="{% url 'blog:main' latepost.id %}">{{ latepost.title }}</a></p>
{% endfor %}



小カテゴリです。
class SmallCategory(models.Model):
    """ 小カテゴリー """

    name = models.CharField(max_length=255)
    parent = models.ForeignKey(BigCategory)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

    def get_latest_post(self):
        result = POST.objects.filter(
            category=self).filter(
            is_publick=True).order_by('-created_at')[:5]
        return result


親カテゴリの指定です。
parent = models.ForeignKey(BigCategory)


POSTからは直接参照できるので、大カテゴリに比べるとコードは短くなります。
views.pyでのPOSTfilterの例
POST.objects.filter(category__name="小カテゴリ"),


テンプレートからの最新5件は、以下のようになります。
{% for latepost in post.category.get_latest_post %}
  <p><a href="{% url 'blog:main' latepost.id %}">{{ latepost.title }}</a></p>
{% endfor %}


ちょっと脱線しますが、この大カテゴリ、小カテゴリを使ったパンくずリストの例は、以下です。
Home > Python > Django > ブログに使えそうなModel
となります。
<ol class="breadcrumb">
  <li><a href="#">Home</a></li>
  <li><a href="#">{{ post.category.parent.name }}</a></li>
  <li><a href="#">{{ post.category.name }}</a></li>
  <li class="active">{{ post.title }}</li>
</ol>


私のブログでは
/serach/大カテゴリ名
/serach/大カテゴリ名/小カテゴリ名
というURLのアクセスができます。hrefには以下のように書いています。
<ol class="breadcrumb">
  <li><a href="{% url 'blog:index' %}">Home</a></li>
  <li><a href="{% url 'blog:serach' post.category.parent.name %}">{{ post.category.parent.name }}</a></li>
  <li>
    <a href="{% url 'blog:serach' post.category.parent.name post.category.name%}">
      {{ post.category.name }}
    </a>
  </li>
  <li class="active">{{ post.title }}</li>
</ol>


urls.pyでは、以下のようにし...
    url(r'^serach/(?P<big>\w+)/(?P<small>\w+)/$', views.serach, name='serach'),
    url(r'^serach/(?P<big>\w+)/$', views.serach, name='serach'),


そしてviews.pyです。
def serach(request, big, small=None):
    """ カテゴリ別に表示"""

    # 小カテゴリでの検索
    if small:
        posts = POST.objects.filter(category__name=small)

    # 大カテゴリでの検索
    else:
        posts = POST.objects.filter(category__parent__name=big)


話を戻して、POST、記事のメイン部分のModelです。


class POST(models.Model):
    """ ブログのポスト """

    title = models.CharField(max_length=255)
    text = models.TextField()
    category = models.ForeignKey(SmallCategory, null=True, blank=True)
    tag = models.ManyToManyField(Tag, blank=True)
    thumnail = models.ImageField(upload_to='images/', null=True, blank=True)
    is_publick = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title


記事タイトルと記事本文は、見たまんまです。
    title = models.CharField(max_length=255)
    text = models.TextField()


タグと、小カテゴリです。大カテゴリには、小カテゴリ経由で取得します。
どちらも、空白を許可しています。
categoryは1記事1にしているのでForeignKey、タグは複数ということでManyToMany
    category = models.ForeignKey(SmallCategory, null=True, blank=True)
    tag = models.ManyToManyField(Tag, blank=True)


これは記事のサムネ画像です。
    thumnail = models.ImageField(upload_to='images/', null=True, blank=True)


BooleanFieldです。これにチェックをつけると公開、チェックをはずすとログインしなければ見えない状態です。
使い方のサンプルは、下記のURL
https://torina.top/main/237/
    is_publick = models.BooleanField(default=True)



他のサイトで使っているものでは、以下のようなModelもあります。
class Page(models.Model):
    index = models.IntegerField()
    title = models.CharField(max_length=255)
    description = models.CharField(max_length=255)
    keywords = models.CharField(max_length=255)
    text = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return "{0}:{1}".format(self.index, self.title)


これは表示するための順番になります。
Page.objects.order_by("index")のように使います。
index = models.IntegerField()



titleは、titleタグに表示したり、トップページからのリンクで表示する文字列です。
des、keyはmetaタグのdescription、keywordsになります。
    title = models.CharField(max_length=255)
    description = models.CharField(max_length=255)
    keywords = models.CharField(max_length=255)


これはページの本文になります。このModelを使っているサイトでは、HTMLを直接書いています。
text = models.TextField()


テンプレートでは、概ね以下のようになります。
{% block bread %}は、ぱんくずリストです。
autoescape off は、HTMLを直接書いているためエスケープされないようにするための措置です。
{% extends "main/base.html" %}
{% block meta_des %}{{ page.description }}{% endblock %}
{% block meta_key %}{{ page.keywords }}{% endblock %}
{% block title %}{{ page.title }} | サイト名{% endblock %}

{% block bread %}
<ol class="breadcrumb">
  <li><a href="/">ホーム</a></li>
  <li class="active">{{ page.title }}</li>
</ol>
{% endblock %}

{% block content %}
  {% autoescape off %}
    <h1>{{ page.title }}</h1>
    {{ page.text }}
  {% endautoescape %}
{% endblock %}


これはこれで、中々使い勝手がいいです。