DullCodes’s diary

programming,c++,python,MachineLearning,Math,Django,Competitive

初めてのDjango 13 - ORM を使おう 表示

管理サイトをちょっと見やすく

from django.db import models


class Problems(models.Model):
    # 問題名
    taskname = models.CharField(max_length=255)

    ...

    # こんなのをつけると
    def __str__(self):
        return self.taskname


f:id:DullCodes:20200305185450p:plain
django admin list show taskname

こう見るとやっぱりサイト名表示しないと見づらすぎる
あと作成日時=解いた日時も表示したいなというわけで、カラム追加作成を行う

テーブルの追加

.すごく簡単 モデルに必要なカラムを追加して makemigrations -> migrate すれば終了

from django.db import models


class Problems(models.Model):
    # サイト名を追加
    sitename = models.CharField(max_length=30, default=False)

    ....
$ python3 manage.py makemigrations

とすると不思議な表示が

mysite-project $ python3 manage.py makemigrations

You are trying to add a non-nullable field 'sitename' to problems without a default;
we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
Select an option: _

sitenameというフィールドを追加しようとしていますが
このsitenameはNull不許可なのでデフォルトで値を何かいれてもらわないと
makemigrations ができません
1) a one-off default をProvideします(存在しているすべての行にnullをぶち込みます)
2) やめます、で自分の手でデフォルトの値をモデルに追加します

つまり存在しているテーブルに新たなカラムを追加した場合
存在しているすべての行に新しい項目が追加されるわけで、もしそのカラムがNull不許可 であった場合、何か適当な値を入れてくれないとそれはバグだよってこと

そりゃそうですね
今回は問題がすべてAtCoderの問題なので、AtCoderというデフォルの値を入れてもらうようにします

makemigrations 対応

 venv  ~  Programming  Python  mysite-project  master  ✎  $  
$ python3 manage.py makemigrations
You are trying to add a non-nullable field 'sitename' to problems without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
Select an option: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt

>>> 'AtCoder'
Migrations for 'competitive':
  competitive/migrations/0002_problems_sitename.py
    - Add field sitename to problems

AtCoderという文字列をデフォルトで入れてもらうように設定 無事成功
再度admin管理画面を見てみると

f:id:DullCodes:20200305192103p:plain
django second migration and show admin list to Task1

sitenameという項目が新たに追加されている!
よく考えるとこれは入力する類のものではなくて、ドロップダウン?で選択するのが普通のはず
いつか修正する

admin 管理画面でいろんなカラムを表示したい

adminでの表示を変えるには admin.py をいじくる

from django.contrib import admin
from competitive.models import Problems

# こういうクラスを作る クラス名は適当で良い
class ProblemsAdmin(admin.ModelAdmin):
    # 表示したい Problems Model のフィールドを指定する
    # 存在しない フィールドを指定するとエラーでバズる
    list_display = ['sitename', 'taskname', 'created_at']


# adminに登録
admin.site.register(Problems, ProblemsAdmin)

はい簡単 これで admin を表示すると

f:id:DullCodes:20200305210959p:plain
django admin list display

この adminクラス 表示のオプションもそれそれはたくさんある
adminオプション専用のページってわけじゃないけど公式だとここらへん

はじめての Django アプリ作成、その 7 | Django ドキュメント | Django

Djangoに関しては、調べればとりあえず何かの情報が出てくるくらいには広まってるみたいで
大変ありがたい もっとDjango詳しい人たくさん記事書いてほしい

レコードを表示

なんやかんやあって admin 管理ページがようやくまともになってきたので
普通のページでレコードを表示する

ORマッパーの威力が発揮される
使い方はとても簡単

views.py にて Model をインポートする
この Model はただのオブジェクトみたいに扱える(ORM)
このORMを通じてModelからデータを取得する

from django.shortcuts import render
from django.views import View
# モデルを views.py にインポートする
from competitive.models import Problems


class IndexView(View):
    def get(self, request, *args, **kwargs):
        ctx = {
            # 見た目通りそのまんまのメソッド
            'data': Problems.objects.all()
        }

        print(ctx)

        return render(request, 'competitive/index.html', context=ctx)

中身

mysite $

[05/Mar/2020 21:34:05] "GET /competitive/ HTTP/1.1" 200 715
{'data': <QuerySet [
    <Problems: A - Serval vs Monster>,
    <Problems: B - Common Raccoon vs Monster>,
    <Problems: C - Fennec vs Monster>]>}

[05/Mar/2020 21:34:09] "GET /competitive/ HTTP/1.1" 200 715

QuerySet なるオブジェクト?が入っていた
なんだかわからんけどとりあえずHTML上に表示してみよう
HTML上への表示もすっごく簡単
とりあえずもらったデータそのまま出力してみる

{% extends "base.html" %}

{% block main %}
<h2>Competitive</h2>

<div>
  {{ data }}
</div>
{% endblock main %}

f:id:DullCodes:20200305214155p:plain
django show model to html

そのまま文字列として QuerySet が表示された・・・
どういう処理してんだかわからないけど
Model を ORMを通じて取得すると QuerySet というオブジェクトになることがわかった
でどうみてもQuerySetには挿入したレコードが入っているため、取り出し方(ループの仕方)を知ればOK

{% extends "base.html" %}

{% block main %}
<h2>Competitive</h2>

<table>
  <tr>
    <th>SiteName</th>
    <th>TaskId</th>
    <th>TaskName</th>
    <th>URL</th>
    <th>Code</th>
    <th>Created_at</th>
    <th>Updated_at</th>
  </tr>

  {% comment %} ここ!  {% endcomment %}
  {% for row in data %}
  <tr>
    <td>{{ row.sitename }}</td>
    <td>{{ row.taskid }}</td>
    <td>{{ row.taskname }}</td>
    <td>{{ row.url }}</td>
    <td><pre>{{ row.code }}</pre></td>
    <td>{{ row.created_at }}</td>
    <td>{{ row.updated_at }}</td>
  </tr>
  {% endfor %}
</table>
{% endblock main %}

うーんわかりやすい コードは糞だけど大変見やすい
row.code は TextFieldで入っている、つまり改行が大量に入っているのでpreで整形して出力する

f:id:DullCodes:20200305221630p:plain
django QuerySet loop

すげぇ

というわけでこんな簡単に表示できる

おまけ

ソースコードに色をつけたい・・・
とってもソースコードに色をつけたい
いろいろなJavaScriptフレームワーク?がある

今回は highlight.js を使う

highlightjs.org

  • ファイルをダウンロードして、static の中に入れて
mysite-project $ tree static/

static/
├── css
│   ├── code-styles
│   │   ├── default.css
│   │   ├── ...
│   ├── materialize.css
│   └── materialize.min.css
├── js
│   ├── highlight.pack.js
│   ├── materialize.js
│   └── materialize.min.js
└── pictures
    └── 10.static_file_my_drawing.png
  • base.html で css と js をロードして
{% load static %}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <link rel="stylesheet" href="{% static 'css/materialize.min.css' %}">
  <link rel="stylesheet" href="{% static 'css/code-styles/default.css' %}">

  <script src="{% static 'js/materialize.min.js' %}"></script>
  <script src="{% static 'js/highlight.pack.js' %}"></script>
  <script>hljs.initHighlightingOnLoad();</script>
  <title>Index</title>
</head>

<body>
  {% include "components/_header.html"%}

  <main>
    {% block main %}
    {% endblock main %}
  </main>

</body>

</html>

hilight.js 読み込みOK 使い方も至って簡単

{% for row in data %}
<tr>
  ...
  <td><pre><code class="cpp">{{ row.code }}</code></pre></td>
  ...
</tr>
{% endfor %}

と指定すると

f:id:DullCodes:20200305220834p:plain
django colorized code with highlight.js

テーブルで横長に表示しているだけだからとても見づらいけどとにかく表示できた
調べればとりあえず処理は出来るようになってきたかな あーいい すごくいい
次はcodeを入力するページを作って admin 管理ページからではなく、普通のページからレコードを挿入できるようにする