Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

DjangoでさくっとWeb アプリケーション開発をする話

13,098 views

Published on

DjangoでさくっとWebアプリケーション開発をする話

Published in: Technology
  • Hello! Get Your Professional Job-Winning Resume Here - Check our website! https://vk.cc/818RFv
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

DjangoでさくっとWeb アプリケーション開発をする話

  1. 1. Djangoでさくっと Webアプリケーション 開発をする話 みんなのPython勉強会 in 長野
  2. 2. Yuichi Nakazawa @y_nakazawa1220 (株)日本システム技研 所属 Python/Django案件 ディレクター兼エンジニア GEEKLAB.NAGANO エバンジェリスト 自己紹介
  3. 3. 元ネタ スライドシェア:https://goo.gl/Hx9zEp Youtube:https://goo.gl/c8y6xy
  4. 4. 元ネタ2 https://goo.gl/3FlpjP
  5. 5. Django入門の参考書
  6. 6. https://goo.gl/bMHGyi サンプルコード:github https://goo.gl/9LumRd デモURL
  7. 7. • PythonでWebアプリケーション開発 したい人 • これからDjangoで開発したい人、興味 がある人 • Djangoの初級者向け このセッションの対象
  8. 8. • ベストプラクティス的なこと • Tips的なこと • その他、細かいDjangoの機能 (テストとか、多言語対応とか・・) 話さないこと
  9. 9. #stapy お願い • こんな書き方あるよ • こうした方が分かりやすいよ • こんなこと知りたいよ
  10. 10. Django
  11. 11. • フルスタックなWebアプリケーション フレームワーク • MVCでなく、MVT • 学習コストが低い • adminサイトが秀逸 • migrationが便利 Django特徴
  12. 12. チュートリアルが良い https://docs.djangoproject.com/ja/1.10/intro/
  13. 13. • Djangoアプリ作成 • Model作成とmigration • adminサイト • 簡単なCRUD • Herokuへのデプロイ レジュメ (書籍管理アプリの作り方)
  14. 14. Djanogoアプリ作成
  15. 15. Djangoインストール $ pip install django 現状だと1.10.6あたりが入るはず $ pip freeze django Django==1.10.6
  16. 16. Djangoプロジェクト&アプリの生成 $ django-admin.py startproject [myproject] プロジェクトを作る $ python manage.py startapp [myapp] アプリ作る settings.pyにアプリ追加 INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'myapp', ]
  17. 17. ディレクトリ構成 . ├── cms │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── urls.py │ ├── forms.py │ └── views.py │ └── templates ├── db.sqlite3 ├── manage.py └── stapydemo ├── __init__.py ├── __pycache__ ├── settings.py ├── urls.py └── wsgi.py ・色付きファイルを中心に作業します 。 ・青字は自動生成されます。 ・赤字は自分で適宜作成します。
  18. 18. 開発用サーバー起動 $ cd myproject $ python manage.py runserver System check identified no issues (0 silenced). March 03, 2017 - 15:23:22 Django version 1.10.6, using settings 'stapydemo.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
  19. 19. 開発用サーバーのTips $ python manage.py runserver IP:ポート番号 $ python manage.py rumserver --settings= myapp.settings IPとポート指定したい場合 settings.py(環境設定ファイル)を切り替えたい場合
  20. 20. Webアプリケーション開発フロー 1. Model(データ構造)を考える ⇒ Models.pyの実装(ビジネスロジックの実装) 2. Views.py ⇒ templateへ渡すためのデータ加工 3. templateを作る(HTML/CSS/Javascript) 4. URL設計 ⇒ Urls.pyの実装
  21. 21. Model作成とmigration
  22. 22. Webアプリケーション開発フロー 1. Model(データ構造)を考える ⇒ Models.pyの実装(ビジネスロジックの実装) 2. Views.py ⇒ templateへ渡すためのデータ加工 3. templateを作る(HTML/CSS/Javascript) 4. URL設計 ⇒ Urls.pyの実装
  23. 23. Modelとは? そのアプリケーションが扱う領域のデータと手続き (ビジネスロジック - ショッピングの合計額や送料を計算するなど) を表現する要素である - wikipediaより DBに定義したいデータ構造を定義する
  24. 24. • 基本的にDjangoに用意されているORMのみで DBにアクセスできる。 • SQLは書かなくて済む(個人的に経験なし・・) def book_list(request): '''書籍の一覧''' books = Book.objects.all().order_by('id') # 親の書籍を全件読む return render_to_response('cms/book_list.html', # 使用するテンプレート {'books': books}, # テンプレートに渡すデータ context_instance=RequestContext(request)) def impression_list(request, book_id): '''感想の一覧''' book = get_object_or_404(Book, pk=book_id) # 親の書籍を1件読む impressions = book.impressions.all().order_by('id') # 書籍の子供の、感想を読む : : 親の読み方、子の読み方 ORM (Object Relation Mapping)
  25. 25. モデルの追加や項目の更新・削除を 手軽にDBと同期が取れる仕組み migrationとは
  26. 26. • model.py の定義変更をDBに反映させることができ る • modelを直した場合に、所定のコマンドを叩くと migrateファイルを作ってくれる • Django 1.6まではSouth - http://south.aeracode.org • Django 1.7からは標準として取り込まれた migration
  27. 27. 認証とセッション関連の テーブルを作成 $ python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying sessions.0001_initial... OK まずmigrateして、認証とセッション関連をDB上にテーブルを作成する
  28. 28. 管理者ユーザ作成 $ python manage.py createsuperuser Username (leave blank to use 'nakazawa'): admin Email address: admin@any.com Password: Password (again): Superuser created successfully. createsuperuser で管理者ユーザを作成する
  29. 29. テーブルの中身をのぞいてみる $ python manage.py dbshell SQLite version 3.8.10.2 2015-05-20 18:17:19 Enter ".help" for usage hints. sqlite> settings.pyに設定してあるDBエンジンに接続(デフォは、sqlite) sqlite> .tables auth_group auth_user_user_permissions auth_group_permissions django_admin_log auth_permission django_content_type auth_user django_migrations auth_user_groups django_session
  30. 30. SQLを叩いてみる sqlite> .header on sqlite> .mode column sqlite> select id, username, email from auth_user; id username email ---------- ---------- ------------- 1 admin admin@any.com
  31. 31. from django.db import models class Book(models.Model): """書籍""" name = models.CharField('書籍名', max_length=255) publisher = models.CharField('出版社', max_length=255, blank=True) page = models.IntegerField('ページ数', blank=True, default=0) def __str__(self): return self.name class Impression(models.Model): """感想""" book = models.ForeignKey(Book, verbose_name='書籍', related_name='impressions') comment = models.TextField('コメント', blank=True) def __str__(self): return self.comment Modelの実装 「書籍」と各書籍の「感想」をモデル化(関連が1:nのモデル)
  32. 32. modelを更新した場合 (migrationsディレクトリ配下に000x_xxxx.pyが作成される) $ python manage.py makemigrations Migrations for 'cms': cms/migrations/0001_initial.py: - Create model Book - Create model Impression DB migration(変更の抽出)
  33. 33. class Migration(migrations.Migration): initial = True dependencies = [ ] operations = [ migrations.CreateModel( name='Book', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=255, verbose_name='書籍名')), ('publisher', models.CharField(blank=True, max_length=255, verbose_name='出版社')), ('page', models.IntegerField(blank=True, default=0, verbose_name='ページ数')), ], ), migrations.CreateModel( name='Impression', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('comment', models.TextField(blank=True, verbose_name='コメント')), ('book', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='impressions', to='cms.Book', verbose_name='書籍')), ], ), ] 000x_xxxx.pyのサンプル
  34. 34. $ python manage.py migrate Operations to perform: Apply all migrations: admin, auth, cms, contenttypes, sessions Running migrations: Applying cms.0001_initial... OK DB migration(DBへ反映) sqlite> .tables auth_group cms_book auth_group_permissions cms_impression auth_permission django_admin_log auth_user django_content_type auth_user_groups django_migrations auth_user_user_permissions django_session
  35. 35. DB migration(カラム追加) models.py に isbn という項目を追加します。 class Book(models.Model): : page = models.IntegerField(u'ページ数', blank=True, default=0) isbn = models.CharField(u'ISBN', max_length=255, blank=True, null=True) # 追加
  36. 36. DB migration $ python manage.py makemigrations myapp makemigrationsコマンド(models.pyの変更を拾う) makemigrationsが作成したマイグレーション ファイルを確認 myproj/myapp/migrations/0002_book_isbn.py migrateコマンドで、変更をDBに反映する $ python manage.py migrate myapp などといったファイルができているので、エディタで確認する モデルの項目追加/変更がDBのテーブルに反映される
  37. 37. adminサイト
  38. 38. adminサイトに ログインしてみる http://127.0.0.1:8000/admin ※adminサイト: DBのメンテナンスに使用するサイト
  39. 39. adminサイト ログイン画面
  40. 40. adminサイト
  41. 41. adminサイトへ追加する admin.site.register(Book) admin.site.register(Impression) Listにカラム表示するのであれば、こんな感じ カスタマイズするとこんな感じ class BookAdmin(admin.ModelAdmin): list_display = ('id', 'name', 'publisher', 'page',) # 一覧に出したい項目 list_display_links = ('id', 'name',) # 修正リンクでクリックできる項目 search_fields = ['name'] # 検索ボックス を出す admin.site.register(Book, BookAdmin) class ImpressionAdmin(admin.ModelAdmin): list_display = ('id', 'comment',) list_display_links = ('id', 'comment',) admin.site.register(Impression, ImpressionAdmin)
  42. 42. adminサイトへ追加する
  43. 43. CRUD
  44. 44. CRUD(クラッド)とは、ほとんど全てのコンピュータソフ トウェアが持つ永続性[1]の4つの基本機能のイニシャルを並 べた用語。その4つとは、Create(生成)、Read(読み取り )、Update(更新)、Delete(削除)である。ユーザイン タフェースが備えるべき機能(情報の参照/検索/更新)を指 す用語としても使われる。 - wikipediaより CRUDとは
  45. 45. Webアプリケーション開発フロー 1. Model(データ構造)を考える ⇒ Models.pyの実装(ビジネスロジックの実装) 2. Views.py ⇒ templateへ渡すためのデータ加工 3. URL設計 ⇒ Urls.pyの実装 4. templateを作る(HTML/CSS/Javascript)
  46. 46. CRUDの書き方 書籍の一覧を表示したい場合は、以下のような感じ def book_list(request): '''書籍の一覧''' books = Book.objects.all().order_by('id') return render_to_response('cms/book_list.html', # 使用するテンプレート dict(books=books), # テンプレートに渡すデータ context_instance=RequestContext(request))
  47. 47. • django.views.generic.list.ListView を使っておくと、ページネートが簡単 Listページの書き方 class BookList(ListView): """書籍の一覧""" context_object_name = 'books' template_name = 'cms/book_list.html' paginate_by = 2 # 1ページは最大2件ずつでページングする def get(self, request, *args, **kw): self.object_list = Book.objects.all().order_by('id') #書籍を全件取得 context = self.get_context_data(object_list=self.object_list) return self.render_to_response(context) views.py 一覧
  48. 48. Listページの書き方 {% if is_paginated %} <ul class="pagination"> {% if page_obj.has_previous %} <li><a href="?page={{ page_obj.previous_page_number }}">&laquo;</a></li> {% else %} <li class="disabled"><a href="#">&laquo;</a></li> {% endif %} {% for linkpage in page_obj.paginator.page_range %} {% ifequal linkpage page_obj.number %} <li class="active"><a href="#">{{ linkpage }}</a></li> {% else %} <li><a href="?page={{ linkpage }}">{{ linkpage }}</a></li> {% endifequal %} {% endfor %} {% if page_obj.has_next %} <li><a href="?page={{ page_obj.next_page_number }}">&raquo;</a></li> {% else %} <li class="disabled"><a href="#">&raquo;</a></li> {% endif %} </ul> {% endif %} book_list.html のページング部分
  49. 49. Listページの書き方 ページングの表示例 この部分
  50. 50. CRUDの書き方 def book_edit(request, book_id=None): """書籍の編集""" if book_id: # book_id が指定されている (修正時) book = get_object_or_404(Book, pk=book_id) else: # book_id が指定されていない (追加時) book = Book() if request.method == ‘POST': # POST された request データからフォームを作成 form = BookForm(request.POST, instance=book) if form.is_valid(): # フォームのバリデーション form.save() return redirect('cms:book_list') else: # GET の時 # book インスタンスからフォームを作成 form = BookForm(instance=book) return render(request, 'cms/book_edit.html', dict(form=form, book_id=book_id)) views.py 登録/修正
  51. 51. CRUDの書き方 forms.py class BookForm(ModelForm): '''書籍のフォーム''' class Meta: model = Book fields = ('name', 'publisher', 'page', )
  52. 52. CRUDの書き方 def book_del(request, book_id): '''書籍の削除''' book = get_object_or_404(Book, pk=book_id) book.delete() return redirect('cms:book_list') views.py 削除 urls.py urlpatterns = patterns('', # 書籍 url(r'^book/$', views.book_list, name='book_list'), # 一覧 url(r'^book/add/$', views.book_edit, name='book_add'), # 登録 url(r'^book/mod/(?P<book_id>d+)/$', views.book_edit, name='book_mod'), # 修正 url(r'^book/del/(?P<book_id>d+)/$', views.book_del, name='book_del'), # 削除 )
  53. 53. Webアプリケーション開発フロー 1. Model(データ構造)を考える ⇒ Models.pyの実装(ビジネスロジックの実装) 2. Views.py ⇒ templateへ渡すためのデータ加工 3. URL設計 ⇒ Urls.pyの実装 4. templateを作る(HTML/CSS/Javascript)
  54. 54. • CSSフレームワーク http://getbootstrap.com/ • とりあえずお手軽に見栄えするサイトを作りたい場合に便 利 Bootstrapを使う
  55. 55. STATICFILES_DIRS = ( os.path.join(BASE_DIR, "static"), ) 静的ファイルの配置場所を設定 myproject ディレクトリの下に static ディレクトリを作って 配置する場合は、settings.pyに以下のように定義します • Bootstrap - http://getbootstrap.com/ • jQuery - http://jquery.com/ 今回は、以下のサイトからベタにDLして配置します
  56. 56. myproject/ static/ css/ bootstrap-theme.css bootstrap-theme.css.map bootstrap-theme.min.css bootstrap-theme.min.css.map bootstrap.css bootstrap.css.map bootstrap.min.css bootstrap.min.css.map fonts/ glyphicons-halflings-regular.eot glyphicons-halflings-regular.svg glyphicons-halflings-regular.ttf glyphicons-halflings-regular.woff glyphicons-halflings-regular.woff2 js/ bootstrap.js bootstrap.min.js npm.js jquery-3.1.1.min.js 静的ファイルの配置イメージ
  57. 57. • 一覧系のページは、Bootstrap提供のclassを使って書きます • Form系のページは、django-bootstrap-form https://github.com/tzangms/django-bootstrap-form を使うと便利です。 Bootstrap $ pip install django-bootstrap-form INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'bootstrapform', # django-bootstrap-form 'cms', )
  58. 58. • Djangoのテンプレートは継承できるので、以下のような構造に すると流用性が高いです。 Bootstrap BootstrapのJS、CSS を定義したベース CMSの各種ページ base.html index.html など
  59. 59. Bootstrapの例(一覧)
  60. 60. {% load staticfiles %} <!DOCTYPE html> <html lang="{{ LANGUAGE_CODE|default:"en-us" }}"> <head> <meta charset="UTF-8"> <title>{% block title %}My books{% endblock %}</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet"> <link href="{% static 'css/bootstrap-theme.min.css' %}" rel="stylesheet"> <script src="{% static 'js/jquery-3.1.1.min.js' %}"></script> <script src="{% static 'js/bootstrap.min.js' %}"></script> {% block extrahead %}{% endblock %} </head> <body> <div class="container"> {% block content %} {{ content }} {% endblock %} </div> </body> </html> base.html Bootstrapの例(一覧) Bootstrap の JS、CSSを記述する ベースとなるテンプレート
  61. 61. {% extends "base.html" %} {% block title %}書籍の一覧{% endblock title %} {% block extrahead %} <style> table { margin-top: 8px; } </style> {% endblock %} {% block content %} <h3 class="page-header">書籍の一覧</h3> <a href="{% url 'cms:book_add' %}" class="btn btn-default btn-sm">追加</a> <table class="table table-striped table-bordered"> <thead> <tr> <th>ID</th> <th>書籍名</th> <th>出版社</th> <th>ページ数</th> <th>操作</th> </tr> </thead> <tbody> {% for book in books %} <tr> <td>{{ book.id }}</td> <td>{{ book.name }}</td> <td>{{ book.publisher }}</td> <td>{{ book.page }}</td> <td> <a href="{% url 'cms:book_mod' book_id=book.id %}" class="btn btn-default btn-sm">修正</a> <a href="{% url 'cms:book_del' book_id=book.id %}" class="btn btn-default btn-sm">削除</a> </td> </tr> book_list.html Bootstrapの例 ↑ 一覧系は Bootstrap の class を使って普通に書く ← base.html を継承 ← base.html の title ブロックを置き換え ← base.html の content ブロックを置き換え
  62. 62. HTML出力例 <!DOCTYPE html> <html lang="en-us"> <head> <meta charset="UTF-8"> <title>書籍の一覧</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="/static/js/jquery-3.1.1.min.js"></script> </head> <body> <div class="container"> <h3 class="page-header">書籍の一覧</h3> <a href="" class="btn btn-default btn-sm">追加</a> <table class="table table-striped table-bordered"> <thead> </thead> <tbody> </tbody> </table> <ul class="pagination"> <li class="disabled"><a href="#">&laquo;</a></li> <li class="active"><a href="#">1</a></li> <li><a href="?page=2">2</a></li> <li><a href="?page=2">&raquo;</a></li> </ul> </div> </body> </html> 青字:base.html 赤字:book_list.html
  63. 63. Bootstrapの例(Form)
  64. 64. {% extends “base_navi.html" %} {% load bootstrap %} {% block title %}書籍の編集{% endblock title %} {% block content %} <h3 class="page-header">書籍の編集</h3> {% if book_id %} <form action="{% url 'cms:book_mod' book_id=book_id %}" method="post" class="form-horizontal" role="form"> {% else %} <form action="{% url 'cms:book_add' %}" method="post" class="form-horizontal" role="form"> {% endif %} {% csrf_token %} {{ form|bootstrap_horizontal }} <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="submit" class="btn btn-primary">送信</button> </div> </div> </form> <a href="{% url 'cms:book_list' %}" class="btn btn-default btn-sm">戻る</a> {% endblock content %} book_edit.html Bootstrapの例(Form系) ← django-bootstrap-form を使っているので Form の項目を Bootstrap 形式で展開してくれる
  65. 65. {{ form|bootstrap_horizontal }} form を丸ごと出す django-bootstrap-formのTips form を項目単位にバラす(項目を出す/出さない の制御をしたい時) {{ form.id|bootstrap_horizontal }} {{ form.name|bootstrap_horizontal }} HTMLレベルにバラす(checkbox、radioは微妙に異なるので注意) <div class="form-group{% if form.name.errors %} has-error{% endif %}"> <label class="control-label" for="{{ form.name.auto_id }}">{{ form.name.label }}</label> <input type="text" class=“form-control" name="{{ form.name.html_name }}" value="{{ form.name.value }}" id="{{ form.name.auto_id }}"> {% for error in form.name.errors %} <span class=“help-block {{ form.error_css_class }}">{{ error }}</span> {% endfor %} {% if form.name.help_text %} <p class="help-block"> {{ form.name.help_text|safe }} </p> {% endif %} </div> checkbox、radioはsite- packages/bootstrapform/templates/bootstrapfrom/field.html を参考にしてみると良いです。
  66. 66. アプリケーションを公開する
  67. 67. Herokuを使う • Paas https://www.heroku.com • フリープランもあるので、今回はそちらを利用します
  68. 68. runtimeとgunicornの設定 $ pip install gunicorn アプリケーション・サーバーとして「gunicorn」を入れる python-3.4.1 PROJECR_ROOT/runtime.txt に使用するPythonバージョン を書く web: gunicorn --env DJANGO_SETTINGS_MODULE=stapydemo.settings stapydemo.wsgi --log-file - PROJECR_ROOT/Procfileに設定を書く
  69. 69. DBの切り替え $ pip install dj_database_url DBをPostgreSQLに切り替える。sqliteが使用できないのと、MySQLは クレカ登録が必要なため HerokuのDB設定を使用するため $ pip install psycopg2 PostgreSQLのアダプタ
  70. 70. Settings.pyを両対応させる from socket import gethostname if ‘my_hostname' in gethostname(): DEBUG = True TEMPLATE_DEBUG = True else: DEBUG = False TEMPLATE_DEBUG = False ALLOWED_HOSTS = ['*'] 開発環境とHerokuの環境で設定を切り替える。 if 'my_hostname' in gethostname(): DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } else: import dj_database_url DATABASES = { 'default': dj_database_url.config() }
  71. 71. 静的ファイルの扱い 静的ファイルをHerokuで扱うため、whitenoise を入れる。 http://whitenoise.evans.io/en/stable/django.html $ pip install whitenoise STATICFILES_DIRS = ( os.path.join(BASE_DIR, "static"), ) STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') STATICFILES_STORAGE = 'whitenoise.django.GzipManifestStaticFilesStorage' settings.pyにパスを追加する
  72. 72. 環境をまとめる $ pip freeze > requirements.txt
  73. 73. デプロイ
  74. 74. まとめ • Djangoは、チュートリアルが充実しているので 学習コストが比較的低いです。 • フルスタックなので、色々やりたいことがあるので あればDjangoを選ぶと良いと思います。 • Herokuは、お手軽にデプロイ経験できるので是非 使ってみてください。
  75. 75. Pythonエンジニア募集中! https://jsl.co.jp • 長野でPython/Djangoをやりたい方 • MBP支給します • リモートワーク有り/Slack有り • 勉強会/セミナー参加支援有り
  76. 76. Q&A

×