なおと
https://scrapbox.io/files/6262713f479884002022c4b8.jpeg
知識情報・図書館学類3年の出井直人と言います。
ソロ活動期間では、webアプリ作ろうと思ってます。
タグの記法 :いっつも忘れるので、ここに置いておきます... まとめた資料
Webアプリのバックエンドが開発しやすそう
Q. バックエンドとフロントエンドってなんぞや…!?
A. (ざっくり、)HTML/CSSみたいなのがフロントエンド、サーバ側がバックエンド
Webフレームワーク…python得意ならDjango(割とシンプル)、JavaScriptならReact、rubyいけるならrails
Djangoでバックエンドを作ることも
・掲示板方式の、クイズの問題集を共有できるサイトでも作ろうかな〜
・あと、WebフレームワークはDjango か React(+Next.js) か Goのどれかを使おうかなと思ってるけど、正直使いながらじゃないとどれ使った方がいいのかは分からないかも。今週中(〜5/1)には決めよう。
・それと、scrapboxの使い方にも慣れておこうか
記録残してねえ!
https://scrapbox.io/files/626cc16cd63805001dd30df4.png
成果物
https://scrapbox.io/files/626cc18652e3d500235f834d.png
KとP
https://scrapbox.io/files/626cc1a000fe5c002395eec2.png
T
反省点だけでなく良かった点も見つめ直して、次に"イカ"そう(-5pt)
とりあえず、Djangoを公式(?)のドキュメント見ながら勉強しよう
Djangoを使うには、Pythonをインストールする必要があるらしい!(そりゃそう)
→とりあえず、最新版インストールしとくか
また、Djangoを使うために、pipのインストール(アップデート)をしないといけないらしい
→まだやってねえ!これからやる!
公式のテキストが英語ばかりで読むのに心が折れそう...
→日本語で書かれているここのページを一度参考にさせてもらおう
次やること(4/30〜5/6)
Djangoのインストールする
Djangoのチュートリアル進める...2個ぐらいまで⇦最低ライン
同時進行で、webサイトの雛形だけでも作ってみる
とりあえず、Djangoインストールするぞ〜
このサイトの情報をもとにやってみる
...公式のドキュメントを参考に、コマンドラインに以下のコードを打ち込んで、試しにDjangoがインストールされてるかどうか確認したら、「4.0.4」って返されて(ファッ!?)となってしまった。もうすでにインストールされてるんか...?...まあ、されてるという体にしよう。これでまたエラーが起きたら、入れ直せばいいだけだし(多分)
% import django
% print(django.get_version())
てか、VSCodeでDjango使えるんだったら、そうしたみあるな〜
→ということで、VSCodeでDjango使えるように設定をがんばることにした
このサイトをもとに、環境構築していこうと思う
「Terminalでの下準備」は終わった。あとは実際にVSCodeで環境構築をしていく(「VSCodeで開発環境を整える」以降)
→ばあちゃんに飯呼ばれたから、VSCodeの環境構築はまた午後にやる!(ばあちゃん、ありがと〜)
昼食終わり〜再開するぞ〜
4番の「仮想環境(今回ならばmyvenv)のinterpreterを選択すること」あたりから怪しくなってきたな
5番、歯車アイコン見当たらなくて「アイコンないやん!!」って一瞬焦ったけど、サイトの方にも「歯車アイコン出なくなったから、Create a launch.json fileを押すんやで〜〜〜」って書いてあったわ。ちゃんとサイト見よ。あと、VSCode自体日本語で書かれてるから、「json fileをつくります」的な感じで書かれてたのも、一瞬戸惑った
https://scrapbox.io/files/626cbafc1d59830023aa1e69.png
きちゃああ。停止は左下のコールスタックの□ボタン押せばええんかな...?
→なんか「Control - C押せ!」みたいに書かれてるけど、押しても停止できなかったから、□ボタンでもよさそう
https://scrapbox.io/files/626cbdbbccdde0001dceade5.png
VSCodeでDjango使えるようになった〜うれし〜
いったん終わり!今日またやるかも
やることにしました。眠気が押し寄せてます。昼寝すればよかった〜〜〜
この方のページを参考にしつつ、VSCodeを使ってPython + Djangoのチュートリアルをやっていこうと思ふ
改めて、「CRUD(Create, Read, Update, Delete)」の重要性を実感。大切にしていこ
「Djangoの内部でどんなことをやっているか」の項目を見たけど、まだあまり理解できてない自分がいる。実際にアプリを作ってみないとわからないと思うな。ここら辺は
$ python manage.py startapp practice_app
アプリを作成するためのコマンド。Djangoを止めてからじゃないと作成できないことに注意。「myappは既にあるから、別の名前を使ってフォルダを作ってね!!」と言われたので、末尾の myapp を practice_app に変更
次に、config/setting.py を修正した。最初、「これどういくねん!!!」ってちょっと焦ったけど、VSCodeの画面上部のログを辿ることで、 setting.py を開けることに思い至った。ひとまず安心。
https://scrapbox.io/files/626d21f654e10500211d9335.png
初見殺し〜〜〜
config/setting.pyの修正は、正直コピペだったからつまづく要素はなかったかと(でも、後でコードの意味は理解しておきたみ)
次に、templates ディレクトリとstatic ディレクトリを manage.py と同じ階層に作成
→ターミナルに戻ってmkdirしました。ちゃんと myproject にいるかどうかも確認しておいた方がいいかも
そしていよいよ、models.pyのコーディング。本来は映画一覧アプリだけど、私は問題集一覧アプリを想定してちょこちょこ変えました。コードは下の通り
code:models.py
from django.db import models
return self.number
name = models.CharField(max_length=100, verbose_name="問題集")
price = models.ForeignKey(Price, on_delete=models.CASCADE, verbose_name="価格", related_name='problems')
problems = models.ForeignKey(Problems, on_delete=models.CASCADE, verbose_name="価格", related_name='log')
...所々変数やらなんやらを変えたから、動かなそうだなって思っちゃってる自分がいるのが悲しい。動いて。
$ python manage.py makemigrations
上のコマンド、実行するぞ〜〜!
code: terminal
File ".../myproject/config/settings.py", line 58, in <module>
NameError: name 'os' is not defined
こんなエラー出てきちゃった。...settings.pyだと!?
→調べたところ、settings.pyでosライブラリをインポートしてなかったからエラーが吐かれたらしかった。そらそうよ。
code:settings.py
import os
from pathlib import Path
ということで、 「from pathlib import Path」 の上に、「import os」を乗せました。これでどうだ。
いけた〜!!
code:terminal
Migrations for 'myapp':
myapp/migrations/0001_initial.py
- Create model Price
- Create model Problems
- Create model Log
こんなのが表示された。0001_initial.pyに3つの表が作成されたんかな〜きっと
% python manage.py migrate
お次はこやつだ!ターミナルに入力する
→OKが立ち並んでるから、大丈夫だと思う!(さすがに異常があればNGつくだろうし...)
code:myapp/admin.py
from django.contrib import admin
from myapp.models import Price, Problems, Log #この部分を追加 admin.site.register(Log)
そして、admin.pyを編集する。
code:terminal
python manage.py createsuperuser
最後に、管理サイトを使用するためのsuperuserを設定して...完成に近づいてきたかな??
code:terminal
Superuser created successfully.
superuserの権利取得!キタキタ
https://scrapbox.io/files/626d2cd36b2db8001d9cb143.png
ログインできない珍事が発生。またアカウント作り直すか〜
→と思ったら、ユーザー名とメアドを勘違いしてた。何やってんだ俺。
ログインできた〜第一段階完了!
次回は urls.py でどこに表示するか、views.py でどうやって情報を取り出すか、Template を作成しどうやってその情報を表示するのか、までをやるらしいので、冷静に落ち着いてやっていきます〜
あと、問題が5件見つかっているのがちょっと心残りですね...重大な問題じゃなければいいけど。
今日は終わり〜!(情報検索とヒューマンインターフェースと専門英語から目を逸らしつつ)
Django生活2日目〜今日はとりあえず、チュートリアルの2番だけやろうかなと
まず、urls.py を作っていく(model.pyの情報を貼り出す箱を作る)
code:myapp/urls.py
from django.urls import path
from . import views
app_name = 'practice_app'
urlpatterns = [
path('problems/<int:pk>/', views.ProblemsDetailView.as_view(), name='problems_detail'),
]
ちょこまか内容を変えながら、urls.pyを作成する。コードの内容は、サイトに記載されているから、適宜それを参照していく。
次に、views.py をつくる(modelに入っていた情報をどう取り出し、貼る前の準備を整えるか、を決める)
code:myapp/views.py
from django.shortcuts import render
from django.views import generic
from practice_app.models import Price, Problems, Log
class IndexView(generic.ListView):
template_name = 'practice_app/index.html'
context_object_name = 'problems_list'
queryset = Problems.objects.all()
class ProblemsDetailView(generic.DetailView):
model = Problems
template_name = 'practice_app/detail.html'
urls.pyと同様、ちょっとずつ変えながらviews.pyを作成する。
クソネミ
そして、Template をつくって情報をサイトに表示していく
templates下にpractice_appディレクトリを作成し、その中にindex.htmlとdetail.htmlを作成
code:templates/practice_app/index.html
{% if problems_list %}
{% for problems in problems_list %}
<li>
<a href="{% url 'myapp:problems_detail' problems.id %}">{{ problems.name }}</a>
{{ problems.number }}
{{ problems.watch_date }}
</li>
{% endfor %}
{% endif %}
そして、index.htmlに以下の内容を書き込んでいく(どっか間違えてそうやな...)
エラー出たーやっぱりなーなんでやー
めっちゃ間空いちゃった。まあいっか(よくない)
前回、pyファイルやhtmlファイルに色々書き込んだらエラー出ちゃったところで終わった。んで、よくよく考えたら、今参考にしているサイトで言及されてるmyappと、自分が作ったpractice_appの関係性がいまいちよくわかってない。それに、今後このままでいったら、また訳のわからんエラーが出てくるとおもた。ので、practice_appを削除し、myappに統合しようと考えました(できるかどうかは置いといて)
変更点は以下の通り
code:myapp/urls.py
4 app_name = 'myapp'
# practice_appからmyappに変更
code:myapp/views.py
7 template_name = 'myapp/index.html'
14 template_name = 'myapp/detail.html'
# それぞれ、practice_appからmyappに変更
templates内のディレクトリを、practice_appからmyappに変更
考えられるところは変更し尽くしたから、あとは実行あるのみ(あんまり直ってる気しないけど...)
https://scrapbox.io/files/627652842d272e001d8ade65.png
やっぱりナ。ちょっとgoogle先生に頼るか〜
https://scrapbox.io/files/6276536d681788001d6b5941.png
と思ったら、urlの末尾にmyapp追加したらページ表示されました。エラーのメッセージを読んでなかったことがバレるな...(でも、価格が表示されないのはなんぞ?)
https://scrapbox.io/files/627655b17696a1001e7ea82a.png
(5行目を problems.price にしたらいけました...ちゃんとサイトの情報を見ようね...)
そして、アオミドロ杯やマウナケアのリンクを辿れるようにするため、 templates/myapp/detail.html に、以下のコードを追加
code:templates/myapp/detail.html
<h1>{{ problems.name }}</h1>
<h2>{{ problems.price }}</h2>
<h3>{{ problems.watch_date }}</h3>
{% for log in problems.log.all %}
<li>{{ log.text }}</li>
{% endfor %}
https://scrapbox.io/files/627657ae0cea89001efe9272.png
無事表示された〜安心や〜
今日のまとめ
myappに移行しました(?)
参照先でも言ってたんですけど、コードがどう繋がっているかを意識しないと迷子になりそう
あと、Django, Python, HTML で記法が違ってくるから、そこも意識しないとな〜(参考 ↓)
{{ 表示するための変数を書くときに使うカッコ }}
{% Pythonのコードを書くときに使うカッコ %}
< HTMLのタグを書くときに使うカッコ >
前回は、CRUDのR(Read)を実装。今回は、CRUDのC(Create)を作成していくぞい。
最初にformを作成する。このformは、「modelとHTMLのフォームをつなぐインターフェース」らしい。
code:myapp/form.py
from django.forms import ModelForm
from myapp.models import Price, Problems, Log
class PriceForm(ModelForm):
class Meta:
model = Price
fields = ('number',)
class ProblemsForm(ModelForm):
class Meta:
model = Problems
fields = ('title','watch_date', 'price')
class LogForm(ModelForm):
class Meta:
model = Log
fields = ('problems','text')
myappフォルダにform.pyを作る。form.pyに上記のコードを記入。fieldsの定義、違ってそおおお
(ちなみに、Formクラスにはforms.Formとforms.ModelFormの2種類があるらしい。ただ、formは入力されたデータの入り口なので、そのままmodelと直結するといった背景から、ModelFormの方が楽らしい。)
次に、urls.pyを編集する。
code:myapp/urls.py
from django.urls import path
from . import views
app_name = 'myapp'
urlpatterns = [
path('problems/<int:pk>/', views.ProblemsDetailView.as_view(), name='problems_detail'),
path('register/price/', views.RegisterPriceView.as_view(), name='registerprice'), #ここを追加 path('register/problems/', views.RegisterProblemsView.as_view(), name='registerproblems'), #ここを追加 path('writing/log/', views.WritingLogView.as_view(), name='writinglog'), #ここを追加 ]
3つのClass-based-viewを作成。
そして、views.pyを編集する。
code:myapp/views.py
from django.shortcuts import render
from django.views import generic
from myapp.models import Price, Problems, Log
class IndexView(generic.ListView):
template_name = 'myapp/index.html'
context_object_name = 'problems_list'
queryset = Problems.objects.all()
class ProblemsDetailView(generic.DetailView):
model = Problems
template_name = 'myapp/detail.html'
model = Price
form_class = PriceForm
template_name = 'myapp/register.html'
def get_success_url(self):
return reverse('myapp:registerproblems')
model = Problems
form_class = ProblemsForm
template_name = 'myapp/register.html'
def get_success_url(self):
return reverse('myapp:problems_detail', kwargs={'pk': self.object.pk })
model = Log
form_class = LogForm
template_name = 'myapp/register.html'
def get_success_url(self):
return reverse('myapp:problems_detail', kwargs={'pk': self.object.problems.pk })
価格・問題集・問題集の感想を書くためのViewを作成する。
最後に、register.htmlを編集する。
code:templates/myapp/register.html
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">save</button>
</form>
HTMLのフォームを作成。
よっしゃこれで完成や!あとは、Djangoを実行して...
https://scrapbox.io/files/627b3fc4030213001d2552ae.png
えーーーーーーー!!!なんでエラーきたあ!!!???
...とまあ、茶番はこれぐらいにして、エラーの対処に移ります。
簡単にいうと、PriceFormが定義されてない。で、本来ならform.pyで定義したPriceFormあたりを参照してフォームを取り出すと思うんですけど...どうなんやろ。とりあえず、ググってヒント探します。
とりあえず、やったこと
views.pyに、以下の2文を定義。
code:myapp/views.py
from django.forms import ModelForm
from myapp.models import Price, Problems, Log
→下のエラーが発生。
https://scrapbox.io/files/627b45d6694aeb00231a21b6.png
Unknown field(s) (title) specified for Problemsを解消するために、models.pyのProblemsクラスを以下のように変更。
code:myapp/models.py
title = models.CharField(max_length=100, verbose_name="問題集") #nameからtitleに変更 watch_date = models.DateField()
price = models.ForeignKey(Price, on_delete=models.CASCADE, verbose_name="価格", related_name='problems')
def __str__(self):
→下図。あれ、うまくいったか...?
https://scrapbox.io/files/627b48a80b91e2001dc259d3.png
↓
https://scrapbox.io/files/627b4973e922c10023e1b38a.png
例の如く、エラーが出た。うーん...
https://scrapbox.io/files/627b49c5fd5400002262da80.png
え...いけるやん?一応、「1000」と打ってみる...
↓
https://scrapbox.io/files/627b4a141220f3001d80c887.png
ぬおおおおぉぉぉなんんでええええぇぇぇぇぇ
エラーがでる = つらい(当たり前)
次回もまた、エラー対処かもです...とほほ...(今度は、書いたコードを精査してみよ...)
...と思ったけど、どっから手をつけたらいいんだ...とりあえず、今度は下のサイトを参照してみようと思う
コメント
azami.icon
おおーーやるもの決まってよかった!!
自分で記事探したりとかしたんだ偉い
すごく開発日誌丁寧だー
エラーは対話だから..がんばれ、でもハマりまくったらすぐにメンターに聞くでもいいとおもうよ
Djangoだ!うおおおおおお Djangoちょっとだけ分かります
困ったら聞いてね!!!!!
バージョンはサイトに合わせたほうがいいかもね(サイトの方ちょっと古いけど おすすめは3.2です)
vscode使っててすごいタイムリー
エラー対処中なのを全部写真載っけているのすごい!頑張ってください!!
エラー対処参考になる!
エラー対処がんばって!
皆さんありがとうございます...!エラー対処、頑張れそうです!!😭
来週(5/14〜5/21)の予定
エラー対処する(水曜までにできなかったら、他の人に頼る...!)
サイト4まで終わらしたい
その後...
小西さんのアドバイスをもとに、Djangoのバージョンを3.2にしてみました。どうなるか...
→他の授業でDjangoの設定したら、4.0.4に戻ってしまった...下記の公式チュートリアルを進めるのには4.0だったら保証されてるそうだから、4.0で進める方が確実だとは思うけど...うーん...
他の授業でDjangoの公式チュートリアルを進める必要性が生まれたが、今やってるチュートリアルに代えて公式のチュートリアルを進めようかな...どうするか...
→ 公式のチュートリアル進めることにしました!やることが二転三転してるな...
やることは変わっても、目標は変えないように(問題集の掲示板アプリ)
今回はビューの作成らしく、割とフロント寄りの内容らしい
まず、polls/views.py にビューを追加する
code:polls/views.py
def detail(request, question_id):
return HttpResponse("You're looking at question %s." % question_id)
def results(request, question_id):
response = "You're looking at the results of question %s."
return HttpResponse(response % question_id)
def vote(request, question_id):
return HttpResponse("You're voting on question %s." % question_id)
次に、 polls.urls モジュールとビューを結びつける
code:polls/urls.py
from django.urls import path
from . import views
urlpatterns = [
# ex: /polls/
path('', views.index, name='index'),
# ex: /polls/5/
path('<int:question_id>/', views.detail, name='detail'),
# ex: /polls/5/results/
path('<int:question_id>/results/', views.results, name='results'),
# ex: /polls/5/vote/
path('<int:question_id>/vote/', views.vote, name='vote'),
]
「システム上にある最新の 5 件の質問項目をカンマで区切り、日付順に表示するビュー」を作成する
code:polls/views.py
from django.http import HttpResponse
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date'):5 return HttpResponse(output)
# Leave the rest of the views (detail, results, vote) unchanged
polls ディレクトリの中に、 templates ディレクトリを作成する
そして、templates ディレクトリの中に polls というディレクトリを作成し、さらにその中に index.html というファイルを作成する
index.html を下のように記述する
code:polls/templates/polls/index.html
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
テンプレートを使用できるようにするため、polls/views.py の index ビューを更新する
code: polls/views.py
from django.http import HttpResponse
from django.template import loader
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date'):5 template = loader.get_template('polls/index.html')
context = {
'latest_question_list': latest_question_list,
}
return HttpResponse(template.render(context, request))
render()ショートカットを用いて、index()ビューを書き換える
code: polls/views.py
from django.shortcuts import render
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date'):5 context = {'latest_question_list': latest_question_list}
return render(request, 'polls/index.html', context)
指定された投票の質問文を表示する
code: polls/views.py
from django.http import Http404
from django.shortcuts import render
from .models import Question
# ...
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404("Question does not exist")
return render(request, 'polls/detail.html', {'question': question})
polls/detail.html テンプレートに以下の内容を記述する
code: polls/templates/polls/detail.html
{{ question }}
「get() を実行し、オブジェクトが存在しない場合には Http404 を送出する」内容を、ショートカットを用いて表現する
code: polls/views.py
from django.shortcuts import get_object_or_404, render
from .models import Question
# ...
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/detail.html', {'question': question})
detail.html を変更する
code: polls/templates/polls/detail.html
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
polls/urls.py ファイル内で app_name を追加し、アプリケーションの名前空間を設定する
code:polls/urls.py
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.index, name='index'),
path('<int:question_id>/', views.detail, name='detail'),
path('<int:question_id>/results/', views.results, name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
polls/index.html テンプレートを変更する
code:polls/templates/polls/index.html
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
から
code:polls/templates/polls/index.html
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
へ
※そういえば、VSCodeのコメントアウトは、「cmd + /」です
投票詳細テンプレート ("polls/detail.html") を更新して、HTML の <form> 要素を追加する
code: polls/templates/polls/detail.html
<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
<fieldset>
<legend><h1>{{ question.question_text }}</h1></legend>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
{% for choice in question.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
{% endfor %}
</fieldset>
<input type="submit" value="Vote">
</form>
誰かが質問の投票をすると、 vote() ビューが質問の結果ページにリダイレクトするようにコーディングする
code: polls/views.py
def results(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/results.html', {'question': question})
polls/results.html テンプレートを作成します
code: polls/templates/polls/result.html
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>
<a href="{% url 'polls:detail' question.id %}">Vote again?</a>
最初、エラーが出て焦ったが、エラーの内容をよくよく読むと「results.htmlがないよ〜」という内容で、pollsディレクトリ内のhtmlをresult.htmlとして保存してたことが原因だったよう。あっぶね〜〜
URLconf の polls/urls.py を開いて修正する
code:polls/urls.py
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
古い index 、 detail 、と results のビューを削除し、代わりに Django の汎用ビューを使用する
code: polls/views.py
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic
from .models import Choice, Question
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
"""Return the last five published questions."""
return Question.objects.order_by('-pub_date'):5 class DetailView(generic.DetailView):
model = Question
template_name = 'polls/detail.html'
class ResultsView(generic.DetailView):
model = Question
template_name = 'polls/results.html'
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected_choice = question.choice_set.get(pk=request.POST'choice') except (KeyError, Choice.DoesNotExist):
# Redisplay the question voting form.
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't select a choice.",
})
else:
selected_choice.votes += 1
selected_choice.save()
# Always return an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
とりあえず、「はじめての Django アプリ作成」の4までは終わったから、今度は5だな...
〆
今日だけだと、終わりそうにないから、途中までで...(というか、メンターの皆さんまじでモブプロうめえなああ。めちゃめちゃ参考になるンゴ)
今日は、自動テストの導入らしい。テストを導入することで、エラーを自動で検知して効率的らしき。ということで、やっていくぞ!
polls/tests.py に、以下のコードを書く。自動テストに変換するためのコードである。
code:polls/tests.py
import datetime
from django.test import TestCase
from django.utils import timezone
from .models import Question
class QuestionModelTests(TestCase):
def test_was_published_recently_with_future_question(self):
"""
was_published_recently() returns False for questions whose pub_date
is in the future.
"""
time = timezone.now() + datetime.timedelta(days=30)
future_question = Question(pub_date=time)
self.assertIs(future_question.was_published_recently(), False)
そして、コマンドでテストを実行する。FAILED吐かれたから正常に動いてそう。
polls/models.py を修正する。11行目にあった Question(models.Model)モデル内のwas_published_recently(self)関数を以下のように手直し。
code:polls/models.py
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now
再度、コマンドでテストを実行する。OK出てきたので◯。
...と思ってたら、自己学習終わってた。今度は「より包括的なテスト(中央よりちょい上)」からやる。
振り返り
今週は、公式ドキュメント(?)の はじめての Django アプリ作成 を、1から5の途中までやってました。正直、来月までに何かサイトができるかといえば微妙...なんとか、簡単なものは作らないと... コメント
なんかめっちゃまとまってる...sugoi
アウトプットが大量・・・ヤベェ
他の授業と並行して勉強良き良き!
どちらでもいいんじゃないかなぁと日高(メ)的には思うよ〜b
既にもうtestまでやってる・・・!すごいなぁ
これほど詳しく書いてあるのすごい
めちゃくちゃ書いてある、やば。そこらへんのブログよりココ見た方が分かりやすそう。同じ参考元見てみます。
確かにこれが一つの成果と言っても過言ではない
ドキュメントを通してどんなDjangoアプリを作っているんですか??
5/21〜5/27の予定
完全にドキュメント終わらす!!(無理しない程度に)
モチベタモツ
はよ、クイズの掲示板作らんと...
今週、Django触りませんでした!すみません!!!怠惰😇
何とか形にはしたい...したい...したい...
前回に引き続いてやってきます!!
tests.py のクラス内に以下の内容を追加し、過去・現在のエラーに自己対処できるようにする
code:polls/tests.py
def test_was_published_recently_with_old_question(self):
"""
was_published_recently() returns False for questions whose pub_date
is older than 1 day.
"""
time = timezone.now() - datetime.timedelta(days=1, seconds=1)
old_question = Question(pub_date=time)
self.assertIs(old_question.was_published_recently(), False)
def test_was_published_recently_with_recent_question(self):
"""
was_published_recently() returns True for questions whose pub_date
is within the last day.
"""
time = timezone.now() - datetime.timedelta(hours=23, minutes=59, seconds=59)
recent_question = Question(pub_date=time)
self.assertIs(recent_question.was_published_recently(), True)
公開予定の投票を、非表示する
→ polls/views.py を変更する。
code:polls/views.py
from django.utils import timezone
最初に、timezoneをインポートする
code:polls/views.py
def get_queryset(self):
"""
Return the last five published questions (not including those set to be
published in the future).
"""
return Question.objects.filter(
pub_date__lte=timezone.now()
).order_by('-pub_date'):5 そして、 get_queryset メソッドを修正する
新しく作成したビューをテストする
code:polls/tests.py
from django.urls import reverse
reverseメソッドをインポートする
code:polls/tests.py
def create_question(question_text, days):
"""
Create a question with the given question_text and published the
given number of days offset to now (negative for questions published
in the past, positive for questions that have yet to be published).
"""
time = timezone.now() + datetime.timedelta(days=days)
return Question.objects.create(question_text=question_text, pub_date=time)
class QuestionIndexViewTests(TestCase):
def test_no_questions(self):
"""
If no questions exist, an appropriate message is displayed.
"""
response = self.client.get(reverse('polls:index'))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "No polls are available.")
def test_past_question(self):
"""
Questions with a pub_date in the past are displayed on the
index page.
"""
question = create_question(question_text="Past question.", days=-30)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(
)
def test_future_question(self):
"""
Questions with a pub_date in the future aren't displayed on
the index page.
"""
create_question(question_text="Future question.", days=30)
response = self.client.get(reverse('polls:index'))
self.assertContains(response, "No polls are available.")
def test_future_question_and_past_question(self):
"""
Even if both past and future questions exist, only past questions
are displayed.
"""
question = create_question(question_text="Past question.", days=-30)
create_question(question_text="Future question.", days=30)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(
)
def test_two_past_questions(self):
"""
The questions index page may display multiple questions.
"""
question1 = create_question(question_text="Past question 1.", days=-30)
question2 = create_question(question_text="Past question 2.", days=-5)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(
)
そして、question を簡単に作れるようにするショートカット関数と、新しいテストクラスを作成する
DetailViewのテスト ... URLで検索しても表示されないように設定する
code:polls/views.py
class DetailView(generic.DetailView):
...
def get_queryset(self):
"""
Excludes any questions that aren't published yet.
"""
return Question.objects.filter(pub_date__lte=timezone.now())
DetailView(generic.DetailView)に、関数を追加する
code:polls/tests.py
class QuestionDetailViewTests(TestCase):
def test_future_question(self):
"""
The detail view of a question with a pub_date in the future
returns a 404 not found.
"""
future_question = create_question(question_text='Future question.', days=5)
url = reverse('polls:detail', args=(future_question.id,))
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
def test_past_question(self):
"""
The detail view of a question with a pub_date in the past
displays the question's text.
"""
past_question = create_question(question_text='Past Question.', days=-5)
url = reverse('polls:detail', args=(past_question.id,))
response = self.client.get(url)
self.assertContains(response, past_question.question_text)
そして、テストを追加する。
今週の振り返り
とりあえず、公式ドキュメントの5まではできた
ただ、これを応用できるかと言われると、うーん...というのが本音。ほんとに形にできんのか...そもそも、この知識を7月からの活動に活かせるのだろうか...⇦今後はそれも意識していきたい
コメント
テストやってるのすごい…
5/28〜6/3の予定
公式ドキュメント(最低6, できれば7)やる
pyコードやDjangoファイルを理解する!!⇦めちゃ重要
n回目の自己学習。もう残りも少なくなってきてるから、少しでも知識を蓄えるぞい。
とりあえず、今日で公式ドキュメント終わらせるぞいぞい
今回は...画像やJavaScript、CSSなどのファイルを管理するための手法を学ぶそう
そのための技術として、django.contrib.staticfiles を用いるらしき。早速やってみるぞい
☆アプリの構造をカスタマイズする
最初に、 polls ディレクトリの中に static ディレクトリを作成する
次に、static ディレクトリ内に polls という名前のディレクトリを作り、その中に style.css というファイルを作成する
スタイルシートをコーディングする
code:polls/static/polls/style.css
li a {
color: green;
}
そして、index.html に、以下のコードを上部に追加する
code:polls/templates/polls/index.html
{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}">
※{% static %} テンプレートタグは、静的ファイルの完全 URL を生成するタグ。
https://scrapbox.io/files/6296de89a86c71001d76760f.png
文字が緑になってるけんね〜
☆背景画像を追加する
polls/static/polls/ ディレクトリの中に images サブディレクトリを作成する
そして、ディレクトリ内に background.gif ファイルを配置する
さらに、スタイルシートを編集する
code:polls/static/polls/style.css
body {
background: white url("images/background.gif") no-repeat;
}
https://scrapbox.io/files/6296e04e4a8a4f001d369a32.png
背景に画像ファイルが表示された。でかでかだ〜
ラスト〜〜いくで〜〜
☆admin フォームのカスタマイズ
編集フォームでのフィールドの並び順を並べ替える
code:polls/admin.py
from django.contrib import admin
from .models import Question
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
]
admin.site.register(Question, QuestionAdmin)
☆リレーションを張ったオブジェクトの追加
管理ページに Choice を表示させるために、adminにも Choice を登録する
code:polls/admin.py
from django.contrib import admin
from .models import Choice, Question
# ...
admin.site.register(Choice)
前述の方法に代わり、Question オブジェクトを追加する時に Choice をひと揃い追加する
code:polls/admin.py
from django.contrib import admin
from .models import Choice, Question
class ChoiceInline(admin.StackedInline):
model = Choice
extra = 3
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
]
admin.site.register(Question, QuestionAdmin)
インラインで表示するように設定する
code:polls/admin.py
class ChoiceInline(admin.TabularInline):
☆管理サイトのチェンジリストページをカスタマイズする
各フィールドの値を表示させるため、 list_display オプションを使用する
code:polls/admin.py
class QuestionAdmin(admin.ModelAdmin):
# ...
list_display = ('question_text', 'pub_date', 'was_published_recently')
display() デコレータを使用する
code:polls/models.py
from django.contrib import admin
class Question(models.Model):
# ...
@admin.display(
boolean=True,
ordering='pub_date',
description='Published recently?',
)
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now
今は...adminやらなんやらのサイトを編集してるのよね、きっと(表示の仕方の変更なり、表示する項目の追加なり)
polls/admin.py の QuestionAdmin に次に行を追加する
code:polls/admin.py
検索機能を追加する
code:polls/admin.py
☆管理サイトのルック & フィールをカスタマイズする
プロジェクトテンプレートをカスタムする
templates ディレクトリをプロジェクトディレクトリ (manage.py が置かれているディレクトリ) に作成する
設定ファイル (mysite/settings.py) を開き、TEMPLATES 設定オプションの中に DIRS オプションを追加する
code:mysite/settings.py
TEMPLATES = [
{
],
},
},
]
※DIRS は、Django がテンプレートを読み込む時にチェックする、ファイルシステム上のディレクトリのリスト
/Users/naoto/opt/anaconda3/lib/python3.8/site-packages/django/contrib/admin/templates/admin から base_site.html というテンプレートを、新しく作ったディレクトリにコピーする
上記のhtmlファイルを、以下のように変更する
code:admin/base_site.html
{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">えんぴっと</a></h1>
{% endblock %}
豆知識1 ; 標準の admin テンプレートを変更する必要が出てきた場合、 アプリケーションの テンプレートを編集するほうが、 プロジェクト のテンプレートを編集するより賢い選択になることがある
振り返り
とりあえず、公式のドキュメントは終わった〜えらい!
...と言いたいところだけど、全くDjangoの内部を理解できてないゾ...とほほ...
ちょっとこれからはどうしようかな...一応、図書館から借りてきてるテキストあるから、復習がてらそれをやってきますかね...(アウトプットのつもりで!)
スクラムの講義メモ
ここに話の内容を聴きながら書くべきだったな〜
「何かあった時じゃ遅いから、常にコミュニケーションする」←めっちゃ大事だなと感じたし、これは何回も失敗した経験あるから、大事にしていきたい
エクストリームプログラミング(XP)って初めて聞いたな〜
でも、話聞いてると、やっぱりスクラムに似てるなと感じたけど、実践的な要素を入れてるところが違いらしい
(ペアプロやモブプロ)
てすとだいじ
小さな工夫でチームの開発がスムーズになっていく。些細な工夫も参考にしていこう。
テスト駆動開発
ゲームのルール = スクラム
振り返り
今週までで、公式のドキュメントは完了した...のかな?
↑ こう思っちゃうぐらいには、内容が定着してない
ということで、来週からは本格的に共有アプリを作っていきたいな
それと、最近『Python Django 3超入門』ていう本読んでんですけど...めっちゃくちゃわかりやすい!!来週からは、この本を頼りにしながらやっていこうかと思ってます〜
最近、他の授業やらテストやらサークルやらが忙しすぎて、あまりenPiTが進められていない...ので、この2時間の間だけでも頑張りたいぞい
ということで、ここからは『Python Django 3超入門』を参考にしつつ、本格的に問題集共有アプリ(サイト?)を作っていくぞ!
chapter2(section2-2)までのおさらい
主専攻実習でDjangoを使う関係で、ここまでは読んでました。今日はその復習も兼ねて、また新しいフォルダを作成しようかと
1. プロジェクトの作成、アプリケーションの作成
" django-admin startproject 〇〇 " で、プロジェクトを作成する
→今回は、" django-admin startproject django_app "にします
一応、" python manage.py runserver "でサーバーが動くかどうか確認。
→ロケット飛んだのでヨシ!
" python manage.py startapp 〇〇 " で、アプリケーションを作成する
→今回は quiz とかにします。problems とかに比べたらわかりやすい。
https://scrapbox.io/files/62a0275b81b295001d9384cf.png
できてますね。安心だ〜
2. quizにurls.pyを作成する
「アプリケーションのアドレスは、それぞれのアプリケーションで管理できるようにする」ために、quiz内にurls.pyを作成し、quiz内でquizのurlを管理する
urls.pyを作成したら、以下の内容を書き込む
code:quiz/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
今回の場合、django_app/urls.py も修正しないと開かないンゴ
code:django_app/urls.py
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('quiz/', include('quiz.urls')), #こいつを加える ]
path('quiz/', include('quiz.urls')) を指定することで、quizフォルダ内のurls.pyが読み込まれ、'quiz/'に割り当てられるようになる
3. テンプレートを利用できるようにする
アプリケーションを登録する
→settings.py を修正する
code:django_app/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
こうすることで、「Djangoのテンプレート機能がquizを検索」できるようになる
テンプレートフォルダを作成する
→ django_app/quiz/templates/quiz の順に、フォルダを作成する。
テンプレートを作成する
→ django_app/quiz/templates/quiz の中に、index.htmlファイルを作成する。
code:quiz/templates/quiz/index.html
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>{{title}}</title>
<link rel="stylesheet"
</head>
<h1 class="display-4 text-primary mb-4">{{title}}</h1>
<p class="h5">{{msg}}</p>
<p class="h6"><a href="{% url goto %}">{{goto}}</a></p>
#{% %}はテンプレートを使用するためのタグ。{% url ... %} の ... に urlpatterns で指定した名前(name)を入れることで、そのパスを使用することができる! </body>
</html>
{{title}}、{{msg}}、{{goto}}は次の views.py で内容を指定する
code:quiz/views.py
from django.shortcuts import render
from django.http import HttpResponse
def index(request):
params = {
'title':'quiz/Index',
'msg':'これは、サンプルで作ったページです。',
'goto':'next',
}
return render(request, 'quiz/index.html', params)
def next(request):
params = {
'title':'quiz/Next',
'msg':'これは、もう1つのページです。',
'goto':'index',
}
return render(request, 'quiz/index.html', params)
そして、urls.py で next を指定する
code:quiz/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('next', views.next, name='next'),
]
静的ファイルを利用できるようにする
→ django_app/quiz/static/quiz の順にフォルダを作成する。状況次第で、 quiz フォルダの中にcssファイルや画像ファイルを入れる(一応今回は、style.css を入れた想定)
code:quiz/static/quiz/style.css
body {
color:gray;
font-size:16pt;
}
h1 {
color:red;
opacity:0.2;
font-size:60pt;
margin-top:-20px;
margin-bottom:0px;
text-align:right;
}
p {
margin:10px;
}
a {
color:blue;
text-decoration: none;
}
一応、staticの内のファイルをhtmlファイルに反映させるなら、htmlのheadタグを以下のように修正
code:quiz/templates/quiz/index.html
<head>
<meta charset="utf-8">
<title>{{title}}</title>
<link rel="stylesheet" type="text/css"
href="{% static 'quiz/css/style.css' %}" />
#{% static '〇〇' %}の形で、静的ファイルのURLを作成する。ファイルを使用するためには、staticフォルダ内のパスを指定する必要がある。今回の場合、〇〇に「quiz/css/style.css」を指定。 </head>
ここまでが、これまでに学んだ内容。こっからは、フォームでの送信について。
Git, Github入門(20220610)
今週はGitや〜大事なこと書いてきます
Git(Github)はバージョン管理するためのツール
.gitignore ⇦ 管理しないファイルを入れるためのツール
gitignoreしてないと、他の人がクローンした時にクローンしちゃいけないファイルも取り込まれて、gitが使えないことが起こるらしき。こわひ。
わからなかったら 「git status」
グラフの位置を知るには「git --〇〇」
コミットコメントでは、「変化した結果」だけを書くだけでなく、「なんで変更したのか」という意図を書くことが重要
プッシュ ... ローカルの変更をリモートに反映する
プル ... リモートの変更を取り入れてマージする
思ったこと(06/14)
なんとなく、自己肯定感高めるアプリ作りたいな〜
タイマー機能実装して、時間になったら褒めてくれるアプリ
to doリスト設定して、タスクをdoneに移したら褒めてくれるアプリ
今日めっちゃwifiおもおもやんけ〜
ということで、前回に引き続いて『Python Django 3超入門』を参考にしながら開発を進めていきます
4. フォームで送信できるようにする
フォーム機能を実装!
quizファイルにforms.pyを作成
forms.pyに以下の内容を記述
code:quiz/forms.py
from django import forms
class QuizForm(forms.Form):
name = forms.CharField(label='name')
price = forms.IntegerField(label='price')
comment = forms.CharField(label='comment')
※Formクラスは、form.Formクラスを継承(あるクラスを定義する際に親となるクラスを指定し、そのクラスの機能をまるごと引き継ぐこと)して作成する
※CharFieldはテキストを、IntegerFieldは整数を入力するためのフィールドクラス
ビュー関数を定義する
code:quiz/views.py
from django.shortcuts import render
from django.http import HttpResponse
def index(request):
params = {
'title': '問題集共有サイト',
'message': '問題集を共有せよ:',
'form': QuizForm()
}
'<br>価格:' + request.POST'price' + \ params'form' = QuizForm(request.POST) return render(request, 'quiz/index.html', params)
index.htmlファイルの<body>タグ内を編集する
code:templates/quiz/index.html
<body class="container">
<h1 class="display-4 text-primary">{{title}}</h1>
<p class="h5 mt-4">{{message|safe}}</p>
{% comment %} |safe をつけることで、HTMLタグをそのままタグとして書き出せる {% endcomment %}
<form action="{% url 'index' %}" method="post">
{% csrf_token %}
{{ form }}
{% comment %} {{ form }}だけで大丈夫。便利! {% endcomment %}
<input type="submit" value="click">
{% comment %} 送信ボタン {% endcomment %}
</form>
</body>
デフォルトだと横一列で見づらいため、縦一列に表示するように、htmlファイルを編集する
code:templates/quiz/index.html
<body class="container">
<h1 class="display-4 text-primary">{{title}}</h1>
<p class="h5 mt-4">{{message|safe}}</p>
<form action="{% url 'index' %}" method="post">
{% csrf_token %}
{% comment %} ここから {% endcomment %}
<table>
{{ form.as_table }}
{% comment %} .as_tableでテーブルにまとめる {% endcomment %}
<tr><td></td><td>
<input type="submit" value="click">
</td></tr>
</table>
{% comment %} ここまで {% endcomment %}
</form>
</body>
formクラスで Bootstrapが使えるようにするために、forms.py を変更
code:quiz/forms.py
class QuizForm(forms.Form):
name = forms.CharField(label='name', \
widget=forms.TextInput(attrs={'class':'form-control'}))
price = forms.IntegerField(label='price', \
widget=forms.TextInput(attrs={'class':'form-control'}))
comment = forms.CharField(label='comment', \
widget=forms.NumberInput(attrs={'class':'form-control'}))
#TextInputは <input type="text">のウィジェット、NumberInputは <input type="number">のウィジェット そして、htmlファイルを変更
code:templates/quiz/index.html
<form action="{% url 'index' %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" class="btn btn-primary my-2"
value="click">
</form>
結構スッキリしたわね
☆いろんな機能を実装するなら、HTMLタグでForm作るより、Formクラスで実装した方が吉
◎ちょいと応用編
ビュー関数をクラス化し、GETアクセス時とPOSTアクセス時の操作を規定する
code:quiz/views.py
from django.shortcuts import render
from django.http import HttpResponse
from django.views.generic import TemplateView
from .forms import QuizForm
class QuizView(TemplateView):
self.params = {
'title': 'Hello',
'message': 'your data:',
'form': QuizForm()
}
return render(request, 'quiz/index.html', self.params)
msg = '問題集は、<b>' + request.POST'name' + \ ')</b>です。<br>コメントは <b>' + request.POST'comment' + \ '</b> ですね。'
self.params'form' = QuizForm(request.POST) return render(request, 'quiz/index.html', self.params)
そして、urlpatternsを修正する
code:quiz/urls.py
from django.urls import path
from .views import QuizView
urlpatterns = [
path(r'', QuizView.as_view(), name='index'),
]
5. さまざまなフィールドを使えるようにする
保留!必要になった時にまた参照します
ちょい振り返り
ビュー関係に関しては、ひとまず終わりました!嬉しい!
次からはおそらく、モデルとデータベース関係ですね。頑張ります〜
6. 管理ツールでデータベースを作成する
Djangoにはデータベースの一種である「SQLite」が標準搭載されている。嬉しい!
まず初めに、テーブルを設計する。後々修正するのは大変なので、最初にきっちり決めておこう!今回はこう
名前|問題集の名前を保管する。テキスト値。
価格|問題集の価格を保管する。整数値。
日付|問題集の出版日を保管する。日付の値。
コメント|問題集についての補足を保管する。テキスト値。
モデルを利用して、テーブルを定義する。テーブルの作成がいらない点が、Django特有かと。
code:quiz/models.py
from django.db import models
class Quiz(models.Model):
name = models.CharField(max_length=100)
price = models.IntegerField(default=0)
data = models.DateField()
comment = models.CharField(max_length=200)
return '<Quiz:id=' + str(self.id) + ', ' + \
self.name + '(' + str(self.price) + ')>'
マイレグレーション(データベースの移行)を行う。
ターミナル上で、以下の二つのコードを実行
code:terminal
python manage.py makemigrations quiz
python manage.py migrate
振り返り
何となく、中途半端なところでおわっちゃったけど、まいっか!!
なんとな〜く、きょうはいっぱいすすめられたきがします(小並感)
とりあえずミグレーションまでは完了したから、今度は管理ツールを使えるように設定させます(来週までに完成させたいががが)
開発していくぞ
7. 管理ツールを使おう
管理ツールを使って、ダミーのデータを作るぞ!(テスト用とかで使えそうなやつ)
管理者登録をする。ターミナルで以下のコマンドをタイプ
code:terminal
python manage.py createsuperuser
そすると、ユーザ名(admin)・めあど(t.........@..........)・パスワード(..o....)を求められるから、入力してく
Quizモデルを管理ツールで利用できるように登録してく
code:quiz/admin.py
from django.contrib import admin
from .models import Quiz
https://scrapbox.io/files/62ac35c10a868200236da5c7.png
※1つのレコードを扱うのか、はたまた複数のレコードを扱うのかによって、単数と複数が異なってくるぞい
https://scrapbox.io/files/62ac36980a868200236dbb12.png
quizsに入った後。もちろん、何にもデータは入ってないので、右上の「ADD QUIZ+」から仮のレコードを登録する。
https://scrapbox.io/files/62ac37c8f13867001e8a084a.png
登録後。ちゃんと表示されてますね。
次に、利用者を登録する。Home > Users > ADD USER からユーザを登録する。 → 登録できた!
6/13 〜 6/17 までの振り返り
今週は割と、本の内容を進められたような気がする。
フォームの送信から管理ツールの設定まで(p. 88 〜 164)できた感じか。着実に進んできている...と信じたい(来週の週末までには形にしたいなあ)
というかあれか、6/8 から本の内容を初めて、今日(6/17)までで管理ツールまできた感じか。なるほど。
おそらく、chapter3の内容を終わらせれば、自分の作りたいものは作れると思うから、そこまで終わらしたいな(ページにして、あと64p...以外とできそう...?)
フィードバック
スクラップボックスにソースコードごと載せていて、今後に生かせそうでいいなと思ったKeisuke Kuma.icon
スクラップボックスに溜まっている知見がすごい....
デザインが好み
うおおありがとうございます!!!頑張ります( ; ; )からの来週の予定
今作っているのを形にしつつ、ちゃんとスクラップボックスを書いていくぞ!!!!
ちょっとやっていく
8. レコードを取得する
レコードを表示する
views.py を編集する
code:quiz/views.py
from django.shortcuts import render
from .models import Quiz
def index(request):
params = {
'title': 'Hello',
'message': '問題集',
'data': data,
}
return render(request, 'quiz/index.html', params)
index.htmlを修正し、レコードを表示する
code:quiz/index.html
<body class="container">
<h1 class="display-4 text-primary">{{title}}</h1>
<p class="h5 mt-4">{{message|safe}}</p>
<table class="table">
<tr>
<th>ID</th>
<th>NAME</th>
<th>PRICE</th>
<th>DATA</th>
<th>COMMENT</th>
</tr>
{% for item in data %}
{% comment %} forタグを用いて変数dataから多数のデータを取り出し、itemに代入する。 {% endcomment %}
<tr>
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.price}}</td>
<td>{{item.data}}</td>
<td>{{item.comment}}</td>
<tr>
{% endfor %}
{% comment %} forタグの終了地点 {% endcomment %}
</table>
</body>
モデルを表示させるため、urls.pyを編集する
code:quiz/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
https://scrapbox.io/files/62af1263f525f7002393b07e.png
ちゃんと表示されてる。えらい(?)
指定のIDのレコードだけを取り出せるようにする
forms.pyを修正する
code:quiz/forms.py
class QuizForm(forms.Form):
id = forms.IntegerField(label='ID')
そして、index.htmlを修正する
code:quiz/index.html
<body class="container">
<h1 class="display-4 text-primary">{{title}}</h1>
<p class="h5 mt-4">{{message|safe}}</p>
{% comment %} ここから {% endcomment %}
<form action="{% url 'index' %}" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="click">
</form>
{% comment %} ここまでを追加 {% endcomment %}
<table class="table">
<tr>
<th>ID</th>
<th>NAME</th>
<th>PRICE</th>
<th>DATA</th>
<th>COMMENT</th>
</tr>
{% for item in data %}
<tr>
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.price}}</td>
<td>{{item.data}}</td>
<td>{{item.comment}}</td>
<tr>
{% endfor %}
</table>
</body>
そしてviews.pyを編集
code:quiz/views.py
from django.shortcuts import render
from .models import Quiz
from .forms import QuizForm
def index(request):
data = Quiz.objects.all()
params = {
'title': 'Hello',
'message': '問題集',
'form':QuizForm(),
'data': [],
}
if (request.method == 'POST'):
item = Quiz.objects.get(id=num)
params'form' = QuizForm(request.POST) else:
params'data' = Quiz.objects.all() return render(request, 'quiz/index.html', params)
ちょっと、理解が怪しくなってきたな...
※メソッドチェーンを使いこなせれば複雑な操作も書けるようになるから、極められるようになりたい!( Quiz.objects.all().values() みたいな書き方のこと)
※ Quiz.objects.all().values() において、valuesメソッドの引数に項目名を書いておくと、その項目の値だけ取り出せる(例:Quiz.objects.all().values('id', 'name') )
※レコードを取得する手法には、最初だけ取るものや最後だけ取るもの、また個数を数えるものがある
code:quiz/views.py
from ast import Num
from django.shortcuts import render
from .models import Quiz
from .forms import QuizForm
def index(request):
params = {
'title': 'Hello',
'data': data,
}
return render(request, 'quiz/index.html', params)
code:quiz/index.html
<body class="container">
<h1 class="display-4 text-primary">{{title}}</h1>
<p class="h5 mt-4">{{message|safe}}</p>
<form action="{% url 'index' %}" method="post">
<table class="table">
<tr>
<th>DATA</th>
</tr>
{% for item in data %}
<tr>
<td>{{item}}</td>
<tr>
{% endfor %}
</table>
</body>
https://scrapbox.io/files/62af1bbbfd4e8a00210c14d3.png
項目数、最初のレコード、最後のレコードだけ表示
振り返り
終わり〜!疲れたンゴ
CRUDまでやりたかったけど、まあしゃあないか
...情報検索システムの最終課題、どっちにするかめちゃくちゃ迷うな(正直Aやってみたいけど、現実的に考えればBになりそうだな...)
ということで、Djangoやっていきます!
Querysetの機能を書き換える。
code:quiz/views.py
from ast import Num
from django.shortcuts import render
from .models import Quiz
from django.db.models import QuerySet
def __new_str__(self):
result = ''
for item in self:
result += '<tr>'
for k in item:
result += '<td>' + str(k) + '=' + str(itemk) + '</td>' result += '</tr>'
return result
QuerySet.__str__ = __new_str__
def index(request):
data = Quiz.objects.all().values('id', 'name', 'price')
params = {
'title': 'Hello',
'data': data,
}
return render(request, 'quiz/index.html', params)
index.htmlで表示できるようにするぞ
code:quiz/index.html
<body class="container">
<h1 class="display-4 text-primary">{{title}}</h1>
<table class="table">
{{data|safe}}
</table>
</body>
これだけの内容で、
https://scrapbox.io/files/62b1103cf1b845001e945436.png
これだけの表示が出る。マジですごい。
9. CRUDを作る
データベースを利用していく上で基本となる、「CRUD」を構築していく。
※CRUD ... Create, Read, Update, Delete
C(Create)を実装する
モデルインスタンスを用意し、保存メソッド(今回は save を使用)を実行する
フォームを作成
code:quiz/forms.py
from django import forms
class QuizForm(forms.Form):
name = forms.CharField(label='Name', \
widget=forms.TextInput(attrs={'class':'form-control'}))
price = forms.IntegerField(label='Price', \
widget=forms.NumberInput(attrs={'class':'form-control'}))
data = forms.DateField(label='Data', \
widget=forms.DateInput(attrs={'class':'form-control'}))
comment = forms.CharField(label='Comment', \
widget=forms.TextInput(attrs={'class':'form-control'}))
C(create)専用のhtmlファイルを作成
code:quiz/create.html
{% load static %}
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>{{title}}</title>
<link rel="stylesheet"
crossorigin="anonymous">
</head>
<body class="container">
<h1 class="display-4 text-primary">{{title}}</h1>
<form action="{% url 'create' %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="click" class="btn btn-primary mt-2">
</form>
</body>
</html>
form専用のhtmlファイルみたいっすね
ついでに、createからのデータを表示するよう、index.htmlも変更
code:quiz/index.html
<body class="container">
<h1 class="display-4 text-primary">{{title}}</h1>
<table class="table">
<tr>
<th>data</th>
</tr>
{% for item in data %}
<tr>
<td>{{item}}</td>
<tr>
{% endfor %}
</table>
</body>
そして、viewsを変更
code:quiz/views.py
from xml.etree.ElementTree import Comment
from django.shortcuts import render, redirect
from .models import Quiz
from .forms import QuizForm
def index(request):
data = Quiz.objects.all()
params = {
'title': 'Hello',
'data': data,
}
return render(request, 'quiz/index.html', params)
# create model
def create(request):
params = {
'title': 'Hello',
'form': QuizForm(),
}
return render(request, 'quiz/create.html', params)
最後に、urls.pyを修正
code:quiz/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('create', views.create, name='create'),
]
https://scrapbox.io/files/62b11813c1f810001d7a6f26.png
OKそう。フォームに何か記入すると、
https://scrapbox.io/files/62b1184c1e0f430022b782dd.png
index.htmlにちゃんと戻ってきますね。
今日はひとまず終わろう。
自己学習もいよいよ大詰め!今日までに検索できる所まで終わらしたいな。
ModelFormを使う
前回の方法よりもスムーズにレコードを保存できるようにしていくぞい
forms.pyを修正
code:quiz/forms.py
from django import forms
from .models import Quiz
class QuizForm(forms.ModelForm):
model = Quiz
create関数を修正する
code:quiz/views.py
def create(request):
quiz = QuizForm(request.POST, instance=obj)
params = {
'title': 'Hello',
'form': QuizForm(),
}
return render(request, 'quiz/create.html', params)
create.htmlも修正
code:quiz/index.html
<body class="container">
<h1 class="display-4 text-primary">{{title}}</h1>
<form action="{% url 'create' %}" method="post">
{% csrf_token %}
<table class="table">
{% comment %} tableタグを使って整形する {% endcomment %}
{{ form.as_p }}
<tr><th><td>
<input type="submit" value="click" class="btn btn-primary mt-2">
</tr></th></td>
</table>
</form>
</body>
https://scrapbox.io/files/62b2897e8344d20023336ad0.png
本の表示とちょっと違うけど、ひとまず保留でいいか。
Updateを実装
◯「あらかじめ、更新するレコードのモデルを用意しておく」
◯編集用のQuizインスタンスを作成する
urls.pyを修正
code:quiz/urls.py
urlpatterns = [
path('', views.index, name='index'),
path('create', views.create, name='create'),
]
index.htmlも修正
code:quiz/index.html
<body class="container">
<h1 class="display-4 text-primary">{{title}}</h1>
<table class="table">
<tr>
<th>data</th>
</tr>
{% for item in data %}
<tr>
<td>{{item}}</td>
<td><a href="{% url 'edit' item.id %}">Edit</a></td>
{% comment %} ここの一文を追加。edit/id番号に飛べるようにする {% endcomment %}
<tr>
{% endfor %}
</table>
</body>
edit.htmlを作成する
code:quiz/edit.html
{% load static %}
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>{{title}}</title>
<link rel="stylesheet"
crossorigin="anonymous">
</head>
<body class="container">
<h1 class="display-4 text-primary">{{title}}</h1>
<form action="{% url 'edit' id %}" method="post">
{% comment %} ここの一文だけ、create.htmlと異なる {% endcomment %}
{% csrf_token %}
<table class="table">
{{ form.as_table }}
<tr><th><td>
<input type="submit" value="click" class="btn btn-primary mt-2">
</td></th></tr>
</table>
</form>
</body>
</html>
views.pyを編集し、edit関数を作成する
code:quiz/views.py
if (request.method == 'POST'):
return redirect(to='/quiz')
params = {
'title': 'Hello',
'id':num,
'form': QuizForm(instance=obj),
}
return render(request, 'quiz/edit.html', params)
コードの内容はほぼcreate関数と同じ。
Deleteを作成する
◯DeleteもUpdateと非常に似ているらし
urls.py と index.html を編集。編集内容はuptateとほぼ同じ。
delete.htmlを作成する
code:quiz/delete.html
{% load static %}
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>{{title}}</title>
<link rel="stylesheet"
crossorigin="anonymous">
</head>
<body class="container">
<h1 class="display-4 text-primary">{{title}}</h1>
<p>※以下のレコードを削除します。</p>
<table class="table">
<tr><th>ID</th><td>{{obj.id}}</td></tr>
<tr><th>Name</th><td>{{obj.name}}</td></tr>
<tr><th>Price</th><td>{{obj.price}}</td></tr>
<tr><th>Data</th><td>{{obj.data}}</td></tr>
<tr><th>Comment</th><td>{{obj.comment}}</td></tr>
{% comment %} formはじまり。 {% endcomment %}
<form action="{% url 'delete' id %}" method="post">
{% csrf_token %}
<tr><th></th><td>
<input type="submit" value="click" class="btn btn-primary">
</td></tr>
</form>
{% comment %} formおわり。機能としては、{% csrf_token %}と<input type="submit">だけ。 {% endcomment %}
</table>
</body>
</html>
delete関数を作る
code:quiz/views.py
def delete(request, num):
quiz = Quiz.objects.get(id=num)
if (request.method == 'POST'):
return redirect(to='/quiz')
params = {
'title': 'Hello',
'id':num,
'obj': quiz,
}
https://scrapbox.io/files/62b29b1844a5a6001d98f00f.png
deleteまで実装。
https://scrapbox.io/files/62b29b2026d3260023c23924.png
deleteを押すと、この画面に遷移。下のclickを押すと...
https://scrapbox.io/files/62b29b28f15985001de7b023.png
レコードが削除される(※id=4のレコードは1枚目と2枚目の間に消しました。すみません!)
ジェネリックビューを実装する
保留!時間があれば実装します。
10. 検索機能を実装する
レコードを検索できるようにする
検索とフィルター
Managerクラスのインスタンスを用いて、フィルター機能を利用する
urls.pyを編集
code:quiz/urls.py
urlpatterns = [
... 略 ...
]
forms.pyを編集
code:quiz/forms.py
class FindForm(forms.Form):
find = forms.CharField(label='Find', required=False, \
find.htmlを作成
code:quiz/find.html
{% load static %}
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>{{title}}</title>
<link rel="stylesheet"
crossorigin="anonymous">
</head>
<body class="container">
<h1 class="display-4 text-primary">{{title}}</h1>
<p>{{message|safe}}</p>
<form action="{% url 'find' %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<tr><th></th><td>
<input type="submit" value="click"class="btn btn-primary mt-2">
</td></tr>
</form>
<hr>
<table class="table">
<tr>
<th>id</th>
<th>name</th>
<th>data</th>
<th>comment</th>
</tr>
{% for item in data %}
<tr>
<th>{{item.id}}</th>
<td>{{item.name}}({{item.price}})</td>
<td>{{item.data}}</td>
<td>{{item.comment}}</td>
<tr>
{% endfor %}
</table>
</body>
</html>
find関数を定義
code:quiz/views.py
def find(request):
if (request.method == 'POST'):
form = FindForm(request.POST)
msg = 'Result: ' + str(data.count())
else:
msg = 'search words...'
form = FindForm()
data =Quiz.objects.all()
params = {
'title': 'Hello',
'message': msg,
'form':form,
'data':data,
}
return render(request, 'quiz/find.html', params)
https://scrapbox.io/files/62b2a5168e6d8b001dd4528b.png
これでひとまず、実装は完了。
あいまい検索
上記のコードだと、完全一致するものしか検索できない。
→あいまい検索(クエリーに内容を含むものが全て検索される機能)を実装しよう!
至ってシンプル。views.pyの1行を変更するだけ。
code:quiz/views.py
※このほかにも、__startswithや__endswithなども存在する
大文字や小文字を区別しないようにする
大文字小文字を区別しない、あいまい検索を可能にする
→ name__contains を name_icontains にするだけ(iを入れるだけ)!
数値の比較
数値の大小を比較する
...__gt(値よりも大きい), __gte(値と等しいか大きい), __lt(値よりも小さい), __lte(値と等しいか小さい)
※引数をintにブロードすることを忘れずに!
◯円以上、◯円以下を表現する
AND検索を実装
code:quiz/views.py
if (request.method == 'POST'):
form = FindForm(request.POST)
data = Quiz.objects \
.filter(price__gte=val0) \ msg = 'Result: ' + str(data.count()
... あとは同じ ...
条件Aと条件Bをどっちも検索する
OR検索を実装
code:quiz/views.py
from django.db.models import Q
data = Quiz.objects.filter( Q(name__icontains=find) | Q(comment__icontains=find) )
リストを使って検索する
書いた名前を全て検索する
例のごとく、views.pyを編集する
code:quiz/views.py
list = find.split()
data = Quiz.objects.filter( name__in=list )
https://scrapbox.io/files/62b2a5168e6d8b001dd4528b.png
試してみる。
https://scrapbox.io/files/62b2aef4c1ab68001e1b5016.png
クエリーの内容が検索された(これ部分一致じゃないのね)
振り返り
とりあえず...
Chpter3まで終わった!えらい!!
あとは、ホームページの中身をちょこっと変えて、scrapboxの内容整理ですね。あともう少しだからガンバロ。
おまけ
ちょこっとUIを変えました
titleを押すと、ホーム画面に戻ってこれるようにした
ホーム画面に、id, name, price, data, comment を表示 ⇦ 要改善
createとfindに飛べるようにした ⇦ 要改善
とりあえず、今日は終わるか。
ちょっとだけ、サイトの方改良していきます
今は、問題集検索のところで名前検索だけじゃなくてコメント検索もできるようにしようと、粘っておりやす
https://scrapbox.io/files/62b57043bd0281001da50cc8.png
こんな感じで、検索窓をどうにか増殖できないか考えてるところです。この状態だと一応、問題集名検索とコメント検索はできてるみたいだけど...うーんといったところ。
いや...検索窓1つで、どっちもできるようにすればいいんでは?
→ということで、コードをちと修正
code:quiz/views.py
def find(request):
...
data = Quiz.objects.filter( Q(name__icontains=find) | Q(comment__icontains=find) )
...
これでどっちも検索できるようになった。てか、このコード前の方に書いてあったわ。ありがとう過去の自分。
https://scrapbox.io/files/62b573bb6744e7001de00291.png
https://scrapbox.io/files/62b573aca4a083001de08d25.png
これで、1つの検索窓で問題集名検索とコメント検索ができるようになった◎
とりあえず、サイトの開発はこれくらいで大丈夫...だろう
振り返り
ものは作れたので安心&満足〜。後は、scrapboxの内容を整理した方がいいのかな...?
→やるやるやるやる(てかやらなきゃ)
いやてか本当に、よくここまで作れたなと感じてしまった...最初の手探り状態からよくここまで形にしたなと...(まあ、よかったか悪かったかは別にして)
https://scrapbox.io/files/62bea6e5b73af4001de03aaa.png
色々書いた。もうちょっと各フェーズに絞って書いてもよかったな。
メモ
enPiTの体験談...!まだenPiTで何するか分かってないから、是非とも去年のやつ参考にしたい
スクラムも理論だけ学んで実際がわかってないから、スクラムガイドも適宜参考にしていきたい
ガッシュクコワイ...(息抜きできるもの、用意しておかないとな)
LTネタ、今のうちから考えないとな
適宜、他のチームを見て回る(自分のチームに拘らない...)
わいわいしたい!