■
DjangoでSerializerを使ったバリデーション方法について
DjangoではRestApiを作成するための便利なライブラリとしてDjango REST framework
というものがあります。
このライブラリを使うと、簡単にapiを作成することができます。
この記事ではDjango REST framework
で用意されているserializer
を使ったバリデーション方法を紹介します。
また、Django REST framework
でviewを表示する方法についても紹介します。
views.py
views.py
ではviewsetを使って実装しています。
action
を使って、get
やpost
といったメソッドを作らないようにしました。
action
を使った場合はurls
を少し工夫する必要がありますが、それについてはurls.pyを見てください。
from django.http import JsonResponse from django.shortcuts import render # Create your views here. from rest_framework import viewsets from rest_framework.decorators import action from apps.xxxxxxx.serializer import SampleSerializer class SampleViewSet(viewsets.ModelViewSet): serializer_class = SampleSerializer @action(detail=False, name='sample_post_api') def sample_post(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) if not serializer.is_valid(): return JsonResponse({"status": 400}, status=404) return JsonResponse(serializer.data, status=200) @action(detail=False, name='sample_get_open_view') def sample_get(self, request, *args, **kwargs): return render(request, 'home.html', status=200)
saple_get
ではrenderというショートカットを使用してhome.html
を表示しています。
restframework
のサンプルでは以下のように実装されているので、以下の方法でも良いです。
@action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer]) def highlight(self, request, *args, **kwargs): snippet = self.get_object() return Response(snippet.highlighted)
serializer.py
countというフィールドを持っています。
countは-1か1の値を受け付けます。
IntegerField
ではmin_value
とmax_value
を使えますが、0は許可してほしくありません。
そういった場合はvalidation_xxxx
というようにバリデーションを追加できます。
from rest_framework import serializers class SampleSerializer(serializers.Serializer): count = serializers.IntegerField(min_value=-1, max_value=1) def validate_count(self, count): if count == 0: raise serializers.ValidationError("count is not allowed zero.") return count
新しいシリアライザクラスを作成したほうが良いのかもしれません。
urls.py
views.py
で作成したapiを登録します。
restframework
ではrouterというものを使うのが一般的だと思いますが、この記事ではurlpatternsをそのまま使ってます。
routerについてはこちらを参照してください。
https://www.django-rest-framework.org/api-guide/routers/
また、検索するとよく見つかるurl()
というのの代わりにpath
とre_path
を使用しています。
理由はurlが廃止される可能性があるからです。
from django.contrib import admin from django.urls import path, re_path from apps.xxxxxxxx.views import SampleViewSet # admin urlpatterns = [ path('admin/', admin.site.urls), ] # sample api and view urlpatterns += { re_path(r'sample/?$', SampleViewSet.as_view(actions={ 'get': 'sample_get', })), path('api/sample/post', SampleViewSet.as_view(actions={ 'post': 'sample_post' })), }
.as_view
の’action
を使って登録します。
見るだけでわかると思います。
getでリクエストが来たら、sample_get
メソッドへ。
postでリクエストが来たら、sample_post
メソッドへ。
ちなみに、以下のように実装しないのには理由があります。
urlpatterns += { re_path(r'sample/?$', SampleViewSet.as_view(actions={ 'get': 'sample_get', 'post': 'sample_post' })), }
このようにすると、/sample
にリクエストした際に、restframework
が用意している管理画面を表示できません。
postをテストしたいのに、getが最初に表示されるので、管理画面が表示されないのです。
ハロウィンだしネタアプリを作ってみた話
タイトルの通り、ネタアプリを作ってみました。
作ったアプリケーションはこちらです。
「進捗どうですか?」というアプリケーションで、作成時間は1~2時間ぐらいです。
使用したフレームワークや環境を箇条書きにするとこんな感じです。
- Django2.1.2
- heroku free
- python3.6.4
- heroku postgresql
- domain 1円
これまで書いてきた記事を参照しつつ短時間で作ることができました。
ブログを書き続けてよかったなと感じた瞬間です。
あと、webサービスを作りたい人や、プログラミングの勉強をしている人は今回私が作ったような小さなアプリケーションから始めると良いと思います。
小さなアプリケーションでも、大きなアプリケーションでもやることは変わらないからです。
設計して、開発して、テストして、デプロイして、、あれこれと、リリースまでにやることが体験できます。
最初は時間がかかりますが、何度かやっているとやり方が分かるので、3回ぐらいやるとパパっと作れるようになります。
そして、慣れたら大きなサービスを作ってみましょう。
きっとうまくいきます
はみ出す文字列を三点リーダーにする方法
領域をはみ出したときに三点リーダー(…)にする方法を紹介します。
既出かと思いますが、詳しい内容とかあまり見つからない(こうやればできるというのは見つかる)ので、私なりに調べた内容を載せます。
領域をはみ出したときに三点リーダー(…)にする場合は以下のように設定します。
span.ellipsis { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
spanタグのclassにellipsis
を設定した場合、はみ出した部分を三点リーダーにします。
spanタグが対象なのは特に意味はないです。強いて言うならellipsisの使い方をわかりやすくするためです。
※ このスタイルでは複数行の対応はできません。
overflow:hiddenについて
領域をはみ出した際に、はみ出したものを表示しないようにします。
scrollとか、autoにするとスクロールバーが出るやつです。
余談ですが、最近はscrollとかautoは使わなくてperfect-scrollbar.js
というのを使ってます。
white-space:nowrapについて
空白文字の扱いを決めています。
nowrapを指定すると、一行で表示されます。
preでもできると思いますが、改行やタブが残るのでうまくいかない場合があると思います。
こちらのページがわかりやすいと思います。
https://developer.mozilla.org/ja/docs/Web/CSS/white-space
text-overflow: ellipsisについて
はみ出した部分を三点リーダーにします。
おまけ
youtubeのサイドバーを見てみると、同じようなことをやってるのがわかります。
さらに、右側に24pxの余白をいれて、見やすくするというテクニックを発見できます。
24pxというのは1.5rem(1rem=16pxのとき)なので何か意味があるのだと思います。
なんの意味があるのかはわからないです。
margin-right: 24px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: 1.4rem; font-weight: 400; line-height: 2.1rem; -ms-flex: 1 1 0.000000001px; -webkit-flex: 1; flex: 1; -webkit-flex-basis: 0.000000001px; flex-basis: 0.000000001px;
lxmlを使ったxmlのパース方法
lxmlというライブラリを使ってxmlをパースしたときのメモを記事にしています。
lxmlはBeautifulSoupというスクレイピングのライブラリでも使われたりしなかったりします。
xmlファイルを取得する
pythonでファイルを読み込む方法はいろいろありますが、再帰的にファイルを取得しつつ、指定したディレクトリ以下のxmlファイルをすべて取得します。
from lxml import etree from pathlib import Path def get_xml_files(target): files = list(Path(target).glob('**/*.xml')) return files
get_xml_files
を実行すると、指定したディレクトリ以下のxmlファイルのパスがすべて取得できます。
このパスをlxmlで読み込んで、利用します。
lxmlでxmlをパースする
get_xml_files
を使って、ファイルまでのパスを取得後にパース処理をします。
xml_files = get_xml_files(xml_target) for xml_file in xml_files: xml_file_name = str(xml_file) tree = etree.parse(xml_file_name)
xml_file
はPosixPoth
クラスのインスタンスなので、そのままetree.parse
に渡せません。
一度strで文字列にしてからparseします。
コメントの削除方法
xmlのコメントが残っていると、forでループする際にcommentが引っかかります。
これを解消するために、removeしようと考えたのですが、remove系の関数がありませんでした。
理想ではtree.rm_comments()
とかetree.rm_comments()
があったらよかったです。
というわけで、対処方法が以下のプログラムです。
def remove_comments(tree): comments = tree.xpath('//comment()') for comment in comments: parent = comment.getparent() parent.remove(comment)
tagを判定する
以下のプログラムは、mybatisというjavaのormで使うxmlをパースする際に使った関数です。
xmlの中身はこのようなものです。
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd"> <sqlMap namespace="jp.hoge.com"> <select id="selectId" resultClass="Sample" parameterClass="string"> SELECT id FROM sample WHERE id = 1 </select> </sqlMap>
このxmlをパースしてステートメントごとに配列にしています。
def get_statements(tree): statement_elements = [] for element in tree.iter(): if element.tag in ['select', 'insert', 'update', 'delete', 'procedure']: statement_elements.append(element) return statement_elements
属性を取得する際はelement.get('id')
のようにします。
tree.iter()
でelementを取得できますが、少し癖があります。
深さ優先探索のようにタグを取得していくので、書くのが難しかったです。
一度に欲しい情報を取得するのではなく、個別に取得する方法を採用しました。
例えば、以下のようなxmlがあった場合、sqlをdictに格納して、selectをパースしている最中にincludeを見つけたらdictを参照して、中身を置き換えます。
<sqlMap namespace="jp.hoge.com"> <sql id="sampleInclude"> where id = 2 </sql> <select id="selectId" resultClass="Sample" parameterClass="string"> SELECT id FROM sample <include refid="sampleInclude" /> </select> </sqlMap>
bulmaのpanelのヘッダーにボタンを付ける
bulmaというcssフレームワークでpanelというものがあります。
このpanelではpanel-headingというのがあるのですが、このヘッダーに編集ボタンは用意されていません。
この記事ではbulmaのpanelのヘッダーに編集ボタンを付ける方法を紹介します。
panelについてのドキュメントはこちらです。
https://bulma.io/documentation/components/panel/
最終的にこのようなものができます。
html
panel
のテンプレートに編集を追加しています。
<nav class="panel"> <p class="panel-heading is-size-6 sp-panel-heading"> 管理 <a class="button is-small sp-right" href="edit"> <span>編集</span> <span class="icon is-small"> <i class="fas fa-cog"></i> </span> </a> </p> <a class="panel-block" href=""> <span class="panel-icon"> <i class="fas fa-flag" aria-hidden="true"></i> </span> <span class="is-size-7">お気に入り</span> </a> </nav>
追加している部分はこの部分です。
<a class="button is-small sp-right" href="edit"> <span>編集</span> <span class="icon is-small"> <i class="fas fa-cog"></i> </span> </a>
css
cssは殆どをbulmaで補っているので、2つ追加するだけです。
.sp-panel-heading { display: flex; align-items: center; } .sp-panel-heading .sp-right { margin-left: auto; }
bulmaのbutton is-small
でも、高さが合わずに管理
という文字がずれます。
そのため、flex
の指定とalign-items: center
で調整しています。
ボタンを右寄せするためにmargin-left: auto;
をいれています。
注意
margin-left: auto;
を入れていますが、ヘッダーに入れる文字によってはレイアウトが崩れる恐れがあります。
その場合はflex: 1;
などで調整するのが良いでしょう。
画像の上に文字列を表示する方法
htmlとcssを使って、画像にマウスを置くと文字が出てくる部品の作り方を解説します。
今回javascriptは使用しません。また、scssも使いません。もちろんwebpackとかビルドは必要ないです。
最終的に↓のようなものを作れます。
htmlのコード
htmlのコードはこのようになっています。
<div> <div class="sp-image-wrap"> <img src="https://bulma.io/images/bulma-banner.png" alt=""> <span class="sp-description">Bulma is an open source CSS framework based on Flexbox and built with Sass. It's 100% responsive, fully modular, and available for free.</span> </div> <span>Bulma: a modern CSS framework based on Flexbox</span> </div>
imgのsrcにはサンプルとしてbulmaというcssフレームワークの画像を使っています。
今回の実装には関係ないですが、とても使いやすく、便利なコンポーネントが揃っています。
htmlでイメージ部分と、画像の下に出す文字列を分けています。
sp-description
をimgの上に表示されているように見せます。
cssのコード
cssの書き方はたくさんあると思いますが、ひとまずコードを載せます。
.sp-image-wrap { position: relative; } .sp-image-wrap:before { position: absolute; content: ''; background-color: #c3c3c3a3; top: 0; left: 0; width: 100%; height: 100%; visibility: hidden; z-index: -1; } .sp-image-wrap .sp-description { position: absolute; top: 0; bottom: 0; height: 100%; width: 100%; padding: 16px; overflow-y: scroll; visibility: hidden; z-index: -1; transform: translateY(-10px); transition: transform .3s ease; } .sp-image-wrap:hover img { filter: blur(5px); } .sp-image-wrap:hover:before { visibility: visible; z-index: 10; } .sp-image-wrap:hover .sp-description { visibility: visible; z-index: 11; transform: translateY(0px); }
機能を実装する上で工夫した点です。
sp-image-wrap
をrelative
にして、その子要素のsp-description
をabsolute
にしたhover
時に上から降りてくるようにtransform
とtransition
を設定したsp-image-wrap
のbefore
にオーバーレイを設定し、.sp-image-wrap:hover:before
とすることでhover時にbeforeを表示するようにした- そのままオーバーレイを表示すると、背景画像が残ってしまって見えづらいため
filter:blur
を使ってモザイク処理した display:none
を使うのではなく、アニメーションすることも考えてvisibility
を仕様した
stackoverflowのapiをpythonで使ってみた
stackoverflowのoauthを試してみたので、そのときにやったことを紹介します。
基本的なステップについてはすべてドキュメントにかかれていたので、試す際に書いたコードを載せます。
ドキュメントはこちらです。
https://api.stackexchange.com/docs/authentication#scope
import requests import json client_id = '12345' client_secret = 'xxxxxxxxxxxxxxx' scope = 'read_inbox' redirect_uri = 'http://localhost' def step1(): step1 = requests.get( 'https://stackoverflow.com/oauth', { 'client_id': client_id, 'scope': scope, 'redirect_uri': redirect_uri, } ) print(step1) print(step1.url) def step2(code): step2 = requests.post( 'https://stackoverflow.com/oauth/access_token/json', { 'client_id': client_id, 'client_secret': client_secret, 'code': code, 'redirect_uri': redirect_uri, } ) print(step2) obj = json.loads(step2.text) access_token = obj.get('access_token') expires = obj.get('expires') print(access_token, expires) '{"access_token":"xxxxxxxxxxxxxxx","expires":86400}' if __name__ == '__main__': step1() # step2()
step1について
step1ではcodeというものを取得します。
requests.getで取得した文字列の中に、↓のようなurlが含まれているので、そのurlをブラウザで開きます。
https://stackoverflow.com/users/login?xxxxxxxxxxxxxxxxxx
Django等で作成している場合は、urlにリダイレクトする感じで良いはずです。
ブラウザで開くと、stackoverflowのログイン画面が開きます。
ユーザーがアプリケーションのアクセスを許可すると、リダイレクトurlへリクエストが来ます。
サンプルのコードは、サーバーサイドの実装はしていないので、ブラウザのurlを見ます。
ここで取得したcodeをコピーして、step2を実行します。
step2
step2は簡単です。
step1で取得したcodeをpostで送ると、access_tokenとexpireを取得できます。
このaccess_tokenを使って、ユーザーが持っている情報を取得できます。
DjangoではSocialAuth用のテーブルにaccess_tokenを保存していたので、DBに保存しても良いでしょう。
そして、有効期限が切れたらもう一度tokenを発行してもらう形で対応できると思います。
おまけ
認証が必要ないapiもあります。
stackoverflowでログインが不要な処理はできます。
ドキュメントはこちらです。
https://api.stackexchange.com/docs
例えば、回答一覧を取得するなら↓で実行できます。
def get_answers(): url = 'https://api.stackexchange.com/2.2/answers?order=desc&sort=activity&site=stackoverflow' r = requests.get(url) print(r.text)