Vue.js 入門 vol.3
参考サイト
https://jp.vuejs.org/v2/guide/class-and-style.html
クラスとスタイルのバインディング
<!-- isActiveがtrueの場合, classはactiveになる --> <div v-bind:class="{ active: isActive }"></div> <!-- hasErrorがtrueの場合、text-dangerが有効になる --> <div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }"> </div>
data: { isActive: true, hasError: false } //上記のようにデータを与えた場合以下のhtmlのコードのよな結果になる。
<!-- 上記のコードの結果 --> <div class="static active"></div>
- 以下のようにも表示可能
<div v-bind:class="classObject"></div>
data: { classObject: { active: true, 'text-danger': false } }
- 算出プロパティでもclassを制御可能。これが一般的ぽいぞ。
<div v-bind:class="classObject"></div>
//.. data: { isActive: true, error: null }, computed: { classObject: function () { return { active: this.isActive && !this.error, //isActiveがtrueで、errorが無い場合 'text-danger': this.error && this.error.type === 'fatal', } } }
配列構文
- v-bind:class にクラスのリストを適用する配列を渡すことができます
<div v-bind:class="[activeClass, errorClass]"></div>
data:{ activeClass: 'active', errorClass: 'text-danger' }
<!-- 上記のコードの結果 --> <div class="active text-danger"></div>
- 条件付きでリストを切り替える場合
<div v-bind:class="[isActive ? activeClass : '', errorClass]"> <!-- isActiveがtrueのときだけ、activeClassが適用される --> <div v-bind:class="[{ active: isActive }, errorClass]">
コンポーネントにおいて
- 以下のようなコンポーネントがあったとして、いくつかのクラスを追加してみる。
Vue.component('my-component', { template: '<p class="foo bar">Hi</p>' })
<my-component class="baz boo"></my-component> <!-- 結果として以下のhtmlが描画される --> <p class="foo bar baz boo">Hi</p>
- class bindingでも同様に機能する。
<my-component v-bind:class="{ active: isActive }"></my-component> <!-- activeがtrueの場合は、以下の通り --> <p class="foo bar active">Hi</p>
インラインスタイルのバインディング
オブジェクト構文
- v-bind:style はほとんどcssの文法に近い。
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: { activeColor: 'red', fontSize: 30 }
- templateをきれいにするために、styleObjectを作成するのもあり!
<div v-bind:style="styleObject"></div>
data: { styleObject: { color: 'red', fontSize: '13px' } }
- 配列構文もある。
<div v-bind:style="[baseStyles, overridingStyles]">
- 複数の値も配列で設定できる。
<div v-bind:style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }">
条件付きレンダリング
- v-if を使う。
<h1 v-if="ok">Yes</h1> <h1 v-else>No</h1>
- v-if をグループに適用したい場合は、
<template>
を使う。最終的に描画される結果は、 要素は含まれません。すごい!
<template v-if="ok"> <h1>Title</h1> <p>Paragraph 1</p> <p>Paragraph 2</p> </template>
- v-elseは、v-ifの直後でないとだめ。
<div v-if="Math.random() > 0.5"> Now you see me </div> <div v-else> Now you don't </div>
- v-else-if もある。
<div v-if="type === 'A'"> A </div> <div v-else-if="type === 'B'"> B </div> <div v-else-if="type === 'C'"> C </div> <div v-else> Not A/B/C </div>
key による再利用可能な要素の制御
- 以下のコードで、logintypeを切り替えても、usernameで入力したvalueがそのまま残ってしまう。
<template v-if="loginType === 'username'"> <label>Username</label> <input placeholder="Enter your username"> </template> <template v-else> <label>Email</label> <input placeholder="Enter your email address"> </template>
- そのため、
Vue は”この 2 つの要素は完全に別個のもので、再利用しないでください”
と伝える必要がある。key属性を追加するだけ。
<template v-if="loginType === 'username'"> <label>Username</label> <input placeholder="Enter your username" key="username-input"> </template> <template v-else> <label>Email</label> <input placeholder="Enter your email-address" key="email-input"> </template>
- v-show。v-ifとの違いは、v-showはtrueだろうが、falseだろうが、必ずDOMに維持される。
<h1 v-show="ok">Hello!</h1>
頻繁に何かを切り替える必要があれば v-show, 条件が実行時に変更することがほとんどない場合は、v-if を選びます。(v-ifのほうが、切り替えコストが高く(DOMの書き換えがあるため?)、v-showは、初期描画コストが高い(基本的に、すべてDOMに描画が維持されるため))
v-if と v-for : v-forは、v-ifより優先度が高い。
リストレンダリング
- v-for : item in items の形式で特別な構文を要求し、items はソースデータの配列で、item は配列要素がその上で反復されているエイリアスです(エイリアスは、シンボリックリンクと同義)
<ul id="example-1"> <li v-for="item in items"> {{ item.message }} </li> </ul>
var example1 = new Vue({ el: '#example-1', data: { items: [ { message: 'Foo'}, { message: 'Bar'} ] } }) // 結果 // Foo // Bar
- 配列を表示することも可能。
<ul id="example-2"> <li v-for="(item, index) in items"> {{ parentMessage }} - {{ index }} - {{ item.message }} </li> </ul>
var example2 = new Vue({ el: '#example-2', data: { parentMessage: 'Parent', items: [ { message: 'Foo'}, { message: 'Bar'} ] } })
- テンプレートでのv-for
<ul> <template v-for="item in items"> <li>{{ item.msg }}</li> <li class="divider"></li> </template> </ul>
- リストではなく、オブジェクトに対してもv-forを使って反復処理できる。(pythonでいうところの、辞書型か)
<ul id="repeat-object" class="demo"> <li v-for="value in object"> {{ value }} </li> </ul>
new Vue({ el: '#repeat-object', data: { object: { firstName: 'John', lastName: 'Doe', age: 30 } } })
- オブジェクト(辞書型)に対するv-forでは、key, indexもゲットできる。(順序は、Object.keys()の順番らしい。一貫性は保証されていないとのこと)
<div v-for="(value, key, index) in object"> {{ index }}. {{ key }} : {{ value }} </div>
- 範囲のv-for: 整数値も取ることができる。
<div> <span v-for="n in 10">{{ n }}</span> </div>
<my-component v-for="item in items" :key="item.id"></my-component>
<my-component v-for="(item, index) in items" v-bind:item="item" v-bind:index="index" v-bind:key="item.id" > </my-component>
- ToDoリストの例
<div id="todo-list-example"> <input v-model="newTodoText" *inputしたときに、valueを、newTodoTextに渡す v-on:keyup.enter="addNewTodo" * enterを押したときに、addNewTodoを実行する placeholder="Add a todo" * placeholderをセットする > <ul> <li is="todo-item" ※コンポーネントのimport v-for="(todo, index) in todos" ※ todosは、Vueのdataプロパティ のtodos v-bind:key="todo" ※コンポーネントに、todo(todosの中身)を渡す v-bind:title="todo" *コンポーネントの変数 titleに、todo(todosの中身)を渡す v-on:remove="todos.splice(index, 1)" * todosから削除する ></li> </ul> </div>
Vue.component('todo-item', { template: ` <li> {{ title }} <button v-on:click="$emit('remove')">X</button> </li> `, props: ['title'] }) new Vue({ el: '#todo-list-example', data: { newTodoText: '', todos: [ 'Do the dishes', 'Take out the trash', 'Mow the lawn' ] }, methods: { addNewTodo: function () { this.todos.push(this.newTodoText) this.newTodoText = '' } } })
- v-for と v-ifだと、v-forを優先する。以下のコードは、for ループしながら、if文で、completeしてないもののみ表示する。
<li v-for="todo in todos" v-if="!todo.isComplete"> {{ todo }} </li>
- if文で条件分岐して、for文を使う場合は、wrapperされる要素にfor文を書く
<ul v-if="shouldRenderTodos"> <li v-for="todo in todos"> {{ todo }} </li> </ul>
- key は、for文の各要素を区別するために使うことができる。v-bindを使う。可能な場合はこれを使ったほうがいい。
<div v-for="item in items" :key="item.id"> <!-- content --> </div>
配列の変化を検出
- 配列の変更のメソッドは以下のものが使える。
push() pop() shift() unshift() splice() sort() reverse()
- 配列の置き換え
example1.items = example1.items.filter(function (item) { return item.message.match(/Foo/) })
- 注意事項: vueは配列で以下の変更を検出することができない。
// 1. インデックスでアイテムを直接設定するとき。例: vm.items[indexOfItem] = newValue // なので、以下のように設定する。 example1.items.splice(indexOfItem, 1, newValue) // 2. 配列の長さを変更するとき。例: vm.items.length = newLength example1.items.splice(newLength)
フィルタ/ソートされた結果の表示
- 算出プロパティ使うのが一番良さそう。
<li v-for="n in evenNumbers">{{ n }}</li>
data: { numbers: [ 1, 2, 3, 4, 5 ] }, computed: { evenNumbers: function () { return this.numbers.filter(function (number) { return number % 2 === 0 }) } }
- 算出プロパティが使えない場合は、メソッドも可。
<li v-for="n in even(numbers)">{{ n }}</li>
data: { numbers: [ 1, 2, 3, 4, 5 ] }, methods: { even: function (numbers) { return numbers.filter(function (number) { return number % 2 === 0 }) } }
まとめ
Vue.js 入門 vol.2
参考サイト
https://jp.vuejs.org/v2/guide/instance.html
コンストラクタ
すべてのVue vmは、Vueコンストラクタ関数(new Vue)で、root Vue instance を作成することから始まる。root Vue instanceは、Vue instanceとはちょっと違うと思うので、注意。
Vue constructorを拡張することで、再利用可能な ComponentConstructorを生成できる。
var MyComponent = Vue.extend({ //extendで、コンストラクタを拡張できる。 }) var MyComponentInstance = new MyComponent() // MyComponentから、生成されるinstanceは、extendオプションで定義した拡張オプションを利用して生成される。
プロパティとメソッド
Vue インスタンスは、自身の data オブジェクトの全てのプロパティをプロキシします:
var data = { a: 1} var vm = new Vue({ data: data }) vm.a === data.a // true になる vm.a = 2 data.a // これは2になる data.a = 3 vm.a // これは3にtなる
- 上記のような状態を、dataオブジェクトのすべてのプロパティをプロキシする と表現するらしい(^^)ようは、どちらが変更するとそれに合わせてもう一方も変更するということか。
var data = {a: 1} var vm = new Vue({ el: '#example', data: data }) vm.$data === data // true vm.$el === document.getElementById('example') // true vm.$watch('a', function(newVal, oldVal){ // このコールバックは、'vm.a'の値が変わるときに呼ばれる。 })
上記で
vm.data
,vm.el
ではなく、vm.$data
,vm.$el
となっている理由は、vue instanceの場合(javascriptでも同じなのか?)は、vm.dataで定義したプロパティ を呼び出すように設定されているので、vm.elではたぶん呼び出しができない。たぶん、vue instanceのプロパティ、メソッドの呼び出しには、デフォで、$が必要な気がする。$watch
は、指定した変数などに変更があったときに、コールバックするメソッドを指定する。
インスタンスライフサイクルフック
- lifecycle hooks の呼び出し。createdは、instance生成後にコールされる。
var vm = new Vue({ data: { a: 1 }, created: function(){ // created は、instance 生成後にコールされる! // 'this' は、vm インスタンスを指します。 console.log('a is: ' + this.a) } }) // -> "a is: 1"
- mounted, updated, destroyed などがある。
- すべてのlifecycle hooksは、
this
がVueインスタンスを指す。
テンプレート構文
展開
- テキストの場合
<span>Message: {{ msg }}</span> <span v-once>This will never change: {{ msg }}</span> <!-- v-once directiveは、1度だけ展開することができる -->
- 生のhtml(基本使用しないのか?)
<div v-html="rawHtml"></div> <!-- v-htmlを利用する -->
- 属性 HTML属性の内部では、
{{}}
は使えないので、v-bindを使う。
<div v-bind:id="dynamicId"></div> <button v-bind:disabled="someDynamicCondition">Button</button>
- Javascript式の使用。
{{}}
の中で、javascriptの式を使える。v-bindでは、""
の中で使える。
{{ number + 1 }} {{ ok ? 'YES' : 'NO' }} {{ message.split('').reverse().join('') }} <div v-bind:id="'list-' + id"></div> <!-- 以下は動作しない。単一の式ではないため --> <!-- これは文であり、式ではありません: --> {{ var a = 1 }} <!-- フロー制御もいずれも動作しません。三項演算子を使用してください --> {{ if (ok) { return message } }}
ディレクティブ
- v-if文の例
<p v-if="seen">Now you see me</p>
- 引数
<a v-bind:href="url"></a> <!-- href 属性に、urlを代入する --> <a v-on:click="doSomething"> <!-- clickすろと、doSomethingメソッドを実行する -->
- 修飾子(modifier)
<form v-on:submit.prevent="onSubmit"></form> <!-- submitのときに、event.preventDefault()を呼び出して、onSubmitを実行する --> <!-- preventDefault()は、本来の動作を停止して、別の動作を実行するときに使う --> <!-- 例えば、入力フォームで、エラーを検知したときは、submitせずに、alertを表示させるなど -->
- フィルタ(mustache 展開と v-bind 式)
{{ message | capitalize }} <!-- in v-bind --> <div v-bind:id="rawId | formatId"></div>
// * Vue instance内のフィルタ関数 new Vue({ // ... filters: { capitalize: function (value) { if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) } } })
- フィルタの連結、引数
{{ message | filterA | filterB }} {{ message | filterA('arg1', arg2) }}
省略記法
- v-bind と v-on は省略できる。
<!-- 完全な構文 --> <a v-bind:href="url"></a> <!-- 省略記法 --> <a :href="url"></a> <!-- 完全な構文 --> <a v-on:click="doSomething"></a> <!-- 省略記法 --> <a @click="doSomething"></a>
算出プロパティとウォッチャ
複雑なロジックには、算出プロパティを使う。
基本的な例
<div id="example"> <p>Original message: "{{ message }}"</p> <p>Computed reversed message: "{{ reversedMessage }}"</p> </div>
var vm = new Vue({ el: '#example', data: { message: 'Hello' }, computed: { // 算出 getter 関数 reversedMessage: function () { // `this` は vm インスタンスを指します return this.message.split('').reverse().join('') } } })
- 算出プロパティ vs メソッド. メソッドでも同じように設定できる。
<p>Reversed message: "{{ reverseMessage() }}"</p>
// コンポーネント内 methods: { reverseMessage: function () { return this.message.split('').reverse().join('') } }
- 結論的には、キャッシュを使うので、算出プロパティの方が早いかもしれないらしい。
算出プロパティ vs 監視プロパティ
- 結論的には、多くのケースでは、算出プロパティを使おうとのこと。
<div id="demo">{{ fullName }}</div>
// こっちは、watch propertyを使った場合 var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar', fullName: 'Foo Bar' }, watch: { firstName: function (val) { // firstNameに変更があった場合に、full nameを変更する。 this.fullName = val + ' ' + this.lastName }, lastName: function (val) { // lastNameに変更があった場合に、fullNameを変更する。 this.fullName = this.firstName + ' ' + val } } })
// こっちが算出プロパティを使った場合 var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar', }, computed: { fullName: function(){ return this.firstName + ' ' + this.lastName } } })
算出 Setter 関数
- 算出プロパティはデフォルトでは getter 関数のみですが、必要があれば setter 関数も使えます(getter functionは、vm.fullNameのように、プロパティから値を取り出す関数のこと。setter functionは、vm.fullName = ‘Joe Yabuki'のように、プロパティに値をsetする関数のこと)
//... computed: { fullName: { //getter get: function(){ return this.firstName + ' ' + this.lastName }, //setter set: function(newValue){ var names = newValue.split(' ') this.firstName = names[0] this.lastName = names[names.length - 1] } } }
watch(ウォッチャ)
- 多くのケースでは、算出プロパティを使ったほうがいいんだけど、watch オプションも豊富のよう。ajaxを利用するときは、watchオプションを使うことが多いのかな。
<div id="watch-example"> <p> Ask a yes/no question: <input v-model="question"> </p> <p>{{ answer }}</p> </div>
<!-- ajax ライブラリの豊富なエコシステムや、汎用的なユーティリティ --> <!-- メソッドがたくさんあるので、Vue のコアはそれらを再発明せずに --> <!-- 小さく保たれています。この結果として、慣れ親しんでいるものだけを --> <!-- 使えるような自由さを Vue は持ち合わせています。 --> <script src="https://unpkg.com/axios@0.12.0/dist/axios.min.js"></script> <script src="https://unpkg.com/lodash@4.13.1/lodash.min.js"></script> <script> var watchExampleVM = new Vue({ el: '#watch-example', data: { question: '', answer: 'I cannot give you an answer until you ask a question!' }, watch: { // この関数は question が変わるごとに実行されます。 question: function (newQuestion) { this.answer = 'Waiting for you to stop typing...' this.getAnswer() } }, methods: { // _.debounce は特にコストの高い処理の実行を制御するための // lodash の関数です。この場合は、どのくらい頻繁に yesno.wtf/api // へのアクセスすべきかを制限するために、ユーザーの入力が完全に // 終わるのを待ってから ajax リクエストを実行しています。 // _.debounce (とその親戚である _.throttle ) についての詳細は // https://lodash.com/docs#debounce を見てください。 getAnswer: _.debounce( function () { if (this.question.indexOf('?') === -1) { this.answer = 'Questions usually contain a question mark. ;-)' return } this.answer = 'Thinking...' var vm = this axios.get('https://yesno.wtf/api') .then(function (response) { vm.answer = _.capitalize(response.data.answer) }) .catch(function (error) { vm.answer = 'Error! Could not reach the API. ' + error }) }, // ユーザーの入力が終わるのを待つ時間をミリ秒で指定します。 500 ) } }) </script>
まとめ
- 算出プロパティ超便利
Vue.js 入門 vol.1
参考サイト
https://jp.vuejs.org/v2/guide/
Vue のチュートリアル
- vueの基本機能をみてみる。
<!-- ここでvueを読み込むする --> <script src="https://unpkg.com/vue"></script> <!-- ここのappは、vue のselector? --> <div id="app"> <!-- {{ }} でcontextの、messageを表示できる --> {{ message }} </div>
var app = new Vue({ el: '#app', // elで #app は、id=app で指定ができるという意味かな? data: { message: 'Hello Vue!' // message } })
- 次に、ディレクティブと言われる機能を使ってみる。
<script src="https://unpkg.com/vue"></script> <div id="app-2"> <!-- v-bindは、この要素の title 属性を Vue インスタンスの message プロパティによって更新して保存する という意味 --> <span v-bind:title="message"> Hover your mouse over me for a few seconds to see my dynamically bound title! </span> </div>
var app2 = new Vue({ el: '#app-2', data: { message: 'You loaded this page on ' + new Date() } })
- ちなみに、app2 のmessageは、
app2.message = "test"
というように、上書きできる。
var app2 = new Vue({ el: '#app-2', data: { message: 'You loaded this page on ' + new Date() } }) app2.message = "test" //この場合、v-bind:title="message"で表示されるのは、"test"に上書きされる
- if文を使ってみる。
<div id="app-3"> <p v-if="seen">Now you see me</p> </div>
var app3 = new Vue({ el: '#app-3', data: { seen: true } }) // app3.seen = false とすると、文章は消える
- for文を使ってみる。
<div id="app-4"> <ol> <li v-for="todo in todos"> {{ todo.text }} </li> </ol> </div>
var app4 = new Vue({ el: '#app-4', data:{ todos: [ { text: 'Learn Javas'}, { text: 'Learn Vue'}, { text: 'Build something'} ] } })
- ユーザー入力の制御。v-onディレクティブを使ってみる。
<div id="app-5"> <p>{{ message }}</p> <!-- v-onディレクティブを使って clickした時に実行する メソッドを呼び出す --> <button v-on:click="reverseMessage">Reverse Message</button> </div>
var app5 = new Vue({ el: '#app-5', data: { message: 'Hello Vue.js!' }, methods: { //methodsで、実行する reverseMessage: function () { this.message = this.message.split('').reverse().join('') } } })
- v-modelを使ってみる。
<div id="app-6"> <p>{{ message }}</p> <input v-model="message"> </div>
var app6 = new Vue({ el: '#app-6', data: { message: 'Hello Vue!' //messageの初期値 } })
Component
- Componentを登録してみる。htmlの方は、呼び出すときのコード
// todo-item と呼ばれる新しいコンポーネントを定義 Vue.component('todo-item', { template: '<li>This is a todo</li>' })
<ol> <!-- todos 配列にある各 todo に対して todo-item コンポーネントのインスタンスを作成する --> <todo-item></todo-item> </ol>
- componentを利用して、todo listを作成してみる。
Vue.component('todo-item', { //<todo-item> selector props: ['todo'], // todo というプロパティを設定 template: '<li>{{ todo.text }}</li>' //todoプロパティのtext属性?を取り出す }) var app7 = new Vue({ el: '#app-7', // id=app-7 data: { groceryList: [ { id: 0, text: 'Vegetables' }, { id: 1, text: 'Cheese' }, { id: 2, text: 'Whatever else humans are supposed to eat' } ] } })
<div id="app-7"> <ol> <todo-item v-for="item in groceryList" v-bind:todo="item"></todo-item> </ol> </div> * v-for="item in groceryList"で、itemを1つずつ取り出す。 * 取り出したitemオブジェクトを、v-bindで、todo-itemコンポーネントの、todoプロパティに渡す。 * todo-itemは、templateを返して、表示する。 * どうもこれは、v-for と、v-bindの順番を逆にしても動作するぽい。
まとめ
- 文法もあんまり難しくなさそうだし、めっちゃよさそう!
Celery(redis) + Django
参照リンク
https://www.codingforentrepreneurs.com/projects/time-tasks/ https://www.codingforentrepreneurs.com/blog/celery-redis-django/ https://redis.io/ http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html#crontab-schedules
Download Redis
- 以下のコマンドで、brew installして、startする。
$ brew install redis $ brew services start redis
- redisを開く
$ redis-cli ping PONG $ redis-server //これでredis-serverがスタートする?
Install Celery & Redis in Virtualenv
- install Celery redis
pip install celery pip install redis pip install django-celery-beat pip install django-celery-results pip freeze > requirements.txt
- djangoのsettings.pyを修正する。
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django_celery_beat', 'django_celery_results', ] CELERY_BROKER_URL = 'redis://localhost:6379' CELERY_RESULT_BACKEND = 'redis://localhost:6379' CELERY_ACCEPT_CONTENT = ['application/json'] CELERY_TASK_SERIALIZER = 'json' CELERY_RESULT_SERIALIZER = 'json' CELERY_TIMEZONE = TIME_ZONE
Celery Module
- celery.py を、settings.pyと同じフォルダ内に作成する。
from __future__ import absolute_import, unicode_literals import os from celery import Celery # set the default Django settings module for the 'celery' program. os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'red.settings') app = Celery('proj') # Using a string here means the worker don't have to serialize # the configuration object to child processes. # - namespace='CELERY' means all celery-related configuration keys # should have a `CELERY_` prefix. app.config_from_object('django.conf:settings', namespace='CELERY') # Load task modules from all registered Django app configs. app.autodiscover_tasks() @app.task(bind=True) def debug_task(self): print('Request: {0!r}'.format(self.request))
- 以下のように自分のアプリに合わせて修正する。
from __future__ import absolute_import, unicode_literals import os from celery import Celery # set the default Django settings module for the 'celery' program. """ここの部分だけ修正した""" os.environ.setdefault("DJANGO_SETTINGS_MODULE", "kirr.settings") app = Celery('proj') # Using a string here means the worker don't have to serialize # the configuration object to child processes. # - namespace='CELERY' means all celery-related configuration keys # should have a `CELERY_` prefix. app.config_from_object('django.conf:settings', namespace='CELERY') # Load task modules from all registered Django app configs. app.autodiscover_tasks() @app.task(bind=True) def debug_task(self): print('Request: {0!r}'.format(self.request))
- また、celery.pyがあるフォルダの、
__init__.py
に以下のコードを追記する。
# cfehome/src/cfehome/__init__.py from __future__ import absolute_import # This will make sure the app is always imported when # Django starts so that shared_task will use this app. from .celery import app as celery_app # noqa
Create a Django app
適当に、
python manage.py startapp billing
などで、アプリを作成する。modelsは以下のように設定しておく。
from django.db import models # Create your models here. class BillingItem(models.Model): item_name = models.CharField(max_length=120) number_1 = models.IntegerField() number_2 = models.IntegerField() total = models.IntegerField() timestamp = models.DateTimeField(auto_now_add=True) def __unicode__(self): return "{total}".format(total=self.total) def __str__(self): return "{total}".format(total=self.total)
- admin.pyを作成しておく。
from django.contrib import admin # Register your models here. from .models import BillingItem admin.site.register(BillingItem)
- billing/tasks.py ファイルを作成する。
from __future__ import absolute_import, unicode_literals import random from celery.decorators import task @task(name="sum_two_numbers") def add(x, y): return x + y @task(name="multiply_two_numbers") def mul(x, y): total = x * (y * random.randint(3, 100)) return total @task(name="sum_list_numbers") def xsum(numbers): return sum(numbers)
Defer Tasks with Celery
2つのターミナルを開いておく。2つとも、Virtualenvを起動させた状態にしておく。
以下のコマンドで、celery を起動させる。
$ celery -A kirr<ここはsettings.pyがあるフォルダ名> worker -l info
// tasksとなっている箇所が、こちらで定義したtask [tasks] . kirr.celery.debug_task . multiply_two_numbers . sum_list_numbers . sum_two_numbers
- 別のターミナルでは、django shellを起動させて、挙動を確認してみる。
$ python manage.py shell >>> from billing.tasks import add, mul, xsum >>> add(123,30) 153 >>> mul(323,23) 66861 >>> xsum([3,2]) 5 >>> add.delay(12,30) <AsyncResult: cf403f0f-9167-48d4-9ca9-6777cdfb3122> >>> mul.delay(323,23) <AsyncResult: b87efba2-2787-44d0-96bc-dc9266bedcfc> >>> xsum.delay([332,333]) <AsyncResult: f2b729c1-a0e2-48ff-b0bf-757388da52c1>
メソッド.delay(メソッドの引数)で設定すると、celeryの方に、答えが表示される!
さらに、この答えを、modelsに代入することが可能。tasks.pyを以下のように修正してみる。
from __future__ import absolute_import, unicode_literals import random from celery.decorators import task from .models import BillingItem @task(name="sum_two_numbers") def add(x, y): return x + y @task(name="multiply_two_numbers") def mul(x, y): number_1 = x number_2 = (y * random.randint(3, 100)) total = number_1 * number_2 new_obj = BillingItem.objects.create( item_name='some item', number_1=number_1, number_2=number_2, total=total) return total @task(name="sum_list_numbers") def xsum(numbers): return sum(numbers)
- 無事保存された!(^^)
Scheduled tasks
- scheduleを設定する。celery.pyにコードを追記する。
from __future__ import absolute_import, unicode_literals import os from celery import Celery # set the default Django settings module for the 'celery' program. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "kirr.settings") app = Celery('proj') # Using a string here means the worker don't have to serialize # the configuration object to child processes. # - namespace='CELERY' means all celery-related configuration keys # should have a `CELERY_` prefix. app.config_from_object('django.conf:settings', namespace='CELERY') # Load task modules from all registered Django app configs. app.autodiscover_tasks() from celery.schedules import crontab app.conf.beat_schedule = { 'add-every-minute-contrab': { 'task': 'multiply_two_numbers', # taskの名前 'schedule': crontab(), # crontabで、設定も可能。crontabは以下のサイト参照 """ http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html#crontab-schedules """ 'args': (16, 16), }, 'add-every-5-seconds': { 'task': 'multiply_two_numbers', 'schedule': 5.0, 'args': (16, 16) }, 'add-every-30-seconds': { 'task': 'tasks.add',# 直接taskのmoduleをimportして指定することも可能 'schedule': 30.0, 'args': (16, 16) }, } @app.task(bind=True) def debug_task(self): print('Request: {0!r}'.format(self.request))
- scheduleされたtaskは以下のコマンドで、自動で実行される。beatを使う。
$ celery -A kirr beat -l info
お〜〜〜、自動で実行されてる。。。
さらに、このスケジュールは、admin画面から簡単に登録できる!すばらしい!
まとめ
Angular4 + Bootstrap4をローカルでテスト
利用したテーマ
http://rawgit.com/start-angular/SB-Admin-BS4-Angular-4/master/dist/dashboard
Angular4 + Bootstrap4のthemeをデプロイ
以下のリンクからテーマをダウンロードする。 http://rawgit.com/start-angular/SB-Admin-BS4-Angular-4/master/dist/dashboard
$ npm install
で必要なパッケージをinstallする。$ng serve
で、ローカルでテストする。。。。動いた!
Angular4 + Django1.11 vol.12
参考サイト
https://www.codingforentrepreneurs.com/projects/angular-django/
Fix Broken Images
以下の動画を元に、defaultのimagepathなどを、S3のurlに修正する。 https://www.codingforentrepreneurs.com/projects/angular-django/fix-broken-images/?play=true
videoListDefaultImage と、video classの、imagepathのdefaultを修正する。
collectstatic と、migrateを忘れない。
Image Field & Uploading Images
- Video modelに、image fieldを追加する。image_path fieldはコメントアウトする。
class Video(models.Model): name = models.CharField(max_length=220) slug = models.SlugField(unique=True, blank=True) embed = models.CharField(max_length=120, help_text='Youtube Embed Code', null=True, blank=True) # image_path = models.CharField(max_length=120,blank=True, null=True, default='https://madangler.s3.amazonaws.com/static/ang/assets/images/nature/4.jpg') image_path = models.ImageField(upload_to='images/', null=True, blank=True) active = models.BooleanField(default=True) featured = models.BooleanField(default=False) timestamp = models.DateTimeField(auto_now_add=True)
$ pip install pillow
する。videso/serializers.py のimage_pathの箇所を修正する。
from rest_framework import serializers from videos.models import Video class VideoSerializer(serializers.ModelSerializer): image = serializers.SerializerMethodField() class Meta: model = Video fields = ['name', 'slug', 'embed', 'featured', 'image'] # obj.imageがある場合は、image.urlの文字列を返す(無い場合は、デフォルトの値を返す) def get_image(self,obj): if obj.image: return str(obj.image.url) return "/static/ang/assets/images/nature/1.jpg" class VideoDetailSerializer(serializers.ModelSerializer): image = serializers.SerializerMethodField() is_promo = serializers.SerializerMethodField() class Meta: model = Video fields = [ 'name', 'slug', 'embed', 'featured', 'image', 'is_promo' ] # obj.imageがある場合は、image.urlの文字列を返す(無い場合は、デフォルトの値を返す) def get_image(self,obj): if obj.image: return str(obj.image.url) return "/static/ang/assets/images/nature/1.jpg" def get_is_promo(self,obj): return False
- さらに、デフォルトのreturnを、相対パスにする。
from django.contrib.staticfiles.templatetags.staticfiles import static ... class VideoSerializer(serializers.ModelSerializer): ... def get_image(self,obj): if obj.image2: return str(obj.image2.url) return static("ang/assets/images/nature/1.jpg") class VideoDetailSerializer(serializers.ModelSerializer): ... def get_image(self,obj): if obj.image2: return str(obj.image2.url) return static("ang/assets/images/nature/1.jpg") def get_is_promo(self,obj): return False
Env Config Variables
- 環境変数を設定する。
import datetime import os AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID') AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY') if AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY: AWS_FILE_EXPIRE = 200 AWS_PRELOAD_METADATA = True AWS_QUERYSTRING_AUTH = True DEFAULT_FILE_STORAGE = 'tryangular.aws.utils.MediaRootS3BotoStorage' STATICFILES_STORAGE = 'tryangular.aws.utils.StaticRootS3BotoStorage' AWS_STORAGE_BUCKET_NAME = 'tryangular' S3DIRECT_REGION = 'us-west-2' S3_URL = '//%s.s3.amazonaws.com/' % AWS_STORAGE_BUCKET_NAME MEDIA_URL = '//%s.s3.amazonaws.com/media/' % AWS_STORAGE_BUCKET_NAME MEDIA_ROOT = MEDIA_URL STATIC_URL = S3_URL + 'static/' ADMIN_MEDIA_PREFIX = STATIC_URL + 'admin/' two_months = datetime.timedelta(days=61) date_two_months_later = datetime.date.today() + two_months expires = date_two_months_later.strftime("%A, %d %B %Y 20:00:00 GMT") AWS_HEADERS = { 'Expires': expires, 'Cache-Control': 'max-age=%d' % (int(two_months.total_seconds()), ), }
- localで、 .envファイルを作りたい場合は、以下のようにコマンドをうつと.envファイルを作成できる。
$ heroku config:get CONFIG-VAR-NAME -s >> .env $ heroku config:get AWS_ACCESS_KEY_ID -s >> .env $ heroku config:get AWS_SECRET_ACCESS_KEY -s >> .env
Secure your Site with Free SSL / TLS
- 以下のサイトを参考にセッティングする。 https://www.codingforentrepreneurs.com/blog/ssltls-settings-for-django/
Angular4 + Django1.11 vol.11
参考サイト
https://www.codingforentrepreneurs.com/projects/angular-django/
Backend Image Path
heroku上のbackendのimageのpathを設定する。
Video modelにimage_pathを追記する。
class Video(models.Model): name = models.CharField(max_length=220) slug = models.SlugField(unique=True, blank=True) embed = models.CharField(max_length=120, help_text='Youtube Embed Code', null=True, blank=True) image_path = models.CharField(max_length=120,blank=True, default='') active = models.BooleanField(default=True) featured = models.BooleanField(default=False) timestamp = models.DateTimeField(auto_now_add=True)
- herokuにdeployする。
$ git add . $ git commit -m "comment" $ git push heroku master && heroku run python manage.py migrate
Custom Domain Name
- herokuでcustom domainを使う。以下の動画を参照して設定。
https://www.codingforentrepreneurs.com/projects/angular-django/custom-domain-name/?play=true#lecture
AWS S3 For Static Files in Django
以下の記事を参照に、django + S3のセッティングをする。 https://www.codingforentrepreneurs.com/blog/s3-static-media-files-for-django/
user作成(コンソールアクセスは無しのuserでOK)
userのaccess_keyと、secret_keyをメモしておく。
S3のbucketを作成する。
CORS設定→デフォルトのままとりあえずセット(先頭のsample corsという文字だけ削除しておく)
今回のプロジェクト用のPOLYCYを作成する。<your_bucket_name> の箇所を、bucket名に変更する
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListAllMyBuckets" ], "Resource": "arn:aws:s3:::*" }, { "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation", "s3:ListBucketMultipartUploads", "s3:ListBucketVersions" ], "Resource": "arn:aws:s3:::<your_bucket_name>" }, { "Effect": "Allow", "Action": [ "s3:*Object*", "s3:ListMultipartUploadParts", "s3:AbortMultipartUpload" ], "Resource": "arn:aws:s3:::<your_bucket_name>/*" } ] }
IAM > User > 権限を与えたいuserを選択 > アクセス権限の付与 > 作成したポリシーを選択
これで一旦、AWS側の設定は終了。
S3に必要なpython moduleをinstallする。
$ pip install boto3 django-storages $ pip freeze > requirements.txt
'storages'
を, settings.pyに追記する。
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'storages', 'videos', ]
src/tryangular/aws フォルダを作成する。また、
__init__.py
を作成するのも忘れない。src/tryangular/aws に、conf.py, utils.pyを作成する。
utils.pyには、static, mediaのファイルの保存設定を書く。
from storages.backends.s3boto3 import S3Boto3Storage' StaticRootS3BotoStorage = lambda: S3Boto3Storage(location='static') MediaRootS3BotoStorage = lambda: S3Boto3Storage(location='media')
- conf.pyには、以下のテンプレでコードを書く。
import datetime AWS_ACCESS_KEY_ID = "<your_access_key_id>" AWS_SECRET_ACCESS_KEY = "<your_secret_access_key>" AWS_FILE_EXPIRE = 200 AWS_PRELOAD_METADATA = True AWS_QUERYSTRING_AUTH = True DEFAULT_FILE_STORAGE = '<your-project>.aws.utils.MediaRootS3BotoStorage' STATICFILES_STORAGE = '<your-project>.aws.utils.StaticRootS3BotoStorage' AWS_STORAGE_BUCKET_NAME = '<your_bucket_name>' S3DIRECT_REGION = 'us-west-2' S3_URL = '//%s.s3.amazonaws.com/' % AWS_STORAGE_BUCKET_NAME MEDIA_URL = '//%s.s3.amazonaws.com/media/' % AWS_STORAGE_BUCKET_NAME MEDIA_ROOT = MEDIA_URL STATIC_URL = S3_URL + 'static/' ADMIN_MEDIA_PREFIX = STATIC_URL + 'admin/' two_months = datetime.timedelta(days=61) date_two_months_later = datetime.date.today() + two_months expires = date_two_months_later.strftime("%A, %d %B %Y 20:00:00 GMT") AWS_HEADERS = { 'Expires': expires, 'Cache-Control': 'max-age=%d' % (int(two_months.total_seconds()), ), }
- local.pyに、conf.pyを読み込むように設定する。
... from tryangular.aws.conf import *
まずlocalで、
python manage.py collectstatic
を試してみる、、、成功!次に、production環境でも,S3を使うように設定していく。
production.pyの、STATICFILES_STORAGEの設定をコメントアウトする。
# STATICFILES_STORAGE = 'whitenoise.django.GzipManifestStaticFilesStorage'
- wsgi.py のwhitenoiseの設定の箇所を削除する。
import os from django.core.wsgi import get_wsgi_application os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tryangular.settings") application = get_wsgi_application()
$pip uninstall whitenoise
で、whitenoiseをuninstallする。herokuは、自動でcollectstaticしないように、collectstaticを禁止するコマンドを入れる。
$ heroku config:set DISABLE_COLLECTSTATIC=1
- herokuにデプロイして、現時点では、imageが、うまく表示されていないと思うので、それでOK。
まとめ
- projectごとに、awsのuserは分けよう。