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値が取得できないのでエラーになります。
うまい解決方法が見つかったら、別の記事に書きたいと思います。
Djangoでtwitter認証をしたあとにツイートする方法
前提として、social-auth-app-django
を使ってtwitterでの認証が実装できている状態とします。
class TwitterPost(TemplateView): post_api = "https://api.twitter.com/1.1/statuses/update.json" def post(self, request, *args, **kwargs): social_user = UserSocialAuth.objects.filter(user=request.user).first() # ここでバリデーションを入れるべきでしょう。 twitter_oauth = OAuth1Session( SOCIAL_AUTH_TWITTER_KEY, SOCIAL_AUTH_TWITTER_SECRET, social_user.tokens.get('oauth_token'), social_user.tokens.get('oauth_token_secret'), ) params = {"status": 'twitter post api test.', "lang": "ja"} result = twitter_oauth.post(self.post_api, params) return redirect('home')
バリデーションのコードは省いています。
これが実行されると、ログインしているユーザがtwitter post api test.
とツイートします。
次にhtmlのテンプレートを(特別なことはやっていませんが)htmlのどこかに入れます。
{% if user.is_authenticated %} <form action="{% url 'twitter_post' %}" method="post"> {% csrf_token %} <button type="submit"> <span>twitterで共有</span> </button> </form> {% endif %}
settings.pyに以下のpathを追加します。
path('twitter_post', TwitterPost.as_view(), name='twitter_post'),
これでOK
Djangoのtemplate内でクエリパラメータを設定する方法
Djangoのtemplate内で以下のように書いているときに、リンク先をクエリパラメータで渡すときの方法です。
<li><a href="{% url 'home' %}">next</a></li>
渡したいパラメータはp
とします。値は整数です。
まず、一番簡単な方法を紹介します。
<li><a href="{% url 'home' %}?p=1">next</a></li>
この方法はシンプルですが、パラメータが増えたときに大変なことになります。
具体的には以下のようにどこからどこまでがパラメータとして設定しているのか分かりづらくなります。
{% url 'home' %}?p=1&s=23&d=201810&f=90
この問題を解決するための少し、面倒な方法を紹介します。
以下のようなディレクトリ構成でtemplate_tags/tags.py
というファイルを作成します。
. ├── manage.py ├── project │ ├── __init__.py │ ├── assets │ ├── django.log │ ├── settings │ ├── templates │ ├── urls.py │ └── wsgi.py └── template_tags ├── __init__.py └── tags.py
tags.py
の内容は以下のように書きます。
from django import template register = template.Library() @register.simple_tag def query_transform(request, **kwargs): updated = request.GET.copy() for k, v in kwargs.items(): updated[k] = v return updated.urlencode()
これはDjangoのcustom templateという機能で使えるものです。
https://docs.djangoproject.com/en/2.1/howto/custom-template-tags/
次に、settings.py
を修正します
libraries
のところから追加します。
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], 'libraries': { # ← これを追加する 'tags': 'template_tags.tags', } }, }, ]
最後に、template(html)を修正します。
どこでも良いのですが、以下の記述を書きます。
私の場合は<body>
直下に書いています。
<body> {% load tags %} ...
tags
はlibraries.tagsの名称です。つまり、template_tags.tagsで登録しているタグを読み込むということです。
ではタグを使ってリンクを書き直します。
<li><a href="{% url 'home' %}?{%query_transform request p=1%}">next</a></li>
だいぶスッキリしました。
複数のパラメータを渡したい場合は{%query_transform request p=1 q=2%}
となります。