はみ出す文字列を三点リーダーにする方法
領域をはみ出したときに三点リーダー(…)にする方法を紹介します。
既出かと思いますが、詳しい内容とかあまり見つからない(こうやればできるというのは見つかる)ので、私なりに調べた内容を載せます。
領域をはみ出したときに三点リーダー(…)にする場合は以下のように設定します。
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)
Djangoのテンプレート内でcloudinaryのディレクトリを指定して画像を表示する
Djangoを使って、cloudinaryで保存している画像を表示する場合、{% cloudinary %}
を使用します。
その際に、ファイル名を指定することができるのですが、ファイル名のみだとディレクトリ分けしていた場合表示されません。
そこでなんとかディレクトリを指定して、画像を表示したいと思いました。
そこで取った方法が以下の書き方です。
{% cloudinary public_id crop="fill/dir" class="is-rounded" %}
これで出力されるurlは以下のようになります。
http://res.cloudinary.com/hqarhdoek/image/upload/c_fill/dir/image_name
ポイントはcrop
部分です。
もともとはcropの値はfill
だけで、その場合のurlは以下のようになります。
http://res.cloudinary.com/hqarhdoek/image/upload/c_fill/imange_name
ちなみに、folder="dir"としてもだめでした。
問題点
widthやheightを指定する場合、以下のようなurlになります。
'https://res.cloudinary.com/demo/image/upload/c_fill,h_150,w_100/sample.jpg'
すると、fill/dir
のようになってもうまく動きません。
あくまでもファイル名の前の区切りがフォルダとしなければなりません。
その場合は、おとなしくサーバーサイドでファイル名をつくりましょう。
herokuに上げて動作を確認してると、imageが404になったりしたので、この方法は間違っているのかもしれません。
その検証はしてなくて、他のバグを直してるときについでに解決していたので未検証です。
ただ、ローカルでは動いていたのでいまいち原因がわかってないです。
Djangoでログイン後に特定の処理を挟む方法
表題の通りですが、Djangoでログイン後に一回だけ処理を入れたいときの方法を考えて見ました。
一つしか思いつかなかったのですが、紹介したいと思います。
やることをは簡単で、ログイン後のリダイレクトに一つだけviewを挟む形です。
まずは、settings.pyに以下の項目を追加します。
LOGIN_REDIRECT_URL = 'before-home'
これを追加すると、ログイン後にbefore-home
にリダイレクトされます。
before-home
というのはurls.pyにnameで設定した名前を入れます。
次にview.pyを修正します。
class GoToHomeView(RedirectView): pattern_name = 'home' def get_redirect_url(self, *args, **kwargs): request = self.request if request.user.is_authenticated: # ログインしている場合は、なにか処理する # is_authenticatedを入れなくても良いですが、ログインしてるとは限らない self.execute(request.user) return super().get_redirect_url(*args, **kwargs) def execute(self, user): # なにか処理
RedirectViewについては説明は↓に記載されています。
https://docs.djangoproject.com/ja/2.1/ref/class-based-views/base/#redirectview
コードの例では、pattern_nameにhome
を指定していて、リダイレクトするとhome
が呼ばれます。
get_redirect_url
をオーバーライドしており、ログイン後に処理が実行されるようになります。
if request.user.is_authenticated:
でセーフコードを入れる理由としては、ログインしなくてもbefore-home
が実行される可能性があるからです。
@require_login
を入れても同じことができると考えたのですが、以下のコードでつまづきました。
def decorator(view_func): @wraps(view_func) def _wrapped_view(request, *args, **kwargs): if test_func(request.user):
このコードはfrom django.contrib.auth.decorators
内のコードで、login_required
が実行されたときに呼ばれます。
その際にRedirectView
ではuser値が取得できないのでエラーになります。
うまい解決方法が見つかったら、別の記事に書きたいと思います。