├── .gitignore ├── cover.jpg ├── cover_small.jpg ├── LANGS.md ├── en ├── domain │ ├── images │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ └── 6.png │ └── README.md ├── homework │ ├── images │ │ ├── delete3.png │ │ └── publish2.png │ └── README.md ├── homework_create_more_models │ ├── images │ │ ├── url_error.png │ │ ├── views_error.png │ │ ├── template_error.png │ │ └── add_comment_button.png │ └── README.md ├── manual_pythonanywhere_deploy │ ├── images │ │ ├── static_files_screenshot.png │ │ ├── pythonanywhere_bash_console.png │ │ └── pythonanywhere_web_tab_virtualenv.png │ └── README.md ├── SUMMARY.md ├── README.md ├── optional_postgresql_installation │ └── README.md ├── authentication_authorization │ └── README.md └── heroku │ └── README.md ├── es ├── domain │ ├── images │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ └── 6.png │ └── README.md ├── homework │ ├── images │ │ ├── delete3.png │ │ └── publish2.png │ └── README.md ├── homework_create_more_models │ ├── images │ │ ├── url_error.png │ │ ├── views_error.png │ │ ├── template_error.png │ │ └── add_comment_button.png │ └── README.md ├── SUMMARY.md ├── README.md ├── optional_postgresql_installation │ └── README.md ├── authentication_authorization │ └── README.md └── heroku │ └── README.md ├── ja ├── domain │ ├── images │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ └── 6.png │ └── README.md ├── homework │ ├── images │ │ ├── delete3.png │ │ └── publish2.png │ └── README.md ├── homework_create_more_models │ ├── images │ │ ├── url_error.png │ │ ├── views_error.png │ │ ├── template_error.png │ │ └── add_comment_button.png │ └── README.md ├── manual_pythonanywhere_deploy │ ├── images │ │ ├── static_files_screenshot.png │ │ ├── pythonanywhere_bash_console.png │ │ └── pythonanywhere_web_tab_virtualenv.png │ └── README.md ├── SUMMARY.md ├── README.md ├── optional_postgresql_installation │ └── README.md ├── authentication_authorization │ └── README.md └── heroku │ └── README.md ├── ko ├── domain │ ├── images │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ └── 6.png │ └── README.md ├── homework │ ├── images │ │ ├── delete3.png │ │ └── publish2.png │ └── README.md ├── homework_create_more_models │ ├── images │ │ ├── url_error.png │ │ ├── views_error.png │ │ ├── template_error.png │ │ └── add_comment_button.png │ └── README.md ├── manual_pythonanywhere_deploy │ ├── images │ │ ├── static_files_screenshot.png │ │ ├── pythonanywhere_bash_console.png │ │ └── pythonanywhere_web_tab_virtualenv.png │ └── README.md ├── SUMMARY.md ├── README.md ├── optional_postgresql_installation │ └── README.md ├── authentication_authorization │ └── README.md └── heroku │ └── README.md ├── LICENSE ├── .travis.yml ├── package.json ├── book.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | MANIFEST 2 | .DS_Store 3 | _book 4 | node_modules 5 | .idea/ -------------------------------------------------------------------------------- /cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/cover.jpg -------------------------------------------------------------------------------- /cover_small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/cover_small.jpg -------------------------------------------------------------------------------- /LANGS.md: -------------------------------------------------------------------------------- 1 | * [🇺🇸 English](en/) 2 | * [🇰🇷 Korean](ko/) 3 | * [🇪🇸 Spanish](es/) 4 | * [🇯🇵 日本語](ja/) 5 | -------------------------------------------------------------------------------- /en/domain/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/en/domain/images/1.png -------------------------------------------------------------------------------- /en/domain/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/en/domain/images/2.png -------------------------------------------------------------------------------- /en/domain/images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/en/domain/images/3.png -------------------------------------------------------------------------------- /en/domain/images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/en/domain/images/4.png -------------------------------------------------------------------------------- /en/domain/images/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/en/domain/images/5.png -------------------------------------------------------------------------------- /en/domain/images/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/en/domain/images/6.png -------------------------------------------------------------------------------- /es/domain/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/es/domain/images/1.png -------------------------------------------------------------------------------- /es/domain/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/es/domain/images/2.png -------------------------------------------------------------------------------- /es/domain/images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/es/domain/images/3.png -------------------------------------------------------------------------------- /es/domain/images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/es/domain/images/4.png -------------------------------------------------------------------------------- /es/domain/images/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/es/domain/images/5.png -------------------------------------------------------------------------------- /es/domain/images/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/es/domain/images/6.png -------------------------------------------------------------------------------- /ja/domain/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ja/domain/images/1.png -------------------------------------------------------------------------------- /ja/domain/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ja/domain/images/2.png -------------------------------------------------------------------------------- /ja/domain/images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ja/domain/images/3.png -------------------------------------------------------------------------------- /ja/domain/images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ja/domain/images/4.png -------------------------------------------------------------------------------- /ja/domain/images/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ja/domain/images/5.png -------------------------------------------------------------------------------- /ja/domain/images/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ja/domain/images/6.png -------------------------------------------------------------------------------- /ko/domain/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ko/domain/images/1.png -------------------------------------------------------------------------------- /ko/domain/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ko/domain/images/2.png -------------------------------------------------------------------------------- /ko/domain/images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ko/domain/images/3.png -------------------------------------------------------------------------------- /ko/domain/images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ko/domain/images/4.png -------------------------------------------------------------------------------- /ko/domain/images/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ko/domain/images/5.png -------------------------------------------------------------------------------- /ko/domain/images/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ko/domain/images/6.png -------------------------------------------------------------------------------- /en/homework/images/delete3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/en/homework/images/delete3.png -------------------------------------------------------------------------------- /en/homework/images/publish2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/en/homework/images/publish2.png -------------------------------------------------------------------------------- /es/homework/images/delete3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/es/homework/images/delete3.png -------------------------------------------------------------------------------- /es/homework/images/publish2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/es/homework/images/publish2.png -------------------------------------------------------------------------------- /ja/homework/images/delete3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ja/homework/images/delete3.png -------------------------------------------------------------------------------- /ja/homework/images/publish2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ja/homework/images/publish2.png -------------------------------------------------------------------------------- /ko/homework/images/delete3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ko/homework/images/delete3.png -------------------------------------------------------------------------------- /ko/homework/images/publish2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ko/homework/images/publish2.png -------------------------------------------------------------------------------- /en/homework_create_more_models/images/url_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/en/homework_create_more_models/images/url_error.png -------------------------------------------------------------------------------- /es/homework_create_more_models/images/url_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/es/homework_create_more_models/images/url_error.png -------------------------------------------------------------------------------- /ja/homework_create_more_models/images/url_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ja/homework_create_more_models/images/url_error.png -------------------------------------------------------------------------------- /ko/homework_create_more_models/images/url_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ko/homework_create_more_models/images/url_error.png -------------------------------------------------------------------------------- /en/homework_create_more_models/images/views_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/en/homework_create_more_models/images/views_error.png -------------------------------------------------------------------------------- /es/homework_create_more_models/images/views_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/es/homework_create_more_models/images/views_error.png -------------------------------------------------------------------------------- /ja/homework_create_more_models/images/views_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ja/homework_create_more_models/images/views_error.png -------------------------------------------------------------------------------- /ko/homework_create_more_models/images/views_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ko/homework_create_more_models/images/views_error.png -------------------------------------------------------------------------------- /en/homework_create_more_models/images/template_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/en/homework_create_more_models/images/template_error.png -------------------------------------------------------------------------------- /es/homework_create_more_models/images/template_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/es/homework_create_more_models/images/template_error.png -------------------------------------------------------------------------------- /ja/homework_create_more_models/images/template_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ja/homework_create_more_models/images/template_error.png -------------------------------------------------------------------------------- /ko/homework_create_more_models/images/template_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ko/homework_create_more_models/images/template_error.png -------------------------------------------------------------------------------- /en/homework_create_more_models/images/add_comment_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/en/homework_create_more_models/images/add_comment_button.png -------------------------------------------------------------------------------- /es/homework_create_more_models/images/add_comment_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/es/homework_create_more_models/images/add_comment_button.png -------------------------------------------------------------------------------- /ja/homework_create_more_models/images/add_comment_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ja/homework_create_more_models/images/add_comment_button.png -------------------------------------------------------------------------------- /ko/homework_create_more_models/images/add_comment_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ko/homework_create_more_models/images/add_comment_button.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. 2 | To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/ 3 | -------------------------------------------------------------------------------- /en/manual_pythonanywhere_deploy/images/static_files_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/en/manual_pythonanywhere_deploy/images/static_files_screenshot.png -------------------------------------------------------------------------------- /ja/manual_pythonanywhere_deploy/images/static_files_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ja/manual_pythonanywhere_deploy/images/static_files_screenshot.png -------------------------------------------------------------------------------- /ko/manual_pythonanywhere_deploy/images/static_files_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ko/manual_pythonanywhere_deploy/images/static_files_screenshot.png -------------------------------------------------------------------------------- /en/manual_pythonanywhere_deploy/images/pythonanywhere_bash_console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/en/manual_pythonanywhere_deploy/images/pythonanywhere_bash_console.png -------------------------------------------------------------------------------- /ja/manual_pythonanywhere_deploy/images/pythonanywhere_bash_console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ja/manual_pythonanywhere_deploy/images/pythonanywhere_bash_console.png -------------------------------------------------------------------------------- /ko/manual_pythonanywhere_deploy/images/pythonanywhere_bash_console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ko/manual_pythonanywhere_deploy/images/pythonanywhere_bash_console.png -------------------------------------------------------------------------------- /en/manual_pythonanywhere_deploy/images/pythonanywhere_web_tab_virtualenv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/en/manual_pythonanywhere_deploy/images/pythonanywhere_web_tab_virtualenv.png -------------------------------------------------------------------------------- /ja/manual_pythonanywhere_deploy/images/pythonanywhere_web_tab_virtualenv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ja/manual_pythonanywhere_deploy/images/pythonanywhere_web_tab_virtualenv.png -------------------------------------------------------------------------------- /ko/manual_pythonanywhere_deploy/images/pythonanywhere_web_tab_virtualenv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/tutorial-extensions/master/ko/manual_pythonanywhere_deploy/images/pythonanywhere_web_tab_virtualenv.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "8" 5 | 6 | cache: 7 | directories: 8 | - "DjangoGirls/turorial-extensions" 9 | 10 | install: 11 | - npm install gitbook-cli -g 12 | - gitbook install 13 | 14 | script: gitbook build -------------------------------------------------------------------------------- /ja/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [イントロダクション](README.md) 4 | * [宿題: ウェブサイトにもっと機能を追加しよう!](homework/README.md) 5 | * [宿題: ウェブサイトをセキュアにする](authentication_authorization/README.md) 6 | * [宿題: コメントモデルを作ろう](homework_create_more_models/README.md) 7 | * [オプション: PostgreSQLのインストール](optional_postgresql_installation/README.md) 8 | * [オプション: ドメイン](domain/README.md) 9 | * [Herokuにデプロイしよう](heroku/README.md) 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "djangogirls-tutorial-extensions", 3 | "version": "1.0.0", 4 | "description": "", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/DjangoGirls/tutorial-extensions.git" 8 | }, 9 | "author": "Django Girls ", 10 | "dependencies": { 11 | "gitbook-plugin-anchors": "^0.3.0", 12 | "gitbook-plugin-richquotes": "0.0.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ko/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # 목차 2 | 3 | * [들어가며](README.md) 4 | * [숙제: 블로그 개선하기](homework/README.md) 5 | * [숙제: 안전한 웹사이트 만들기](authentication_authorization/README.md) 6 | * [숙제: 댓글 모델 만들기](homework_create_more_models/README.md) 7 | * [옵션: PostgreSQL 설치하기](optional_postgresql_installation/README.md) 8 | * [옵션: 도메인 만들기](domain/README.md) 9 | * [옵션: 헤로쿠(Heroku) 배포하기](heroku/README.md) 10 | * [옵션: 파이썬애니웨어(PythonAnywhere) 수동 배포하기](manual_pythonanywhere_deploy/README.md) 11 | -------------------------------------------------------------------------------- /en/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Introduction](README.md) 4 | * [Homework: add more to your website!](homework/README.md) 5 | * [Homework: secure your website](authentication_authorization/README.md) 6 | * [Homework: create comment model](homework_create_more_models/README.md) 7 | * [Optional: PostgreSQL installation](optional_postgresql_installation/README.md) 8 | * [Optional: Domain](domain/README.md) 9 | * [Deploy your website on Heroku](heroku/README.md) 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /es/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Resumen 2 | 3 | * [Introducción](README.md) 4 | * [Tarea: agregar mas a tu sitio web](homework/README.md) 5 | * [Tarea: Asegura tu sitio](authentication_authorization/README.md) 6 | * [Tarea: Crea un modelo de comentarios](homework_create_more_models/README.md) 7 | * [Opcional: Instalación de PostgreSQL](optional_postgresql_installation/README.md) 8 | * [Opcional: Dominio](domain/README.md) 9 | * [Despliega tu sitio en Heroku](heroku/README.md) 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "gitbook": ">=3.1.1", 3 | "plugins": [ 4 | "heading-anchors", 5 | "richquotes@0.0.7", 6 | "github", 7 | "codeblock-label", 8 | "sidebar-ad", 9 | "language-picker" 10 | ], 11 | "pluginsConfig": { 12 | "richquotes" : { 13 | "default" : false 14 | }, 15 | "github": { 16 | "url": "https://github.com/DjangoGirls/tutorial-extensions" 17 | }, 18 | "sidebar-ad": { 19 | "imageUrl": "https://cdn.shopify.com/s/files/1/0992/7712/products/codelikeagirl---mockup4_large.jpg", 20 | "url": "https://store.djangogirls.org/", 21 | "description": "💖 Support our work and buy a Django Girls t-shirt! ✨", 22 | "btnText": "Get a t-shirt!" 23 | }, 24 | "language-picker": { 25 | "grid-columns": 3 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ja/README.md: -------------------------------------------------------------------------------- 1 | # Django Girls Tutorial: Extensions 2 | 3 | > **Info** これは、Creative Commons Attribution-ShareAlike 4.0 International License のライセンスの下で提供しています。ライセンスについてはこちらをご確認ください。 https://creativecommons.org/licenses/by-sa/4.0/ 4 | 5 | ## イントロダクション 6 | 7 | これは [Django Girls Tutorial](https://tutorial.djangogirls.org/ja/) を終えた後にできる追加のチュートリアルです。 8 | 9 | 現在の追加チュートリアルには以下の章があります: 10 | 11 | - [宿題: ウェブサイトにもっと機能を追加しよう!](homework/README.md) 12 | - [宿題: ウェブサイトをセキュアにする](authentication_authorization/README.md) 13 | - [宿題: コメントモデルを作ろう](homework_create_more_models/README.md) 14 | - [オプション: PostgreSQLのインストール](optional_postgresql_installation/README.md) 15 | - [オプション: ドメイン](domain/README.md) 16 | - [Herokuにデプロイしよう](heroku/README.md) 17 | 18 | ## コントリビューションについて 19 | 20 | このチュートリアルは、 [DjangoGirls](http://djangogirls.org/) がメンテナンスしています。 ミスを見つけたりチュートリアルを更新したくなったりした時は、 [コントリビューションガイドライン](https://github.com/DjangoGirls/tutorial/blob/master/README.md) を読んでください。 21 | -------------------------------------------------------------------------------- /en/README.md: -------------------------------------------------------------------------------- 1 | # Django Girls Tutorial: Extensions 2 | 3 | > **Info** This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 4 | International License. To view a copy of this license, visit 5 | http://creativecommons.org/licenses/by-sa/4.0/ 6 | 7 | ## Introduction 8 | 9 | This book contains additional tutorials you can do after you're finished with [Django Girls Tutorial](http://tutorial.djangogirls.org/). 10 | 11 | Current tutorials are: 12 | * [Homework: add more to your website!](homework/README.md) 13 | * [Homework: secure your website](authentication_authorization/README.md) 14 | * [Homework: create comment model](homework_create_more_models/README.md) 15 | * [Optional: PostgreSQL installation](optional_postgresql_installation/README.md) 16 | 17 | ## Contributing 18 | 19 | This tutorial is maintained by [DjangoGirls](http://djangogirls.org/). If you find any mistakes or want to update the tutorial please [follow the contributing guidelines](https://github.com/DjangoGirls/tutorial/blob/master/README.md). 20 | -------------------------------------------------------------------------------- /es/README.md: -------------------------------------------------------------------------------- 1 | # Tutorial Django Girls : Extensiones 2 | 3 | > **Información** Este trabajo está licenciado bajo la licencia Creative Commons Share Alike 4.0. Para ver una copia de esta licencia visite: 4 | http://creativecommons.org/licenses/by-sa/4.0/ 5 | 6 | ## Introducción 7 | 8 | Este libro contiene tutoriales adicionales que puedes realizar luego de terminal el [Tutorial de Django Girls](http://tutorial.djangogirls.org/). 9 | 10 | Los tutoriales actuales son: 11 | * [Tarea: agregar mas a tu sitio web](homework/README.md) 12 | * [Tarea: Asegura tu sitio](authentication_authorization/README.md) 13 | * [Tarea: Crea un modelo de comentarios](homework_create_more_models/README.md) 14 | * [Opcional: Instalación de PostgreSQL](optional_postgresql_installation/README.md) 15 | 16 | ## Contribuir 17 | 18 | Este tutorial es mantenido por [DjangoGirls](http://djangogirls.org). Si encuentras algunos errores o quieres actualizar el tutorial, por favor [Sigue la guía de contribución](https://github.com/DjangoGirls/tutorial/blob/master/README.md). 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Django Girls Tutorial: Extensions 2 | 3 | > **Info** This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 4 | International License. To view a copy of this license, visit 5 | http://creativecommons.org/licenses/by-sa/4.0/ 6 | 7 | ## Introduction 8 | 9 | This book contains additional tutorials you can do after you're finished with [Django Girls Tutorial](http://tutorial.djangogirls.org/). 10 | 11 | Current tutorials are: 12 | 13 | ### English 14 | - [Homework: add more to your website!](/en/homework) 15 | - [Homework: secure your website](/en/authentication_authorization) 16 | - [Homework: create comment model](/en/homework_create_more_models) 17 | - [Optional: PostgreSQL installation](/en/optional_postgresql_installation) 18 | - [Optional: Domain](/en/domain) 19 | - [Deploy your website on Heroku](/en/heroku) 20 | 21 | ## Contributing 22 | 23 | These tutorials are maintained by [DjangoGirls](http://djangogirls.org/). If you find any mistakes or want to update the tutorial please [follow the contributing guidelines](https://github.com/DjangoGirls/tutorial#how-to-contribute). 24 | -------------------------------------------------------------------------------- /ko/README.md: -------------------------------------------------------------------------------- 1 | # 장고걸스 튜토리얼 : 심화 (Django Girls Tutorial: Extensions) 2 | 3 | > **Info** 이 튜토리얼은 Creative Commons Attribution-ShareAlike 4.0 International 저작권을 따르고 있습니다. 라이센스 전문은 http://creativecommons.org/licenses/by-sa/4.0/ 에서 확인하세요. 4 | 5 | ## 소개 6 | 이 튜토리얼은 [장고걸스 튜토리얼](http://tutorial.djangogirls.org/)을 마친 분들을 위한 심화 튜토리얼입니다. 7 | 8 | ## 번역 9 | 이 튜토리얼은 열정적인 장고걸스 코치들과 자원봉사자들의 수고로 번역되었습니다. 10 | > 번역 업데이트 및 번역자 11 | * 1차 : 2018. 3. 1. [이수진](https://github.com/sujinleeme), [박조은](https://github.com/corazzon) 12 | 13 | ### 목차 14 | * [들어가며](README.md) 15 | * [숙제: 블로그 개선하기](homework/README.md) 16 | * [숙제: 안전한 웹사이트 만들기](authentication_authorization/README.md) 17 | * [숙제: 댓글 모델 만들기](homework_create_more_models/README.md) 18 | * [옵션: PostgreSQL 설치하기](optional_postgresql_installation/README.md) 19 | * [옵션: 도메인 만들기](domain/README.md) 20 | * [옵션: 헤로쿠(Heroku) 배포하기](heroku/README.md) 21 | * [옵션: 파이썬애니웨어(PythonAnywhere) 수동 배포하기](manual_pythonanywhere_deploy/README.md) 22 | 23 | ## 튜토리얼 참여하기 24 | 이 튜토리얼은 [장고걸스](https://djangogirls.org/)에서 지속적으로 관리하고 있습니다. 오류를 발견하거나 튜토리얼 내용을 업데이트하고 싶다면 [참여 방법](https://github.com/DjangoGirls/tutorial/blob/master/README.md)을 확인하세요. 25 | -------------------------------------------------------------------------------- /ja/domain/README.md: -------------------------------------------------------------------------------- 1 | # ドメイン 2 | 3 | PythonAnywhereでは、無料でドメインがもらえます。でも「.pythonanywhere.com」がブログのURLのおわりについてしまうのはいやかもしれません。「www.infinite-kitten-pictures.org」とか「www.3d-printed-steam-engine-parts.com」や「www.antique-buttons.com」や「www.mutant-unicornz.net」みたいなドメインのほうがいいかもしれませんね。 4 | 5 | ここではドメインの取得先とPythonAnywhereのウェブアプリにそのドメインを割り当てる方法を取り上げます。ですが、ドメインを取るにはお金がかかり、PythonAnywhereで自分のドメイン名を使うには月額料金がかかります(そんなに高くはありません。でも本気でやりたいときにならおそらく払ってもいいと思えるでしょうね!)。 6 | 7 | 8 | ## どこでドメインを登録するか? 9 | 10 | 普通のドメインはだいたい年間15USドルぐらいかかります。もっと安いドメインもあるし、もっと高いものもあります。値段はドメイン・プロバイダー次第です。 ドメイン業者はたくさんあります。[Googleで検索](https://www.google.com/search?q=register%20domain)するとたくさん出てきます。 11 | 12 | Django Girlsのお気に入りは[I want my name](https://iwantmyname.com/)です。「苦労しないドメイン管理」をうたっていますし、ほんとに苦労しません。 13 | 14 | 無料でドメインを取ることもできます。例えば[dot.tk](http://www.dot.tk)です。でも無料のドメインだと安っぽいと感じるかもしれません。もし本格的なビジネスのためのサイトを作るつもりなら、「.com」で終わる「正式」なドメインの購入を考えたほうがいいかもしれません。 15 | 16 | ## PythonAnywhereに取得したドメインを向ける 17 | 18 | *iwantmyname.com* をひらいたら、メニューの `Domains` をクリックして、新しく買ったドメインを選んでください。それから `manage DNS records` のリンクを探してクリックします: 19 | 20 | ![](images/4.png) 21 | 22 | それから、下の画像のフォームを探してください: 23 | 24 | ![](images/5.png) 25 | 26 | 見つかったら、下のとおりに入力してください: 27 | - Hostname: www 28 | - Type: CNAME 29 | - Value: PythonAnywhereのドメイン (例えば、djangogirls.pythonanywhere.com) 30 | - TTL: 60 31 | 32 | ![](images/6.png) 33 | 34 | 「Add」ボタンを押してから、下の方にある「Save」ボタンを押して変更を保存してください 35 | 36 | > **注意** *iwantmyname.com* 以外のドメイン業者を使った場合、DNSやCNAMEの設定を検索する画面のUIが上の画像とは違います。でもやることは同じです。新しいドメインが`yourusername.pythonanywhere.com` というドメインに向くようにCNAMEを設定してください。 37 | 38 | ドメインが実際に動き始めるまで数分かかります。あせらず待ちましょう! 39 | 40 | ## PythonAnywhereのウェブアプリにドメインの設定をする 41 | 42 | PythonAnywhereにも取得したカスタムドメインを使うための設定が必要です。 43 | 44 | [PythonAnywhere Accounts page](https://www.pythonanywhere.com/account/)を開いて、アカウントをアップグレードしましょう。一番安い「Hacker」プランで大丈夫です。プランの変更はいつでもできます。変更するのはサイトがものすごく有名になってヒット数が数百万になったらでよいでしょう。 45 | 46 | さて、次に[Web tab](https://www.pythonanywhere.com/web_app_setup/)を開いて、いくつか書き留めます: 47 | 48 | * **virtualenvのパス** をコピーし、どこかにペーストして、保管しておいてください。 49 | * **wsgi config file** をクリックしてください。出てきた内容をコピーし、どこかにペーストし、保管しておいてください。 50 | 51 | 続いて、 古いウェブアプリを**Delete**を押して、削除します。削除と言っても、コードが消えることはありませんので安心してください。*yourusername.pythonanywhere.com* のドメインが無効になるだけです。消したら新しいウェブアプリを作りましょう。下のとおりにしてください: 52 | 53 | * 新しいドメイン名を入力する 54 | * 「manual configuration」を選ぶ 55 | * 「Python 3.4」を選ぶ 56 | * 完了! 57 | 58 | 「web」タブに戻ったら・・・ 59 | 60 | * さっき保存した「virtualenvのパス」を貼り付けてください 61 | * 「wsgi configuration file」の中に、さっき保存した「wsgi config file」の内容を貼り付けてください 62 | 63 | ウェブアプリの「Reload」をクリックしてください。新しいドメインであなたのウェブアプリが動いているのがわかるはずです。 64 | 65 | PythonAnywhereでは、うまく行かないことがあったら、「Send feedback」を押してください。親切な管理者の誰かがすぐに助けてくれます。 66 | -------------------------------------------------------------------------------- /ko/domain/README.md: -------------------------------------------------------------------------------- 1 | # 도메인 만들기 2 | 3 | 파이썬애니웨어(PythonAnywhere)는 무료 도메인을 제공하지만 블로그 주소 끝에 있는 ". pythonanywhere.com"를 원하지 않을 수도 있을 거예요. 아마도 이런 이름을 원할지도 몰라요. "www.infinite-kitten-pictures.org" 혹은 "www.3d-printed-steam-engine-parts.com" 혹은 "www.antique-buttons.com" 혹은 "www.mutant-unicornz.net" 이런 이름으로요. 4 | 5 | 여기에서는 파이썬애니웨어에 있는 웹 앱을 도메인에 연결하는 방법을 알려드릴 거예요. 어쨌든, 대부분 도메인은 비용이 들어가요. 파이썬애니웨어도 도메인명에 대한 비용을 부과할 거예요. 여러분이 원할 때 도메인을 설정하면 되어요. 물론 큰 비용이 들지 않는 선에요. 6 | 7 | ## 도메인은 어디에서 등록할까요? 8 | 9 | 일반적으로 도메인 비용은 연간 약 15달러 정도가 들어요. 공급자에 따라 좀 더 저렴하거나 비싼 옵션도 있어요. 도메인을 구할 수 있는 회사는 많이 있어요. 간단한 [구글 검색](https://www.google.com/search?q=register%20domain)을 통해 수백 개의 옵션을 얻을 수 있어요. 10 | 11 | 우리가 가장 선호하는 곳은 [I want my name](https://iwantmyname.com/)라는 곳이에요. 여기는 "고통 없는 도메인 관리"라는 문구로 홍보하는 데 정말 고통스럽지 않아요. 12 | 13 | [dot.tk](http://www.dot.tk)은 역시나 도메인을 무료로 얻을 수 있는 곳이에요. 비용을 내고 살 수도 있지만, 이 정도면 저렴한 편이에요. 만약 전문적인 비즈니스를 하려고 한다면 아마도 `.com`으로 끝나는 "적정한" 도메인을 구입해야 할 거예요. 14 | 15 | ## 파이썬애니웨어 도메인 설정 방법 16 | 17 | *iwantmyname.com* 사이트를 통해 도메인을 구했다면 `Domains(도메인)` 메뉴를 클릭하고 새로 구입한 도메인을 선택하세요. 그런 다음 `manage DNS records(DNS 기록 관리)` 링크를 클릭하세요. 18 | 19 | ![](images/4.png) 20 | 21 | 이제, 이 폼을 찾아야 해요. 22 | 23 | ![](images/5.png) 24 | 25 | 그리고 아래 정보를 채우세요. 26 | - Hostname: www 27 | - Type: CNAME 28 | - Value: PythonAnywhere의 도메인 (예를 들어, djangogirls.pythonanywhere.com) 29 | - TTL: 60 30 | 31 | ![](images/6.png) 32 | 33 | 'Add' 버튼을 클릭하고 변경사항을 저장해 주세요. 34 | 35 | > **Note** 다른 도메인 업체를 사용한다면 DNS / CNAME 설정을 찾는데 필요한 UI는 다르지만, 우리가 해야 할 일은 같아요. `yourusername.pythonanywhere.com`이 새로운 도메인을 가리키도록 CNAME을 설정하는 거예요. 36 | 37 | 도메인이 동작하려면 몇 분 정도 걸릴 수도 있어요. 인내심을 갖고 기다려봐요! 38 | 39 | ## 파이썬애니웨의의 웹 앱을 통해 도메인 구성을 설정 40 | 41 | 파이썬 애니웨어에도 커스텀 도메인을 사용할 거라고 알려줘야 해요. 42 | 43 | [파이썬애니웨어 계정 설정 페이지(PythonAnywhere Accounts page)](https://www.pythonanywhere.com/account/)로 가서 계정을 업그레이드하세요. 가장 저렴한 옵션("Hacker")으로 시작해도 괜찮아요. 나중에 블로그가 유명해지고 수백만 건의 조회수 있다면 언제든 업그레이드할 수 있어요. 44 | 45 | 다음 [Web tab(웹 탭)](https://www.pythonanywhere.com/web_app_setup/)으로 가서 다음 2가지를 메모해 주세요. 46 | 47 | * **가상 환경 경로**를 적어두고 안전한 곳에 보관해 주세요. 48 | * **wsgi 설정 파일**을 클릭해서 내용을 복사해서 붙여 넣기 해두고 안전한 곳에 보관해 주세요. 49 | 50 | 그런 다음, 이전 웹 앱을 **삭제**해 주세요. 걱정하지 마세요. 이 과정은 어떠한 코드도 삭제하지 않고, *yourusername.pythonanywhere.com* 도메인을 끄기만 할 거예요. 다음으로 새로운 웹 앱으로 가서 아래 단계를 진행해주세요. 51 | 52 | * 새 도메인 이름을 입력하세요. 53 | * "manual configuration"을 선택하세요. 54 | * Python 3.4를 선택하세요. 55 | * 끝났어요! 56 | 57 | 이제 웹 탭으로 다시 돌아가세요. 58 | 59 | * 앞에서 저장해 두었던 가상 환경 경로에 붙여 넣기를 하세요. 60 | * wsgi 설정 파일을 클릭하고 기존 설정 파일의 내용을 붙여 넣기를 하세요. 61 | 62 | 웹 앱을 다시 재실행(새로 고침) 해 보세요. 새로운 도메인으로 사이트를 찾아볼 수 있을 거예요! 63 | 64 | 만약 문제가 있다면 파이썬애니웨어의 "Send feedback(피드백 보내기)" 링크를 통해 문의해 보세요. 친절한 관리자가 시간을 내어 도움을 줄 거예요. 65 | 66 | -------------------------------------------------------------------------------- /en/domain/README.md: -------------------------------------------------------------------------------- 1 | # Domain 2 | 3 | PythonAnywhere gave you a free domain, but maybe you don't want to have ".pythonanywhere.com" at the end of your blog URL. Maybe you want your blog to just live at "www.infinite-kitten-pictures.org" or "www.3d-printed-steam-engine-parts.com" or "www.antique-buttons.com" or "www.mutant-unicornz.net", or whatever it'll be. 4 | 5 | Here we'll talk a bit about where to get a domain, and how to hook it up to your web app on PythonAnywhere. However, you should know that most domains cost money, and PythonAnywere also charges a monthly fee to use your own domain name -- it's not much money in total, but this is probably something you only want to do if you're really committed! 6 | 7 | 8 | ## Where to register a domain? 9 | 10 | A typical domain costs around $15 a year. There are cheaper and more expensive options, depending on the provider. There are a lot of companies that you can buy a domain from: a simple [google search](https://www.google.com/search?q=register%20domain) will give hundreds of options. 11 | 12 | Our favourite one is [I want my name](https://iwantmyname.com/). They advertise as "painless domain management" and it really is painless. 13 | 14 | You can also get domains for free. [dot.tk](http://www.dot.tk) is one place to get one, but you should be aware that free domains sometimes feel a bit cheap -- if your site is going to be for a professional business, you might want to think about paying for a "proper" domain that ends in `.com`. 15 | 16 | 17 | ## How to point your domain at PythonAnywhere 18 | 19 | If you went through *iwantmyname.com*, click `Domains` in the menu and choose your newly purchased domain. Then locate and click on the `manage DNS records` link: 20 | 21 | ![](images/4.png) 22 | 23 | Now you need to locate this form: 24 | 25 | ![](images/5.png) 26 | 27 | And fill it in with the following details: 28 | - Hostname: www 29 | - Type: CNAME 30 | - Value: your domain from PythonAnywhere (for example djangogirls.pythonanywhere.com) 31 | - TTL: 60 32 | 33 | ![](images/6.png) 34 | 35 | Click the Add button and Save changes at the bottom. 36 | 37 | 38 | > **Note** If you used a different domain provider, the exact UI for finding your DNS / CNAME settings will be different, but your objective is the same: to set up a CNAME that points your new domain at `yourusername.pythonanywhere.com`. 39 | 40 | It can take a few minutes for your domain to start working, so be patient! 41 | 42 | 43 | ## Configure the domain via a web app on PythonAnywhere. 44 | 45 | You also need to tell PythonAnywhere that you want to use your custom domain. 46 | 47 | Go to the [PythonAnywhere Accounts page](https://www.pythonanywhere.com/account/) and upgrade your account. The cheapest option (a "Hacker" plan) is fine to start with, you can always upgrade it later when you get super-famous and have millions of hits. 48 | 49 | Next, go over to the [Web tab](https://www.pythonanywhere.com/web_app_setup/) and note down a couple of things: 50 | 51 | * Copy the **path to your virtualenv** and put it somewhere safe 52 | * Click through to your **wsgi config file**, copy the contents, and paste them somewhere safe. 53 | 54 | Next, **Delete** your old web app. Don't worry, this doesn't delete any of your code, it just switches off the domain at *yourusername.pythonanywhere.com*. Next, create a new web app, and follow these steps: 55 | 56 | * Enter your new domain name 57 | * Choose "manual configuration" 58 | * Pick Python 3.4 59 | * And we're done! 60 | 61 | When you get taken back to the web tab. 62 | 63 | * Paste in the virtualenv path you saved earlier 64 | * Click through to the wsgi configuration file, and paste in the contents from your old config file 65 | 66 | Hit reload web app, and you should find your site is live on its new domain! 67 | 68 | If you run into any problems, hit the "Send feedback" link on the PythonAnywhere site, and one of their friendly admins will be there to help you in no time. 69 | 70 | -------------------------------------------------------------------------------- /es/domain/README.md: -------------------------------------------------------------------------------- 1 | # Dominio 2 | 3 | PythonAnywhere nos da un dominio gratuito, pero tal vez tu no quieres tener un ".pythonanywhere.com" al final de tu URL de blog. Quizas tu quieres tener ago como "www.infinite-kitten-pictures.org" o "www.3d-printed-steam-engine-parts.com" o "www.antique-buttons.com" o "www.mutant-unicornz.net", o lo que quieras. 4 | 5 | Aquí vamos a hablar un poco sobre como obtener un dominio, y como enlazarlo con la aplicación en PythonAnywhere. Sin embargo, debes saber que los dominios cuestan dinero, y PythonAnywhere también te cobra mensualmente una tarifa para usar tu propio nombre de dominio -- no es mucho dinero en total, pero esto es posiblemente que solamente quieras si estas realmente comprometido. 6 | 7 | 8 | ## ¿Dónde registrar un dominio? 9 | 10 | Un nombre de dominio cuesta típicamente $15 USD al año. Hay opciones mas baratas y mas caras, dependiendo del probeedor. Existen muchas compañías con las que puedes comprar un dominio: una simple [busqueda en google](https://www.google.com/search?q=register%20domain) te dará muchas opciones. 11 | 12 | Nuestra favorita es [I want my name](https://iwantmyname.com/). Su anuncion dice "manejo de dominio sin dolor" y realmente es sin dolor. 13 | 14 | También puedes obtener dominios gratis. [dot.tk](http://www.dot.tk) es un lugar para obtener uno, pero debes estar conciente que los dominios gratuitos a veces se sienten baratos -- si tu sitio quiere ser para un negocio profesional, deberías pensar sobre pagar por un dominio "propio" que termine en `.com`. 15 | 16 | ## ¿Cómo apuntar tu dominio a PythonAnywhere? 17 | 18 | Si fuiste a través de *iwantmyname.com*, da click en `Domains` en el menú y escoge tu dominio recién comprado. Entonces localiza y da click en `manage DNS records`: 19 | 20 | ![](images/4.png) 21 | 22 | Ahora necesitas localizar este formulario: 23 | 24 | ![](images/5.png) 25 | 26 | Y llena la siguiente información 27 | 28 | - Hostname: www 29 | - Type: CNAME 30 | - Value: your domain from PythonAnywhere (for example djangogirls.pythonanywhere.com) 31 | - TTL: 60 32 | 33 | ![](images/6.png) 34 | 35 | Click en el botón `Add` y Guarda los cambios al final. 36 | 37 | > **Note** Si quieres un proveedor de dominio distinto, la interfaz para encontrar tu configuración de DNS/ CNAME será diferente, pero el objetivo es el mismo: Definir un CNAME que apunte tu nuevo dominio a `nombredeusuario.pythonanywhere.com`. 38 | 39 | También puede tomar algunos minutos para que tu dominio comience a funcionar. ¡Se paciente! 40 | 41 | ## Configura el dominio a través de la aplicación en PythonAnywhere. 42 | 43 | También necesitas decirle a PythonAnywhere que quieres utilizar tu dominio personalizado. 44 | 45 | Ve a la [página de cuenta de PythonAnywhere](https://www.pythonanywhere.com/account/) y mejora tu cuenta. La opcieon mas barata (el plan "Hacker:) está bien para comenzar, y siempre puedes mejorarlo después cuando te vuelvas super famoso y tengas millones de visitas. 46 | 47 | Siguiente, ve sobre la [pestaña Web](https://www.pythonanywhere.com/web_app_setup/) y anota un par de cosas: 48 | 49 | * Copia la **ruta a tu virtualenv** y colocala en un lugar seguro. 50 | * Da click a través de tu **archivo de configuración wsgi**, copia el contenido y pegalo en un lugar seguro. 51 | 52 | Siguiente, **elimina** tu vieja aplicación. No te preocupes, esto no elimina nada de tu código, solamente apaga el dominio en `nombredeusuario.pythonanywhere.com`. Siguiente, crea una nueva aplicación y sigue estos pasos: 53 | 54 | * Ingresa tu nombre de dominio 55 | * Escoge "configuración manual" 56 | * Selecciona Python 3.4 57 | * Y estamos listos 58 | 59 | Cuando seas enviado de vuelta a la pestaña web 60 | 61 | * Pega la ruta del ambiente virtual que guardaste antes. 62 | * Da click através del archivo de configuración, y pega el contenido de tu archivo de configuración viejo. 63 | 64 | Da click en actualizar web app y ¡deberías encontrar tu sitio corriendo en tu nuevo dominio! 65 | 66 | Si tienes problemas, da click en "Send Feedback" en el sitio PythonAnywhere, y uno de sus amables administradores te ayudará a solucionarlo en poco tiempo. 67 | 68 | -------------------------------------------------------------------------------- /ja/homework/README.md: -------------------------------------------------------------------------------- 1 | # 宿題: ウェブサイトにもっと機能を追加しよう! 2 | 3 | ここまでは長い道のりでしたが、まだまだ私たちのブログには改善の余地があります。つづいて、記事の草稿を作成して、それを投稿する機能を作ります。もはや不要になった投稿を削除する機能もつけましょう。すごい! 4 | 5 | ## あたらしい記事を草稿として保存する 6 | 7 | いまのところ、*New Post* フォームから記事を作成すると、そのままブログに記事が掲載されます。書いた記事をブログに表示せず、草稿として保存するには、`blog/views.py` の `post_new` 関数と `post_edit` 関数にある下の行を **削除** してください。 8 | 9 | ```python 10 | post.published_date = timezone.now() 11 | ``` 12 | 13 | こうすると新しい記事は草稿として保存されます。記事はいきなり掲載されず、あとで見直すことができるようになりました。今必要なのは草稿をリストアップし、ブログ上に掲載する機能です。それではやってみましょう! 14 | 15 | ## 草稿の一覧ページを作る 16 | 17 | Djangoのクエリセットを勉強した章を覚えていますか? `post_list` というビューを作って、ブログに掲載されている記事( `published_date` が設定されている記事)だけを表示するようにしました。 18 | 19 | ここでは、それと同じようなことをして、草稿が表示されるようにしましょう。 20 | 21 | `blog/templates/blog/base.html` のヘッダーにリンクを追加しましょう。草稿の一覧は誰でも見られるようにはしません。なので、 `{% if user.is_authenticated %}` という条件の確認に続く箇所で、新しい投稿を追加するボタンのすぐ後にリンクを書いてください。 22 | 23 | ```django 24 | 25 | ``` 26 | 27 | 次は `blog/urls.py` に、urlを追加しましょう! 28 | 29 | ```python 30 | path('drafts/', views.post_draft_list, name='post_draft_list'), 31 | ``` 32 | 33 | 続いて `blog/views.py` にビューを作ります。 34 | 35 | ```python 36 | def post_draft_list(request): 37 | posts = Post.objects.filter(published_date__isnull=True).order_by('created_date') 38 | return render(request, 'blog/post_draft_list.html', {'posts': posts}) 39 | ``` 40 | 41 | `posts = Post.objects.filter(published_date__isnull=True).order_by('created_date')` の行では、草稿( `published_date__isnull=True` となる記事)だけを集めています。そうして、`order_by('created_date')` で `created_date` 順に並べています。 42 | 43 | 最後にやるのはもちろんテンプレートの作成です! `blog/templates/blog/post_draft_list.html` を作成して、下の記述を加えます。 44 | 45 | ```django 46 | {% extends 'blog/base.html' %} 47 | 48 | {% block content %} 49 | {% for post in posts %} 50 |
51 |

created: {{ post.created_date|date:'d-m-Y' }}

52 |

{{ post.title }}

53 |

{{ post.text|truncatechars:200 }}

54 |
55 | {% endfor %} 56 | {% endblock %} 57 | ``` 58 | 59 | これって `post_list.html` の内容と似ていませんか? 60 | 61 | それでは `http://127.0.0.1:8000/drafts/` を開いてみましょう。草稿のリストが表示されるはずです。 62 | 63 | やった! これで1つ完了です! 64 | 65 | ## 掲載ボタンをつける 66 | 67 | 投稿の詳細ページに、押したらすぐに記事が掲載できるようなボタンがあるといいと思いませんか? 68 | 69 | `blog/templates/blog/post_detail.html` を開いて、下の行を・・・ 70 | 71 | ```django 72 | {% if post.published_date %} 73 |
74 | {{ post.published_date }} 75 |
76 | {% endif %} 77 | ``` 78 | 79 | 下のように変更してください。 80 | 81 | ```django 82 | {% if post.published_date %} 83 |
84 | {{ post.published_date }} 85 |
86 | {% else %} 87 | Publish 88 | {% endif %} 89 | ``` 90 | 91 | お気づきのように、`{% else %}` を追加しました。これは、 `{% if post.published_date %}` という条件が満たされない(記事に `published_date` が無い)ときに、 `Publish` を表示する、という意味です。 `{% url %}` のキーワード引数である `pk` に値を渡していることに注意してください。 92 | 93 | それでは新しいURLを追加しましょう。( `blog/urls.py` に) 94 | 95 | ```python 96 | path('post//publish/', views.post_publish, name='post_publish'), 97 | ``` 98 | 99 | 最後に *ビュー* を追加します。(いつものように `blog/views.py` に) 100 | 101 | ```python 102 | def post_publish(request, pk): 103 | post = get_object_or_404(Post, pk=pk) 104 | post.publish() 105 | return redirect('post_detail', pk=pk) 106 | ``` 107 | 108 | `Post` モデルを作ったときに、`publish` メソッドも作りました。こんな感じでしたね・・・ 109 | 110 | ```python 111 | def publish(self): 112 | self.published_date = timezone.now() 113 | self.save() 114 | ``` 115 | 116 | いよいよこの関数の出番です! 117 | 118 | 記事が掲載されるとすぐに `post_detail` ページにリダイレクトされます。 119 | 120 | ![Publish button](images/publish2.png) 121 | 122 | おめでとうございます! もうすぐ完成です。最後のステップとして削除ボタンを作りましょう! 123 | 124 | ## 記事を削除する 125 | 126 | `blog/templates/blog/post_detail.html` をもう一度開いてください。 127 | 下の行を編集ボタンの行の直後に追加します: 128 | 129 | ```django 130 | 131 | ``` 132 | 133 | URLも必要ですね。( `blog/urls.py` に) 134 | 135 | ```python 136 | path('post//remove/', views.post_remove, name='post_remove'), 137 | ``` 138 | 139 | 次はビューも作りましょう。 `blog/views.py` を開いて下のコードを追加してください。 140 | 141 | ```python 142 | def post_remove(request, pk): 143 | post = get_object_or_404(Post, pk=pk) 144 | post.delete() 145 | return redirect('post_list') 146 | ``` 147 | 148 | ここで唯一新しいことは、実際に記事を消す方法です。全てのDjangoモデルは `.delete()` で削除できます。簡単ですね! 149 | 150 | 今回は、記事を消したあとは、記事の一覧ページに行くようにします。なので `redirect` を使っています。 151 | 152 | テストしてみましょう! 投稿を表示して削除してみてください! 153 | 154 | ![Delete button](images/delete3.png) 155 | 156 | この章はこれでおわりです! よく頑張りましたね! 157 | -------------------------------------------------------------------------------- /ko/homework/README.md: -------------------------------------------------------------------------------- 1 | # 숙제: 블로그 개선하기! 2 | 3 | 이미 많은 것을 배웠어요. 하지만 아직 개선해 볼 것들이 남아있어요. 이제 게시글 미리 보기와 발행을 위한 기능을 추가해 볼 것입니다. 이제는 필요 없는 글을 삭제하는 것도 할거에요. 간단하게요! 4 | 5 | ## 미리 보기로 블로그 글 저장하기 6 | 7 | *새 글* 폼을 사용하면 글이 작성될 때 바로 발행 될 거에요. 미리 보기로 글을 저장하려면 미리 보기 `blog/views.py`파일에서 `post_new`와 `post_edit`메소드의 다음 줄을 **삭제**하세요. 8 | 9 | ```python 10 | post.published_date = timezone.now() 11 | ``` 12 | 13 | 이렇게 하면, 새로 작성한 글이 바로 게시되지 않고 미리 볼 수 있는 초안으로 저장이 됩니다. 지금 우리가 하려는 건 작성된 글과 목록을 미리 볼 수 있도록 할거에요. 이제 해보도록 하죠! 14 | 15 | ## 게시되지 않은 블로그 글 목록 페이지 만들기 16 | 17 | 기본 튜토리얼에서 배웠던 QuerySet 내용 모두 기억하고 있죠? 기본 튜토리얼에서는 블로그 게시물만 보여주는 `post_list`를 만들었어요. 18 | (기본 값으로 채워진 `published_date`도 함께요.) 19 | 20 | 이번에도 비슷한 것을 해볼 건데요. 하지만 이번에는 임시저장(draft) 기능을 구현해볼 거에요. 21 | 22 | 새 글 추가하기 버튼 근처에 `blog/templates/blog/base.html` 링크를 추가하세요(`

Django Girls Blog

`위에 바로 추가하면 됩니다!). 발행 전 미리 보기가 모두에게 보이는 걸 원치 않을 거예요. 새로운 글 추가하기 바로 아래에 `{% if user.is_authenticated %}`을 추가해 주세요. 23 | 24 | ```django 25 | 26 | ``` 27 | 28 | 다음: url입니다! `blog/urls.py`을 열고 아래 내용을 추가할 거에요. 29 | 30 | ```python 31 | url(r'^drafts/$', views.post_draft_list, name='post_draft_list'), 32 | ``` 33 | 34 | `blog/views.py`에 view를 생성할 차례입니다. 35 | 36 | ```python 37 | def post_draft_list(request): 38 | posts = Post.objects.filter(published_date__isnull=True).order_by('created_date') 39 | return render(request, 'blog/post_draft_list.html', {'posts': posts}) 40 | ``` 41 | 42 | `posts = Post.objects.filter(published_date__isnull=True).order_by('created_date')` 코드를 뜯어 살펴봅시다. (`published_date__isnull=True`) 코드로 발행되지 않은 글 목록을 가져옵니다. (`order_by('created_date')` 코드로 `created_date` 필드에 대해 오름차순 정렬을 수행합니다.) 43 | 44 | 마지막으로 템플릿을 수정해 봅시다. 아래 내용으로 `blog/templates/blog/post_draft_list.html` 파일을 생성해주세요. 45 | 46 | ```django 47 | {% extends 'blog/base.html' %} 48 | 49 | {% block content %} 50 | {% for post in posts %} 51 |
52 |

created: {{ post.created_date|date:'d-m-Y' }}

53 |

{{ post.title }}

54 |

{{ post.text|truncatechars:200 }}

55 |
56 | {% endfor %} 57 | {% endblock %} 58 | ``` 59 | 60 | `post_list.html` 템플릿과 코드가 많이 비슷해보이죠? 61 | 62 | 브라우저로 `http://127.0.0.1:8000/draft/` 페이지를 열어보면, 미 게시된 글목록을 확인할 수 있어요. 63 | 64 | 야호! 첫 번째 일이 마쳤어요! 65 | 66 | ## 게시 버튼 추가하기 67 | 68 | 게시글 상세페이지에 블로그 글을 바로 게시할 수 있는 버튼을 만들면 좋겠죠? 69 | 70 | `blog/templates/blog/post_detail.html` 를 열고, 아래 내용을 변경해봅시다: 71 | 72 | ```django 73 | {% if post.published_date %} 74 |
75 | {{ post.published_date }} 76 |
77 | {% endif %} 78 | ``` 79 | 80 | 에서 아래와 같이 변경합니다. 81 | 82 | ```django 83 | {% if post.published_date %} 84 |
85 | {{ post.published_date }} 86 |
87 | {% else %} 88 | Publish 89 | {% endif %} 90 | ``` 91 | 92 | 보시는 대로 `{% else %}` 템플릿 태그를 추가했습니다. 이는 `{% if post.published_date %}` 조건이 만족하지 않을 때 (`published_date` 필드가 비어있을 때), `Publish` 내용으로 렌더링됩니다. `{% url %}` 템플릿태그에 `pk` 인자를 넘겨줌에 유의하세요. 93 | 94 | `blog/urls.py`에 URL 패턴을 추가해봅시다. 95 | 96 | ```python 97 | url(r'^post/(?P\d+)/publish/$', views.post_publish, name='post_publish'), 98 | ``` 99 | 100 | 마지막으로 `post_publish` *뷰*를 `blog/views.py` 에 추가해봅시다. 101 | 102 | ```python 103 | def post_publish(request, pk): 104 | post = get_object_or_404(Post, pk=pk) 105 | post.publish() 106 | return redirect('post_detail', pk=pk) 107 | ``` 108 | 109 | 다음으로 `blog/models.py` 파일에서 `Post` 모델에 publish 멤버 함수를 추가해주세요. 110 | 111 | ```python 112 | def publish(self): 113 | self.published_date = timezone.now() 114 | self.save() 115 | ``` 116 | 117 | 자, 발행기능을 모두 구현했습니다! 118 | 119 | 글을 발행하면 바로 `post_detail` 페이지로 리다이렉션됩니다. 120 | 121 | ![Publish button](images/publish2.png) 122 | 123 | 축하해요! 거의 다 왔습니다. 마지막 단계는 삭제 버튼을 추가하는 것입니다. 124 | 125 | ## 블로그 글 삭제하기 126 | 127 | `blog/templates/blog/post_detail.html` 파일에 아래 코드를 추가해주세요. 128 | 129 | ```django 130 | 131 | ``` 132 | 133 | 수정 버튼 바로 아래 줄에 추가해주세요. 134 | 135 | (`blog/urls.py`)에 URL 패턴을 추가해봅시다: 136 | 137 | ```python 138 | url(r'^post/(?P\d+)/remove/$', views.post_remove, name='post_remove'), 139 | ``` 140 | 141 | 이제 post_remove 뷰를 구현해봅시다. `blog/views.py` 에 아래 코드를 추가해주세요. 142 | 143 | ```python 144 | def post_remove(request, pk): 145 | post = get_object_or_404(Post, pk=pk) 146 | post.delete() 147 | return redirect('post_list') 148 | ``` 149 | 150 | 이제 블로그 글을 삭제할 수 있게 되었어요. 장고 모델을 삭제할 때는 단순히`.delete()`를 호출하면 됩니다. 간단하게요! 151 | 152 | 글을 삭제하고 나면 글 목록 화면으로 리다이렉션됩니다. 이때 `redirect` 함수를 썼어요. 153 | 154 | 이제 테스트해봅시다! 블로그 글 페이지로 가서 삭제해 보세요! 155 | 156 | ![Delete button](images/delete3.png) 157 | 158 | 마지막까지 잘 해냈어요! 드디어 튜토리얼을 마쳤답니다! 정말 멋져요! 159 | -------------------------------------------------------------------------------- /ko/optional_postgresql_installation/README.md: -------------------------------------------------------------------------------- 1 | # 옵션: PostgreSQL 설치하기 2 | 3 | > **Note** 이 장의 일부는 [Geek Girls Carrots](http://django.carrots.pl/) 튜토리얼과 [django-marcador 4 | tutorial](http://django-marcador.keimlink.de/) 튜토리얼을 기초로 작성되었습니다. django-marcador 튜토리얼 저작권은 Markus Zapke-Gründemann et al이 소유하고 있습니다. 5 | 6 | ## 윈도우 7 | 8 | 다음 링크에서 Postgres 설치 프로그램을 다운로드하세요. : http://www.enterprisedb.com/products-services-training/pgdownload#windows 9 | 10 | 운영체제에 맞는 최신 버전의 설치 프로그램을 다운받은 후 다음 가이드를 따라 설치하세요. : http://www.postgresqltutorial.com/install-postgresql/. 설치 경로가 필요하니 주의깊게 봐두세요. (특히 `C:\Program Files\PostgreSQL\9.3` 부분입니다.) 11 | 12 | ## 맥 OS X 13 | 14 | 다음 링크에서 Postgres App 설치 프로그램을 다운로드하세요. : http://postgresapp.com/ 15 | 16 | 다운로드한 애플리케이션을 응용 프로그램 디렉터리로 드래그한 후 더블클릭하면 됩니다! 17 | 18 | `PATH` 변수에 Postgres 커맨드 라인을 추가해야 합니다. 자세한 내용은 [링크](http://postgresapp.com/documentation/cli-tools.html)를 확인하세요. 19 | 20 | ## 리눅스 21 | 22 | 리눅스는 배포판에 따라 설치방법이 매우 다양합니다. 여기서는 우분투 리눅스, 페도라 리눅스를 기준으로 설명하겠습니다. 다른 배포판은 [PostgresSQL 공식 문서 중 리눅스 설치 내용](https://wiki.postgresql.org/wiki/Detailed_installation_guides#General_Linux)을 참고하세요. 23 | 24 | ### 우분투 25 | 26 | 아래 명령을 실행하세요. 27 | 28 | sudo apt-get install postgresql postgresql-contrib 29 | 30 | ### 페도라 31 | 32 | 아래 명령을 실행하세요. 33 | 34 | sudo yum install postgresql93-server 35 | 36 | # 데이터베이스 생성하기 37 | 38 | 이제 데이터베이스를 생성하고, 그 데이터베이스에 접근할 유저 계정을 생성할게요. PostgreSQL에서는 원하는 만큼의 데이터베이스와 유저 계정을 생성할 수 있어요. 그래서 다수의 사이트를 운영할 경우, 각 사이트마다 데이터베이스를 생성할 수 있답니다. 39 | 40 | ## 윈도우 41 | 42 | 윈도에서 몇 가지 설정 작업이 더 필요해요. 지금 다루는 설정을 모두 이해할 필요는 없어요. 하지만 각 설정들이 어떤 역할을 하는지 궁금하다면, 코치들에게 자유롭게 물어보세요. 43 | 44 | 1. 명령 프롬프트를 실행하세요. (시작 → 모든 프로그램 → 보조 프로그램 → 명령 프롬프트) 45 | 2. 다음 명령을 입력하고 엔터키를 입력해주세요: `setx PATH "% PATH%;C:\Program Files\PostgreSQL\9.3\bin"`. 프롬프트에서 코드를 붙여 넣기 하려면, 마우스 오른쪽 버튼을 클릭한 후 메뉴에서 `붙여 넣기를 선택하세요. 경로 끝에 `\bin` 이 있는지 확인하세요. `SUCCESS: Specified value was saved.`라는 메시지가 보여야 해요. 46 | 3. 명령 프롬프트 창을 닫고 다시 열어주세요. 47 | 48 | 49 | ## 데이터베이스 생성하기 50 | 51 | 먼저, `psql` 명령을 입력해서 Postgres 콘솔을 실행하겠습니다. 콘솔을 어떻게 실행시키는지 모두 기억하고 있죠? 52 | 53 | >맥 OS X 에서는 터미널 애플리케이션을 실행하세요. (애플리케이션 → 유틸리티 안에 있어요) 리눅스에서는 애플리케이션(Applications) → 보조 프로그램(Accessories) → 터미널(Command) 안에 있습니다. 윈도우에서는 시작 메뉴 → 모든 프로그램 → 악세사리 → 명령 프롬프트 에 있습니다. 간혹 윈도우에서는 `psql` 실행 시에, Postgres 설치 시에 선택했던 username과 password입력을 요구할 수 있습니다. 만약 psql에서 암호를 입력했는데 인증되지 않는다면, `psql -U -W` 명령을 실행한 후 암호를 입력해보세요. 54 | 55 | $ psql 56 | psql (9.3.4) 57 | Type "help" for help. 58 | # 59 | 60 | 프롬프트가 `$` 에서 `#` 으로 바뀌었어요. 이제 PostgreSQL로 명령을 보낼 수 있다는 뜻이에요. `CREATE USER name;` 명령을 실행해 유저 계정을 생성할게요. 61 | 62 | # CREATE USER name; 63 | 64 | `name` 부분을 로그인 아이디(혹은 자기 이름)로 변경해주세요. 악센트가 들어간 글자나 공백 문자는 쓸 수 없습니다. (예를 들어 `bożena maria`일 경우 유효하지 않습니다. `bozena_maria`로 변경해주세요.) 성공하면 콘솔에서 `CREATE ROLE` 응답이 보일 거예요. 65 | 66 | 이제 장고 프로젝트에서 쓸 데이터베이스를 생성해봐요. 67 | 68 | # CREATE DATABASE djangogirls OWNER name; 69 | 70 | `name`에는 위 ROLE에서 입력했던 `name`을 써야 해요. (예: `bozena_maria`). 이제 현재 프로젝트에서 사용할 수 있는 빈 데이터베이스가 만들어졌어요. 성공하면 콘솔에서 `CREATE ROLE` 응답이 보일 거예요. 71 | 72 | 잘했어요. 이 데이터베이스는 모두 정렬된 데이터베이스랍니다! 73 | 74 | # 프로젝트 설정 수정하기 75 | 76 | `mysite/settings.py` 파일에서 `DATABASES` 부분을 찾아서 77 | 78 | ```python 79 | DATABASES = { 80 | 'default': { 81 | 'ENGINE': 'django.db.backends.sqlite3', 82 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 83 | } 84 | } 85 | ``` 86 | 87 | 아래와 같이 수정해주세요. 88 | 89 | ```python 90 | DATABASES = { 91 | 'default': { 92 | 'ENGINE': 'django.db.backends.postgresql', 93 | 'NAME': 'djangogirls', 94 | 'USER': 'name', 95 | 'PASSWORD': '', 96 | 'HOST': 'localhost', 97 | 'PORT': '', 98 | } 99 | } 100 | ``` 101 | 102 | `USER`와 `NAME`에는 방금 전 생성했던 `name` 을 입력해주세요. 103 | 104 | 105 | # PostgreSQL 파이썬 패키지 설치하기 106 | 107 | 먼저 https://toolbelt.heroku.com에서 Heroku Toolbelt을 다운로드하여 설치해주세요. 이 Toolbelt에는 Git과 더불어 배포에 필요한 모든 유틸리티들이 들어있어요. 108 | 109 | 다음으로 PostgreSQL 패키지를 설치해야 합니다. 이 패키지 이름은 `psycopg2`입니다. 설치방법은 윈도우와 리눅스/맥 마다 조금 달라요. 110 | 111 | 112 | 113 | ## 윈도우 114 | 115 | 윈도우에서는 http://www.stickpeople.com/projects/python/win-psycopg/ 에서 바이너리 패키지를 받으세요. 116 | 117 | 현재 설치되어있는 파이썬 버전과 운영체제(왼쪽 칸 32비트, 오른쪽 칸 64비트)에 맞는 패키지를 다운로드합니다. 118 | 119 | 다운받은 파일을 `C:\` 경로로 옮겨주세요. `C:\psycopg2.exe` 위치에 있어야 합니다. 120 | 121 | 터미널에서 `virtualenv` 을 활성화하고, 아래 명령을 실행하세요. 122 | 123 | easy_install C:\psycopg2.exe 124 | 125 | ## 리눅스 / 맥 OS X 126 | 127 | 터미널에서 아래 명령을 실행하세요. 128 | 129 | 130 | (myvenv) ~/djangogirls$ pip install psycopg2 131 | 132 | 문제가 없다면 아래와 같은 메시지가 보일 거에요. 133 | 134 | Downloading/unpacking psycopg2 135 | Installing collected packages: psycopg2 136 | Successfully installed psycopg2 137 | Cleaning up... 138 | 139 | --- 140 | 141 | 완료되면 `python -c "import psycopg2"` 명령을 실행하세요. 모든 오류가 없다면 성공적으로 설치가 끝난 거에요. 142 | 143 | # 마이그레이션 적용과 슈퍼 유저 생성하기 144 | 145 | 웹사이트 프로젝트에서 새로 생성된 데이터베이스를 사용하려면 모든 마이그레이션을 적용해야 해요. 가상 환경에서 아래 코드를 실행하세요. 146 | 147 | (myvenv) ~/djangogirls$ python manage.py migrate 148 | 149 | 블로그에 새 글을 추가하려면 다음 코드를 실행하여 수퍼 유저를 만들어야 해요. 150 | 151 | (myvenv) ~/djangogirls$ python manage.py createsuperuser --username name 152 | 153 | `name`을 사용자 이름으로 바꾸는 것을 잊지 마세요. 이메일과 비밀번호를 묻는 메시지가 나타날 거예요. 154 | 155 | 이제 다시 서버를 실행하고, 슈퍼 유저 계정으로 애플리케이션에 로그인을 한 다음 새 데이터베이스에 게시물을 추가할 수 있어요. -------------------------------------------------------------------------------- /ja/optional_postgresql_installation/README.md: -------------------------------------------------------------------------------- 1 | # PostgreSQLのインストール 2 | 3 | > このチャプターの一部はGeek Girls Carrotsのチュートリアルをもとにしています。(http://django.carrots.pl/). 4 | 5 | > このチャプターの一部は、Creative Commons Attribution-ShareAlike 4.0 International Licenseの下で提供される[django-marcador tutorial](http://django-marcador.keimlink.de/)をもとにしています。django-marcador tutorialは、Markus Zapke-Gründemannらに著作権が帰属します。 6 | 7 | ## Windows 8 | 9 | PostgresをWindowsにインストールする一番簡単な方法は、次のリンクから入手できるプログラムを使うことです: http://www.enterprisedb.com/products-services-training/pgdownload#windows 10 | 11 | お使いのOSで利用できる最新のバージョンを選んでください。インストーラをダウンロードし、実行し、次のリンクの指示に従ってください:http://www.postgresqltutorial.com/install-postgresql/ 12 | インストール先のディレクトリは次のステップで必要ですので、控えておいてください。(通常は `C:\Program Files\PostgreSQL\9.3` のようになります) 13 | 14 | ## Mac OS X 15 | 16 | 一番簡単な方法は、Mac OSの他のアプリケーションと同様に、無料の [Postgres.app](http://postgresapp.com/) をダウンロードし、インストールすることです。 17 | 18 | ダウンロードし、「アプリケーション」ディレクトリにドラッグし、ダブルクリックで実行してください。これでおしまいです! 19 | 20 | [ここ](http://postgresapp.com/documentation/cli-tools.html)に書かれているように、`PATH` 変数にPostgresのコマンドラインツールを追加する必要があります。 21 | 22 | ## Linux 23 | 24 | インストール手順はディストリビューションごとに異なります。以下にあるのは、UbuntuとFedora向けのコマンドです。これら以外のディストリビューションをお使いの場合は、[PostgreSQLのドキュメントを参照してください](https://wiki.postgresql.org/wiki/Detailed_installation_guides#General_Linux)。 25 | 26 | ### Ubuntu 27 | 28 | 次のコマンドを実行してください: 29 | 30 | sudo apt-get install postgresql postgresql-contrib 31 | 32 | ### Fedora 33 | 34 | 次のコマンドを実行してください: 35 | 36 | sudo yum install postgresql93-server 37 | 38 | # データベースを作成する 39 | 40 | 次に、最初のデータベースと、それにアクセスできるユーザを作ります。PostgreSQLでは好きなだけデータベースとユーザを作成できるので、複数のサイトを稼働させる場合は、サイトごとにデータベースを作るべきです。 41 | 42 | ## Windows 43 | 44 | Windowsをお使いの場合は、完了する必要のあるいくつかの追加のステップがあります。今の時点では、ここで行う設定を理解することは重要ではありませんが、何が行われているか興味がある方はお気軽にコーチに質問してください。 45 | 46 | 1. コマンドプロンプトを開きます(「スタートメニュー」→「アクセサリ」→「コマンドプロンプト」) 47 | 2. 次のように入力して、Enterキーを押して実行します:`setx PATH "%PATH%;C:\Program Files\PostgreSQL\9.3\bin"`。コマンドプロンプトを右クリックして `貼り付け` を選択することで、コピー&ペーストもできます。`"%PATH%;"` に続く部分が、インストール時に控えたパスに `\bin` をつけ足したものと同じか確認してくださいね。`成功: 指定した値は保存されました。`(`SUCCESS: Specified value was saved.`)というメッセージが現れるでしょう。 48 | 3. コマンドプロンプトを閉じて、再度開きます。 49 | 50 | ## データベース作成 51 | 52 | まずは、`psql` コマンドを実行して、Postgresコンソールを起動しましょう。コンソールの起動の仕方は覚えていますか? 53 | >Mac OS Xでは、`ターミナル`アプリケーションを起動して行います(「アプリケーション」→「ユーティリティ」の中にあります)。Linuxでは、おそらく「アプリケーション」→「アクセサリ」→「ターミナル」となるでしょう。Windowsでは、「スタートメニュー」→「すべてのプログラム」→「アクセサリ」→「コマンドプロンプト」となります。さらに言うと、Windowsでは、インストール時に設定したユーザ名とパスワードを使ってログインすることを `psql` コマンドが求めてくるかもしれません。`psql` コマンドがパスワードの入力を求めていて、うまくいかない場合は、`psql -U -W` コマンドをまず実行し、その後にパスワードを入力してください。 54 | 55 | $ psql 56 | psql (9.3.4) 57 | Type "help" for help. 58 | # 59 | 60 | 先頭の `$` が `#` に変わりました。これから入力するコマンドはPostgreSQLに送られることを意味します。`CREATE USER name;` コマンドでユーザを作りましょう。(最後にセミコロン(;)を使うことを覚えておいてくださいね): 61 | 62 | # CREATE USER name; 63 | 64 | `name` の部分はご自分のお名前に書き換えてくださいね。アクセント文字や空白文字は使うべきではありません。(例えば `bożena maria` はうまくいきません。`bozena_maria` のように書き換える必要があります。)うまくいくと、コンソールに `CREATE ROLE` と表示されます。 65 | 66 | いよいよあなたのDjangoプロジェクトのデータベースを作るときです: 67 | 68 | # CREATE DATABASE djangogirls OWNER name; 69 | 70 | `name` は先ほど設定した名前(例 `bozena_maria`)に置き換えてくださいね。このコマンドは、あなたのプロジェクトで今から使える空っぽのデータベースを作ります。うまくいくと、コンソールに `CREATE DATABASE` と表示されます。 71 | 72 | 素晴らしい!データベースの作成は完了です! 73 | 74 | # 設定の更新 75 | 76 | `mysite/settings.py` ファイルの中から、以下の部分を見つけてください: 77 | 78 | ```python 79 | DATABASES = { 80 | 'default': { 81 | 'ENGINE': 'django.db.backends.sqlite3', 82 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 83 | } 84 | } 85 | ``` 86 | 87 | その部分を以下のように書き換えます: 88 | 89 | ```python 90 | DATABASES = { 91 | 'default': { 92 | 'ENGINE': 'django.db.backends.postgresql', 93 | 'NAME': 'djangogirls', 94 | 'USER': 'name', 95 | 'PASSWORD': '', 96 | 'HOST': 'localhost', 97 | 'PORT': '', 98 | } 99 | } 100 | ``` 101 | 102 | `name` をこのチャプターで作ったユーザの名前に書き換えてくださいね。 103 | 104 | # PythonのPostgreSQL用パッケージのインストール 105 | 106 | まず、Heroku Toolbelt を https://toolbelt.heroku.com/ からインストールします。これは後であなたのサイトをデプロイするときに主に必要になりますが、Gitも含んでいます。Gitはもう重宝しているかもしれませんね。 107 | 108 | 次は、PythonがPostgreSQLとやりとりできるようにするためのパッケージをインストールします。`psycopg2` という名前のパッケージです。Windowsの場合と、LinuxまたはOS Xの場合とで、インストール手順はわずかに異なります。 109 | 110 | ## Windows 111 | 112 | Windowsでは、ビルド前のファイルを http://www.stickpeople.com/projects/python/win-psycopg/ からダウンロードしてください。 113 | 114 | あなたの使っているPythonのバージョンに対応するものであること(Python 3.4 対応版は最後の行にあります)と、アーキテクチャに対応するものであること(32ビット版は左の列、64ビット版は右の列です)を確認してください。 115 | 116 | ダウンロードしたファイルの名前を変更してから移動し、`C:\psycopg2.exe` とします。 117 | 118 | ここまで終わったら、コマンドラインに以下のコマンドを入力します(`virtualenv` が有効になっていることを確認して実行してください): 119 | 120 | easy_install C:\psycopg2.exe 121 | 122 | ## Linux and OS X 123 | 124 | コマンドラインで以下を実行します: 125 | 126 | (myvenv) ~/djangogirls$ pip install psycopg2 127 | 128 | うまくいくと、以下のような出力が表示されるでしょう: 129 | 130 | Downloading/unpacking psycopg2 131 | Installing collected packages: psycopg2 132 | Successfully installed psycopg2 133 | Cleaning up... 134 | 135 | --- 136 | 137 | 完了したら、`python -c "import psycopg2"` というコマンドを実行します。エラーが表示されなければ、インストールは全て成功しました。 138 | 139 | # マイグレーションの適用とsuperuserの作成 140 | 141 | 新しく作成したデータベースをあなたのウェブサイトのプロジェクトで使うために、全てのマイグレーションを適用する必要があります。仮想環境の中で以下のコマンドを実行してください: 142 | 143 | (myvenv) ~/djangogirls$ python manage.py migrate 144 | 145 | ブログに記事を追加するために、以下のコマンドを実行して、superuserを作ります: 146 | 147 | (myvenv) ~/djangogirls$ python manage.py createsuperuser --username name 148 | 149 | これまでのように、コマンドの `name` はこのチャプターで作ったユーザ名に置き換えてください。メールアドレスとパスワードを入力するように指示されます。 150 | 151 | 以上で、サーバを起動し、superuserのアカウントでアプリケーションにログインし、新しいデータベースに記事を追加することができるようになりました。 152 | -------------------------------------------------------------------------------- /en/homework/README.md: -------------------------------------------------------------------------------- 1 | # Homework: add more to your website! 2 | 3 | Our blog has come a long way but there's still room for improvement. Next, we will add features for post drafts and their publication. We will also add deletion of posts that we no longer want. Neat! 4 | 5 | ## Save new posts as drafts 6 | 7 | Currently when we're creating new posts using our *New post* form the post is published directly. To instead save the post as a draft, **remove** this line in `blog/views.py` in the `post_new` and `post_edit` methods: 8 | 9 | ```python 10 | post.published_date = timezone.now() 11 | ``` 12 | 13 | This way, new posts will be saved as drafts that we can review later on rather than being instantly published. All we need now is a way to list and publish drafts, let's get to it! 14 | 15 | ## Page with list of unpublished posts 16 | 17 | Remember the chapter about querysets? We created a view `post_list` that displays only published blog posts (those with non-empty `published_date`). 18 | 19 | Time to do something similar, but for draft posts. 20 | 21 | Let's add a link in `blog/templates/blog/base.html` in the header. We don't want to show our list of drafts to everybody, so we'll put it inside the `{% if user.is_authenticated %}` check, right after the button for adding new posts. 22 | 23 | ```django 24 | 25 | ``` 26 | 27 | Next: urls! In `blog/urls.py` we add: 28 | 29 | ```python 30 | path('drafts/', views.post_draft_list, name='post_draft_list'), 31 | ``` 32 | 33 | Time to create a view in `blog/views.py`: 34 | 35 | ```python 36 | def post_draft_list(request): 37 | posts = Post.objects.filter(published_date__isnull=True).order_by('created_date') 38 | return render(request, 'blog/post_draft_list.html', {'posts': posts}) 39 | ``` 40 | 41 | The line ` posts = Post.objects.filter(published_date__isnull=True).order_by('created_date')` makes sure that we take only unpublished posts (`published_date__isnull=True`) and order them by `created_date` (`order_by('created_date')`). 42 | 43 | Ok, the last bit is of course a template! Create a file `blog/templates/blog/post_draft_list.html` and add the following: 44 | 45 | ```django 46 | {% extends 'blog/base.html' %} 47 | 48 | {% block content %} 49 | {% for post in posts %} 50 |
51 |

created: {{ post.created_date|date:'d-m-Y' }}

52 |

{{ post.title }}

53 |

{{ post.text|truncatechars:200 }}

54 |
55 | {% endfor %} 56 | {% endblock %} 57 | ``` 58 | 59 | It looks very similar to our `post_list.html`, right? 60 | 61 | Now when you go to `http://127.0.0.1:8000/drafts/` you will see the list of unpublished posts. 62 | 63 | Yay! Your first task is done! 64 | 65 | ## Add publish button 66 | 67 | It would be nice to have a button on the blog post detail page that will immediately publish the post, right? 68 | 69 | Let's open `blog/templates/blog/post_detail.html` and change these lines: 70 | 71 | ```django 72 | {% if post.published_date %} 73 |
74 | {{ post.published_date }} 75 |
76 | {% endif %} 77 | ``` 78 | 79 | into these: 80 | 81 | ```django 82 | {% if post.published_date %} 83 |
84 | {{ post.published_date }} 85 |
86 | {% else %} 87 | Publish 88 | {% endif %} 89 | ``` 90 | 91 | As you noticed, we added `{% else %}` line here. That means, that if the condition from `{% if post.published_date %}` is not fulfilled (so if there is no `published_date`), then we want to do the line `Publish`. Note that we are passing a `pk` variable in the `{% url %}`. 92 | 93 | Time to create a URL (in `blog/urls.py`): 94 | 95 | ```python 96 | path('post//publish/', views.post_publish, name='post_publish'), 97 | ``` 98 | 99 | and finally, a *view* (as always, in `blog/views.py`): 100 | 101 | ```python 102 | def post_publish(request, pk): 103 | post = get_object_or_404(Post, pk=pk) 104 | post.publish() 105 | return redirect('post_detail', pk=pk) 106 | ``` 107 | 108 | Remember, when we created a `Post` model we wrote a method `publish`. It looked like this: 109 | 110 | ```python 111 | def publish(self): 112 | self.published_date = timezone.now() 113 | self.save() 114 | ``` 115 | 116 | Now we can finally use this! 117 | 118 | And once again after publishing the post we are immediately redirected to the `post_detail` page! 119 | 120 | ![Publish button](images/publish2.png) 121 | 122 | Congratulations! You are almost there. The last step is adding a delete button! 123 | 124 | ## Delete post 125 | 126 | Let's open `blog/templates/blog/post_detail.html` once again and add this line: 127 | 128 | ```django 129 | 130 | ``` 131 | 132 | just under a line with the edit button. 133 | 134 | Now we need a URL (`blog/urls.py`): 135 | 136 | ```python 137 | path('post//remove/', views.post_remove, name='post_remove'), 138 | ``` 139 | 140 | Now, time for a view! Open `blog/views.py` and add this code: 141 | 142 | ```python 143 | def post_remove(request, pk): 144 | post = get_object_or_404(Post, pk=pk) 145 | post.delete() 146 | return redirect('post_list') 147 | ``` 148 | 149 | The only new thing is to actually delete a blog post. Every Django model can be deleted by `.delete()`. It is as simple as that! 150 | 151 | And this time, after deleting a post we want to go to the webpage with a list of posts, so we are using `redirect`. 152 | 153 | Let's test it! Go to the page with a post and try to delete it! 154 | 155 | ![Delete button](images/delete3.png) 156 | 157 | Yes, this is the last thing! You completed this tutorial! You are awesome! 158 | -------------------------------------------------------------------------------- /es/homework/README.md: -------------------------------------------------------------------------------- 1 | # Tarea: Agregar mas a tu sitio web 2 | 3 | Nuestro blog ha recorrido un gran camino hasta aquí, pero aún hay espacio para la mejora. Lo siguiente, vamos a agregar nuevas características para borradores y su publicación. También vamos a agregar eliminación de post que ya no queremos mas. 4 | 5 | ## Guarda nuevos post como borradores 6 | 7 | Actualmente, nosotros creamos nuevos post usando nuestro formulario *New Post* y el post es publicado directamente. En lugar de publicar el post vamos a guardarlos com borradores. **elimina** esta linea en `blog/views.py` en los méetodos `post_new` y `post_edit`: 8 | 9 | ```python 10 | post.published_date = timezone.now() 11 | ``` 12 | 13 | De esta manera los nuevos post serán guardados como borradores y se podrán revisar después en vez de ser publicados instantáneamente. Todo lo que necesitamos es una manera de listar los borradores. ¡Vamos a hacerlo! 14 | 15 | ## Página con los post no publicados 16 | 17 | ¿Recuerdas el capítulo sobre los querysets? Creamos una vista `post_list` que muestra solamente los post publicados (aquellos que tienen un `publication_date` no vacío). 18 | 19 | Es tiempo de hacer algo similiar, pero con borradores. 20 | 21 | Vamos a añadir un enlace en `blog/templates/blog/base.html` en el encabezado. No queremos mostrar nuestro borradores a todo el mundo, entonces vamos a colocarlo dentro de la verificación `{% if user.is_authenticated %}`, justo después del botón de agregar posts. 22 | 23 | ```django 24 | 25 | ``` 26 | 27 | Siguiente: ¡urls! en `blog/urls.py` vamos a agregar: 28 | 29 | ```python 30 | path('drafts/', views.post_draft_list, name='post_draft_list'), 31 | ``` 32 | 33 | Tiempo de crear una nueva vista en `blog/views.py` 34 | 35 | ```python 36 | def post_draft_list(request): 37 | posts = Post.objects.filter(published_date__isnull=True).order_by('created_date') 38 | return render(request, 'blog/post_draft_list.html', {'posts': posts}) 39 | ``` 40 | 41 | Esta línea ` posts = Post.objects.filter(published_date__isnull=True).order_by('created_date')` se asegura de que solamente vamos a tomar post no publicados (`published_date__isnull=True`) y los ordena por `created_date` (`order_by('created_date')`). 42 | 43 | Ok, el último pedazo es la plantilla. Crea un archivo `blog/templates/blog/post_draft_list.html` y agrega lo siguiente: 44 | 45 | ```django 46 | {% extends 'blog/base.html' %} 47 | 48 | {% block content %} 49 | {% for post in posts %} 50 |
51 |

created: {{ post.created_date|date:'d-m-Y' }}

52 |

{{ post.title }}

53 |

{{ post.text|truncatechars:200 }}

54 |
55 | {% endfor %} 56 | {% endblock %} 57 | ``` 58 | 59 | Se ve muy similar a nuestr `post_list.html` ¿verdad? 60 | 61 | Ahora cuando vayas a `http://127.0.0.1:8000/drafts/` vas a ver la lista de post no publicados. 62 | 63 | ¡Bien! ¡Nuestra primera tarea hecha! 64 | 65 | ## Agrega un botón de publicación 66 | 67 | Sería bueno tener un botón en el detalle del post que publique el post inmediatamente, ¿no? 68 | 69 | Vamos a abrir `blog/templates/blog/post_detail.html` y cambiar estas líneas: 70 | 71 | ```django 72 | {% if post.published_date %} 73 |
74 | {{ post.published_date }} 75 |
76 | {% endif %} 77 | ``` 78 | 79 | por estas: 80 | 81 | ```django 82 | {% if post.published_date %} 83 |
84 | {{ post.published_date }} 85 |
86 | {% else %} 87 | Publish 88 | {% endif %} 89 | ``` 90 | 91 | Como puedes ver, hemos agregado la línea `{% else %}`. Esto significa, que la condición de `{% if post.published_date %}` no es cumplida (entonces no hay `publication_date`), entonces queremos agregar la línea `Publish`. Nota que estamos pasando la variable `pk` en el `{% url %}`. 92 | 93 | Tiempo de crear una URL (en `blog/urls.py`): 94 | 95 | ```python 96 | path('post//publish/', views.post_publish, name='post_publish'), 97 | ``` 98 | 99 | Y finalmente una *vista* (como siempre, en `blog/views.py`): 100 | 101 | ```python 102 | def post_publish(request, pk): 103 | post = get_object_or_404(Post, pk=pk) 104 | post.publish() 105 | return redirect('post_detail', pk=pk) 106 | ``` 107 | 108 | Recuerda, cuando creamos el modelo `Post` escribimos un método `publush`. Se veía como esto: 109 | 110 | ```python 111 | def publish(self): 112 | self.published_date = timezone.now() 113 | self.save() 114 | ``` 115 | 116 | ¡Ahora finalmente podemos usarlo! 117 | 118 | Y de nuevo al publicar el post, ¡somos redirigidos inmediatamente a la página `post_detail`! 119 | 120 | ![Publish button](images/publish2.png) 121 | 122 | ¡Felicitaciones! ¡Casi terminas, el último paso es un botón de eliminar! 123 | 124 | ## Eliminar post 125 | 126 | Vamos a abrir `blog/templates/blog/post_detail.html` de nuevo y vamos a añadir esta línea 127 | 128 | ```django 129 | 130 | ``` 131 | 132 | Justo debajo de la línea co el botón editar. 133 | 134 | Ahora necesitamos una URL (`blog/urls.py`): 135 | 136 | ```python 137 | path('post//remove/', views.post_remove, name='post_remove'), 138 | ``` 139 | 140 | Ahora, ¡Tiempo para la vista! Abre `blog/views.py` y agrega este código: 141 | 142 | ```python 143 | def post_remove(request, pk): 144 | post = get_object_or_404(Post, pk=pk) 145 | post.delete() 146 | return redirect('post_list') 147 | ``` 148 | 149 | La única cosa nueva es en realidad, eliminar el post. Cada modelo en django puede ser eliminado con `.delete()`. ¡Así de simple! 150 | 151 | Y en este momento, después de eliminar un post, queremos ir a la página web con la lista de posts, por eso usamos el `redirect`. 152 | 153 | ¡Vamos a probarlo! ¡Vamos a la página con los post e intentemos eliminarlos! 154 | 155 | ![Delete button](images/delete3.png) 156 | 157 | Si, ¡esto es lo último! ¡Acabas de completar este tutorial! ¡Eres asombroso! 158 | -------------------------------------------------------------------------------- /ja/authentication_authorization/README.md: -------------------------------------------------------------------------------- 1 | # 宿題: ウェブサイトをセキュアにする 2 | 3 | 管理画面を使用する以外で、パスワードを使用する必要がないことに気づいたかもしれません。これは、誰でもあなたのブログの投稿を追加したり編集したりできるということを意味するということにも気づいたかもしれませんね。私はあなたのことを存じ上げませんが、私は他の誰かが私のブログに投稿することは望んでいません。なので、対策をほどこしましょう。 4 | 5 | ## 投稿の追加/編集の承認 6 | 7 | まずはセキュアにしてみましょう。`post_new`, `post_edit`, `post_draft_list`, `post_remove`, `post_publish`のビューを保護し、ログインしているユーザだけがアクセスできるようにします。そうするために、Djangoには、_デコレータ_ と呼ばれる素敵なヘルパーが同梱されています。今は技術的なことは気にしないでください。あとでフォローします。使いたいデコレータは、Djangoの`django.contrib.auth.decorators`モジュールに同梱された、`login_required`というものです。 8 | 9 | それでは、`blog/views.py`を編集し、一番上に次のimport文を追加してください: 10 | 11 | ```python 12 | from django.contrib.auth.decorators import login_required 13 | ``` 14 | 15 | 次に`post_new`, `post_edit`, `post_draft_list`, `post_remove`, `post_publish`のそれぞれのビューの前に、以下のように1行追加してください(ビューにデコレータをつけています): 16 | 17 | ```python 18 | @login_required 19 | def post_new(request): 20 | [...] 21 | ``` 22 | 23 | 以上です!`http://localhost:8000/post/new/`にアクセスしてみましょう。違いに気づきましたか? 24 | 25 | > 空のフォームが出てきたときは、管理画面のチャプターからログインしたままかもしれません。`http://localhost:8000/admin/logout/`にアクセスしてログアウトして、再度`http://localhost:8000/post/new/`にアクセスしてみてください。 26 | 27 | 大切なエラーの一つが見えましたね。実際、これはとても興味深いです。追加したデコレータはログインページにリダイレクトするように動きますが、ログインページがまだ利用できないので、「Page not found (404)」が表示されています。 28 | 29 | `post_edit`, `post_remove`, `post_draft_list`, `post_publish`にもデコレータを追加することを忘れないでくださいね。 30 | 31 | やった!目標に一歩近づきました!!もう、他の人が私たちのブログに投稿することはできません。しかし残念ながら、私たちも投稿することができなくなってしまいました。次はそれを修正しましょう。 32 | 33 | ## ユーザーログイン 34 | 35 | ユーザーとパスワードと認証を実装するために多くの素敵なものを使ってみることもできますが、正しく使うのはやや複雑になります。Djangoは「電池同梱販売」(訳注:すべて揃っていることを表す)なので、ユーザーとパスワードと認証の実装という困難な仕事を誰かが成し遂げています。そこで、Djangoで提供されている認証ツールをさらに活用します。 36 | 37 | `mysite/urls.py`の中に、`path('accounts/login/', views.LoginView.as_view(), name='login')`というurlを追加します。ファイルは次のようになるでしょう: 38 | 39 | ```python 40 | from django.urls import path, include 41 | from django.contrib import admin 42 | 43 | from django.contrib.auth import views 44 | 45 | urlpatterns = [ 46 | path('admin/', admin.site.urls), 47 | path('accounts/login/', views.LoginView.as_view(), name='login'), 48 | path('', include('blog.urls')), 49 | ] 50 | ``` 51 | 52 | そして、ログインページのテンプレートが必要なので、`blog/templates/registration`というディレクトリを作って、中に`login.html`というファイルを作成してください: 53 | 54 | ```django 55 | {% extends "blog/base.html" %} 56 | 57 | {% block content %} 58 | {% if form.errors %} 59 |

Your username and password didn't match. Please try again.

60 | {% endif %} 61 | 62 |
63 | {% csrf_token %} 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
{{ form.username.label_tag }}{{ form.username }}
{{ form.password.label_tag }}{{ form.password }}
74 | 75 | 76 | 77 |
78 | {% endblock %} 79 | ``` 80 | 81 | ブログの全体的な見た目を統一するために、_基本_ テンプレートを読み込んでいることがわかります。 82 | 83 | いいところは、_これだけで動作する_ ことです。フォーム送信の処理やパスワードの取り扱い、パスワードの保護は必要ありません。もう少し、やることが残っています。`mysite/settings.py`に設定を追加します: 84 | 85 | ```python 86 | LOGIN_REDIRECT_URL = '/' 87 | ``` 88 | 89 | ログインページに直接アクセスして、ログインが成功したときにトップレベルのインデックス(ブログのホームページ)にリダイレクトします。 90 | 91 | ## レイアウトを改善する 92 | 93 | 投稿を追加したり編集したりするためのボタンを認証されたユーザー(つまり、私たち)のみに見せるようにすでに設定しています。今度は、誰もがアクセスしたときにログインボタンを見せたいですね。 94 | 95 | 次のようにログインボタンを追加してみましょう: 96 | 97 | ```django 98 | 99 | ``` 100 | 101 | テンプレートを編集する必要があるので、`blog/templates/blog/base.html`を開いて、``と``タグの中を次のように変更してください。 102 | 103 | ```django 104 | 105 | 114 |
115 |
116 |
117 | {% block content %} 118 | {% endblock %} 119 |
120 |
121 |
122 | 123 | ``` 124 | 125 | ここで、あるパターンがあることに気づくでしょう。`if`条件式で、認証されたユーザーには追加/編集ボタンが表示されるようにしています。認証されていない場合は、ログインボタンが表示されます。 126 | 127 | ## 認証されたユーザーへの追加項目 128 | 129 | ログイン状態のユーザーに向けて、テンプレートにもう少し付け加えましょう。まず、ログイン時に表示されるものを追加します。`blog/templates/blog/base.html`を次のように修正します: 130 | 131 | ```django 132 | 142 | ``` 143 | 144 | この追加によって、"Hello _<username>_" というふうに、何のユーザーでログインしているか、認証されているかがわかるようになります。また、ブログからログアウトするリンクを追加していますが、お気づきのとおり動いていません。次はそれを修正しましょう! 145 | 146 | ログイン処理をDjangoに頼ったように、ログアウト処理も頼れるかどうか見てみましょう。https://docs.djangoproject.com/en/2.0/topics/auth/default/ をチェックしてみてください。 147 | 148 | 読みました?Djangoのログアウトビュー(例えば`django.contrib.auth.views.logout`)を示すurlを次のように`mysite/urls.py`に追加することを思いついたんではないでしょうか: 149 | 150 | ```python 151 | from django.urls import path, include 152 | from django.contrib import admin 153 | 154 | from django.contrib.auth import views 155 | 156 | urlpatterns = [ 157 | path('admin/', admin.site.urls), 158 | path('accounts/login/', views.LoginView.as_view(), name='login'), 159 | path('accounts/logout/', views.LogoutView.as_view(next_page='/'), name='logout'), 160 | path('', include('blog.urls')), 161 | ] 162 | ``` 163 | 164 | 以上です!これらのことをやり終えたら(そして宿題をやったら)、あなたのブログは次のようになっています。 165 | 166 | - ログイン時にはユーザー名とパスワードが必須である 167 | - 投稿を追加、編集、公開、削除時にはログインが必須である 168 | - そして、ログアウトできる 169 | -------------------------------------------------------------------------------- /ko/authentication_authorization/README.md: -------------------------------------------------------------------------------- 1 | # 숙제: 안전한 웹사이트 만들기 2 | 3 | 관리자 화면을 사용하는 경우를 제외하고는 미리 설정한 비밀번호를 그동안 사용하지 않았어요. 그래서 내 블로그에 누구든지 마음대로 글을 작성하거나 수정할 수 있다는 것을 눈치챘을 거예요. '네가 누군지 모르겠지만, 내 블로그에 글 쓰는 것을 원치 않아!'라는 생각이 들 거예요. 그래서 아무나 들어와서 글을 쓰지 못하도록 뭔가 만들 수 있답니다. 4 | 5 | ## 블로그 글 작성/수정 편집 허가하기 6 | 7 | 첫 번째로 보안을 위해 무언가를 할 거예요. 우리는 로그인 한 사용자만 글에 접근할 수 있도록 `post_new`, `post_edit`, `post_draft_list`, `post_remove` 그리고 `post_publish`의 뷰를 보호할 거예요. Django는 _decorators_ 를 사용해 도움을 주고 있어요. decorators는 고급 주제이긴 해요. 지금 기술적인 것에 걱정하지 마세요. 여러분은 나중에 이해하게 될 거예요. Django에서 제공하는 `django.contrib.auth.decorators` 모듈 안의 decorator를 사용하는데, 이것을 `login_required`라고 해요. 8 | 9 | 그다음, `blog/views.py` 을 수정하고 import를 하는 영역에 아래와 같이 한 줄을 추가해 볼 거예요. 10 | 11 | ```python 12 | from django.contrib.auth.decorators import login_required 13 | ``` 14 | 15 | 그리고 각각의 `post_new`, `post_edit`, `post_draft_list`, `post_remove` 그리고 `post_publish` 뷰의 앞에 아래와 같이 `@login_required`라는 한 줄을 추가해 주세요. 16 | 17 | ```python 18 | @login_required 19 | def post_new(request): 20 | [...] 21 | ``` 22 | 잘했어요! 이제 `http://localhost:8000/post/new/` 로 접속해보세요. 다른 점을 찾으셨나요? 23 | 24 | > 만약 비어있는 폼 페이지만 보인다면, 이미 관리자 화면을 통해 로그인한 상태일 거예요. `http://localhost:8000/admin/logout/` 로 로그아웃을 한 다음, `http://localhost:8000/post/new` 로 다시 접속해보세요. 25 | 26 | 사랑스러운 에러 중 하나를 보게 될 거예요. 정말 흥미로운 사실 중 하나: 추가한 Decorator는 로그인 페이지로 우리를 돌려보낼 거예요. 아직 사용할 수 없지만, "페이지를 찾을 수 없습니다. (404)"라는 에러를 나타내죠. 27 | 28 | `post_edit`, `post_remove`, `post_draft_list`, `post_publish` 위에도 decorator를 추가하는 것을 잊지 마세요. 29 | 30 | 야호, 목표지점까지 다 왔어요! 이제 다른 사람들이 내 블로그에 마음대로 글을 쓸 수 없답니다. 그런데 관리자인 나도 새 글을 작성할 수가 없네요. 그럼 작성할 수 있게 수정해 봅시다. 31 | 32 | 33 | ## 사용자 로그인 34 | 35 | 이제 우리는 사용자와 패스워드 그리고 인증을 구현할 수 있는 많은 종류의 마법의 도구를 사용할 수 있었어요. 그러나 모든 도구를 정확히 사용하는 것은 꽤 복잡하지요. 장고에서 제공되는 인증 도구를 좀 더 사용할 수 있게 만들 거예요. 36 | 37 | `mysite/urls.py` 에서 `url(r'^accounts/login/$', views.login, name='login')`. 부분에 url을 추가해요. 아래처럼 말이죠. 38 | 39 | ```python 40 | from django.conf.urls import include, url 41 | from django.contrib import admin 42 | 43 | from django.contrib.auth import views 44 | 45 | urlpatterns = [ 46 | url(r'^admin/', admin.site.urls), 47 | url(r'^accounts/login/$', views.login, name='login'), 48 | url(r'', include('blog.urls')), 49 | ] 50 | ``` 51 | 52 | 다음으로는 로그인 페이지 템플릿이 필요합니다. `blog/templates/registration` 디렉터리를 생성하고, `login.html` 파일을 그 디렉터리 안에 넣으세요. 53 | 54 | ```django 55 | {% extends "blog/base.html" %} 56 | 57 | {% block content %} 58 | {% if form.errors %} 59 |

이름과 비밀번호가 일치하지 않습니다. 다시 시도해주세요.

60 | {% endif %} 61 | 62 |
63 | {% csrf_token %} 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
{{ form.username.label_tag }}{{ form.username }}
{{ form.password.label_tag }}{{ form.password }}
74 | 75 | 76 | 77 |
78 | {% endblock %} 79 | ``` 80 | 81 | 전반적인 블로그의 모양과 느낌을 _기본_ 템플릿을 적용해 나타낼 수 있어요. 82 | 83 | 여기서 좋은 점은 _just worksTM_ 라는 거예요. 폼을 제출하거나 패스워드와 보완을 처리할 필요가 없어요. 단지 한 가지 남았다면, 우리는 `mysite/settings.py`에 설정만 추가하면 됩니다. 84 | 85 | ```python 86 | LOGIN_REDIRECT_URL = '/' 87 | ``` 88 | 89 | 이제 바로 직접 로그인하면, 최상 위 index 레벨에서 로그인이 성공적으로 될 거에요. 90 | 91 | ## 레이아웃 개선하기 92 | 93 | 지금까지는 인증된 자만 글을 추가/수정/발행하는 버튼을 볼 수 있도록 했어요. 이제는 모두에게 로그인 버튼을 보이게 할 거에요. 94 | 95 | 아래에 보이는 코드처럼 로그인 버튼을 추가해 볼 거에요. 96 | 97 | ```django 98 | 99 | ``` 100 | 101 | 이렇게 버튼을 추가하기 위해서는 템플릿을 수정해 줘야 해요. `blog/templates/blog/base.html` 파일을 열어 `` 태그를 아래처럼 보이게 변경해 보세요.: 102 | 103 | ```django 104 | 105 | 114 |
115 |
116 |
117 | {% block content %} 118 | {% endblock %} 119 |
120 |
121 |
122 | 123 | ``` 124 | 125 | 어떤 패턴이 있음을 눈치채셨나요? 템플릿 내에서 if 문을 통해 인증 여부를 점검하고 있습니다. 인증이 되었을 시에는 추가/수정 버튼을 노출하고, 인증이 되지 않았을 시에는 로그인 버튼을 노출했습니다. 126 | 127 | ## 인증된 사용자를 위한 기능 추가하기 128 | 129 | 템플릿에 달콤한 설탕을 추가해볼게요. 먼저 현재 우리가 로그인 중이라는 표시를 나타내 주는 몇 가지 도구들을 추가할 것입니다. `blog/templates/blog/base.html` 를 아래와 같이 수정해봅시다. 130 | 131 | ```django 132 | 142 | ``` 143 | 144 | " _<이름>_님 안녕하세요."라는 멋진 구문을 추가함으로써 인증된 사용자라는 것을 알려줍니다. 또 블로그에서 로그아웃하는 링크를 추가해봅시다. 하지만 아직 작동하지 않네요. 이런, 우리가 인터넷을 망가뜨렸나 봐요. 자, 고쳐봅시다! 145 | 146 | 장고가 로그인의 처리를 하게 했듯이, 장고가 로그아웃까지 되는지 살펴봅시다. https://docs.djangoproject.com/en/1.10/topics/auth/default/ 에서 관련 내용을 찾아봅시다. 147 | 148 | 다 살펴보았나요? `mysite/urls.py` 파일 안에 `django.contrib.auth.views.logout` 149 | 뷰를 가리키는 URL을 추가해야 한다는 것을 알아챘을 거예요. 이렇게 말이에요. 150 | 151 | ```python 152 | from django.conf.urls import include, url 153 | from django.contrib import admin 154 | 155 | from django.contrib.auth import views 156 | 157 | urlpatterns = [ 158 | url(r'^admin/', admin.site.urls), 159 | url(r'^accounts/login/$', views.login, name='login'), 160 | url(r'^accounts/logout/$', views.logout, name='logout', kwargs={'next_page': '/'}), 161 | url(r'', include('blog.urls')), 162 | ] 163 | ``` 164 | 165 | 끝났어요! 여기까지 잘 따라왔다면(그리고 숙제를 했다면), 아래 기능을 갖춘 멋진 블로그가 완성되었을 거예요. 166 | 167 | - 로그인을 위해 사용자 이름과 패스워드를 넣을 수 있습니다. 168 | - 글 생성/수정/게시(/삭제) 하기 위해 로그인을 할 수 있습니다. 169 | - 다시 로그아웃을 할 수 있습니다. -------------------------------------------------------------------------------- /ja/heroku/README.md: -------------------------------------------------------------------------------- 1 | # Herokuにデプロイしよう(PythonAnywhereだけでなく) 2 | 3 | 開発者にとって、いくつかのデプロイ先を経験しておくことは得策です。ぜひ、PythonAnywhereだけでなくHerokuにもサイトをデプロイしてみませんか? 4 | 5 | [Heroku](http://heroku.com/) は、アクセスがそんなに多くない小さなアプリケーションでは無料ですが、デプロイするのは少し難しいです。 6 | 7 | こちらのチュートリアルを参考にしてください:https://devcenter.heroku.com/articles/getting-started-with-django 8 | ですがここでは、より簡単に説明したいと思います。 9 | 10 | 11 | ## `requirements.txt`ファイル 12 | 13 | もし作成していなかったら、`requirements.txt`ファイルを作成しましょう。サーバーに何のパッケージがインストールされている必要があるかをHerokuに教えるためのファイルです。 14 | 15 | ですがまず、Herokuにデプロイするためにいくつかの新しいパッケージが必要です。`virtualenv`が有効になっているコンソール画面にいって、次のようにタイプします: 16 | 17 | (myvenv) $ pip install dj-database-url gunicorn whitenoise 18 | 19 | インストールが終わったら、`djangogirls`のディレクトリ下で次のコマンドをたたきます: 20 | 21 | (myvenv) $ pip freeze > requirements.txt 22 | 23 | このコマンドで、インストールされているパッケージ(例えばDjangoなど、あなたが使用しているPythonライブラリ)のリストが書かれた`requirements.txt`というファイルが作成されます。 24 | 25 | > __注__:`pip freeze`コマンドは、virtualenv内にインストールされているすべてのPythonライブラリのリストを出力します。`>`をつけると、`pip freeze`の出力をファイルに書き出します。試しに、`> requirements.txt`なしで`pip freeze`コマンドだけたたいて何が起こるか見てみてください! 26 | 27 | `requirements.txt`ファイルを開いて、次の行を一番下に追加しましょう: 28 | 29 | psycopg2==2.7.6.1 30 | 31 | 上記の行は、Heroku上であなたのアプリケーションを動かすのに必要です。 32 | 33 | 34 | ## Procfile 35 | 36 | また、ProcfileというものがHerokuには必要です。これは、ウェブサイトを起動するために実行するコマンドをHerokuに伝えます。コードエディタで`djangogirls`ディレクトリ下に`Procfile`というファイルを作成し、次の行を追加してください: 37 | 38 | web: gunicorn mysite.wsgi --log-file - 39 | 40 | この行は、`web`というアプリケーションをデプロイすることを意味し、そのために`gunicorn mysite.wsgi`コマンドを走らせようとしています(`gunicorn`とは、Djangoの`runserver`コマンドのより強力なバージョンみたいなものです)。 41 | 42 | 保存して完了! 43 | 44 | ## `runtime.txt`ファイル 45 | 46 | また、どのバージョンのPythonを使いたいのかをHerokuに知らせる必要があります。コードエディタで`djangogirls`ディレクトリ下に`runtime.txt`というファイルを作成して、次のテキストを書き込んでください(これ以外には何もありません!): 47 | 48 | python-3.6.4 49 | 50 | ## `mysite/local_settings.py` 51 | 52 | PythonAnywhereよりも制限が厳しいため、Herokuでは私たちがローカルで使用しているもの(コンピュータ上のもの)とは異なる設定を使います。例えば、HerokuではSQLiteではなく、Postgresを使います。そのため、ローカル環境でしか利用できない設定用に、別々のファイルを作成することにします。 53 | 54 | それでは、`mysite/local_settings.py`ファイルを作成しましょう。この中には`mysite/settings.py`ファイル内の`DATABASE`設定が含まれます。次のように: 55 | 56 | ```python 57 | import os 58 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 59 | 60 | DATABASES = { 61 | 'default': { 62 | 'ENGINE': 'django.db.backends.sqlite3', 63 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 64 | } 65 | } 66 | 67 | DEBUG = True 68 | ``` 69 | 70 | では保存しましょう! :) 71 | 72 | ## mysite/settings.py 73 | 74 | あと一つ必要なのは、 `settings.py` ファイルの修正です。エディタで `mysite/settings.py` を開いて、次のように変更/追加します: 75 | 76 | ```python 77 | import dj_database_url 78 | 79 | ... 80 | 81 | DEBUG = False 82 | 83 | ALLOWED_HOSTS = ['127.0.0.1', '.herokuapp.com'] 84 | 85 | ... 86 | 87 | DATABASES = { 88 | 'default': { 89 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 90 | 'NAME': 'djangogirls', 91 | 'USER': 'name', 92 | 'PASSWORD': '', 93 | 'HOST': 'localhost', 94 | 'PORT': '', 95 | } 96 | } 97 | 98 | ... 99 | 100 | db_from_env = dj_database_url.config(conn_max_age=500) 101 | DATABASES['default'].update(db_from_env) 102 | ``` 103 | Herokuに必要な設定になります。 104 | 105 | ファイルを保存しましょう。 106 | 107 | ## mysite/wsgi.py 108 | 109 | `mysite/wsgi.py` ファイルを開いて、最終行に次の行を追加します: 110 | 111 | ```python 112 | from whitenoise.django import DjangoWhiteNoise 113 | application = DjangoWhiteNoise(application) 114 | ``` 115 | 116 | できた! 117 | 118 | ## Herokuアカウント 119 | 120 | Heroku CLI(旧Heroku toolbelt)をインストールする必要があります(セットアップ中に既にインストールしている場合は、このステップをスキップしましょう)。: https://devcenter.heroku.com/articles/heroku-cli 121 | 122 | > コマンドプロンプトを再起動した後は、 `djangogirls` のフォルダに戻って仮想環境を有効にするのを忘れないでください!([「Djangoのインストール」の章をチェック](https://tutorial.djangogirls.org/ja/django_installation/#%E4%BB%AE%E6%83%B3%E7%92%B0%E5%A2%83%E3%81%AE%E6%93%8D%E4%BD%9C)) 123 | 124 | こちらでHerokuのフリーアカウントを作成しましょう:https://signup.heroku.com/www-home-top 125 | 126 | 次にこのコマンドを実行して、あなたのコンピュータでHerokuアカウントを認証します: 127 | 128 | $ heroku login 129 | 130 | SSHキーがない場合は、このコマンドによって自動的に作成されます。 SSHキーは、Herokuにソースコードをプッシュするために必要です。 131 | 132 | ## Gitコミット 133 | 134 | Herokuはデプロイするのにgitを使います。PythonAnywhereとは違って、Githubを経由せずに直接Herokuにプッシュできます。しかし、最初にいくつか微調整する必要があります。 135 | 136 | `djangogirls` ディレクトリの中の `.gitignore` ファイルを開いて、そこに `local_settings.py` を追加してください。gitに `local_settings` を無視させることで、それは私たちのローカルコンピュータにとどまり、Herokuにアップされません。 137 | 138 | *.pyc 139 | db.sqlite3 140 | myvenv 141 | __pycache__ 142 | local_settings.py 143 | 144 | そして変更をコミットします。 145 | 146 | $ git status 147 | $ git add -A . 148 | $ git commit -m "additional files and changes for Heroku" 149 | 150 | 151 | ## アプリケーション名をつける 152 | 153 | あなたのブログを `[あなたのブログの名前].herokuapp.com` で公開しますので、他の誰も取得していない名前を選ぶ必要があります。この名前はDjangoの `blog` アプリや `mysite` 、あるいはこれまでに作成したものに関連している必要はありません。名前は何でもかまいませんが、Herokuでは使える文字の種類が非常に限られています。使用可能なのは、英数小文字とダッシュ( `-` )だけです(大文字やアクセント文字は使えません)。 154 | 155 | 名前を考えたら(あなたの名前やニックネームが含まれているなど)、このコマンドを実行します。 `djangogirlsblog` のところを自分のアプリケーション名に置き換えてください: 156 | 157 | $ heroku create djangogirlsblog 158 | 159 | > __注__: `djangogirlsblog` の部分を、あなたのHerokuのアプリケーション名に置き換えるのを忘れないでください。 160 | 161 | 名前が思いつかなかったら、代わりに次のように実行することができます。 162 | 163 | $ heroku create 164 | 165 | すると、Herokuのほうで自動的に未使用の名前をつけてくれます(おそらく `enigmatic-cove-2527` のようなもの)。 166 | 167 | Herokuアプリケーションの名前を変更したい場合は、いつでもこのコマンドで変更できます( `the-new-name` を使用したい新しい名前に置き換えてください): 168 | 169 | $ heroku apps:rename the-new-name 170 | 171 | > __注__: アプリケーション名を変更した後は、 `[the-new-name].herokuapp.com` にアクセスして確認してください。 172 | 173 | ## Herokuにデプロイ! 174 | 175 | たくさんの設定やインストールをしてきましたが、大丈夫?でも、それらは一度だけでいいんです。もうデプロイできますよ! 176 | 177 | `heroku create` コマンドを実行したときに、あなたのリポジトリにHerokuのリモートサーバーが自動的に追加されています。次は、あなたのアプリケーションをデプロイするのに、単にgitでプッシュするだけです: 178 | 179 | $ git push heroku master 180 | 181 | > __注__: Herokuがpsycopgをコンパイルしてインストールするので、これを最初に実行したときはおそらく*大量*のログが出力されます。ログの最後のほうで、 `https://yourapplicationname.herokuapp.com/ deployed to Heroku` のような記述を見つけたら、デプロイに成功したと思っていいでしょう。 182 | 183 | ## アプリケーションにアクセス 184 | 185 | あなたのソースコードをHerokuにデプロイし、そしてプロセスタイプを `Procfile`で指定しました(以前に `web` プロセスタイプを選んでいます)。Herokuにこの `web process` を開始するように指示できます。 186 | 187 | そのためには、次のコマンドを実行します: 188 | 189 | $ heroku ps:scale web=1 190 | 191 | これはHerokuに `web` プロセスのインスタンスを1つだけ実行するように伝えます。私たちのブログアプリケーションは非常に単純なので、あまりパワーを必要とせず、1つのプロセスだけを実行しても問題ありません。Herokuにもっと多くのプロセスを実行するようにすることも可能です(ところで、Herokuはこれらのプロセスを "Dynos"と呼んでいますので、この用語を見ても驚かないでください)。ですが、それはフリープランではできません。 192 | 193 | これで、 `heroku open` コマンドを使うと、ブラウザでアプリにアクセスできます。 194 | 195 | $ heroku open 196 | 197 | > __注__: エラーページが見えていますね!もう少し説明を加えます。 198 | 199 | ブラウザに [https://djangogirlsblog.herokuapp.com/]() のようなURLが表示され、現時点ではおそらくエラーページが表示されます。 200 | 201 | このエラーは、Herokuにデプロイしたときにデータベースが新規作成され、それが空であるために起こっています。PythonAnywhereにデプロイしたときと同じように、 `migrate` コマンドと `createsuperuser` コマンドを実行する必要があります。ここでは、あなたのコンピュータ上で特別なコマンドラインが用意されています。 `heroku run` です: 202 | 203 | $ heroku run python manage.py migrate 204 | 205 | $ heroku run python manage.py createsuperuser 206 | 207 | コマンドプロンプトから、ユーザー名とパスワードをもう一度入力するように求められます。これらはリモートサーバー上のウェブサイトの管理者ページのログイン情報になります。 208 | 209 | ブラウザで更新してください。これで、2つの異なるホスティングプラットフォームにデプロイする方法がわかりました。お気に入りを選んでくださいね :) 210 | -------------------------------------------------------------------------------- /ko/heroku/README.md: -------------------------------------------------------------------------------- 1 | # 헤로쿠(Heroku) 배포하기 2 | 3 | 항상 서로 다른 두 가지 배포 옵션을 사용하는 것이 좋아요. 헤로쿠(Heroku)에 사이트를 배포해 볼까요? 4 | 5 | [헤로쿠](http://heroku.com/) 역시 방문자 수가 적은 소규모 애플리케이션에는 무료에요. 하지만 배포과정이 다소 까다로워요. 6 | 7 | 우리는 이 [튜토리얼](https://devcenter.heroku.com/articles/getting-started-with-django)을 따라 해볼 거에요. https://devcenter.heroku.com/articles/getting-started-with-django 다소 복잡해 보일지 몰라도 붙여넣기를 사용해서 쉽게 해볼 거에요. 8 | 9 | 10 | ## 우선 `requirements.txt` 파일이 필요해요. 11 | 12 | `requirements.txt` 파일을 만들지 않았다면 이번에 만들어볼 거에요. 이 파일은 우리 서버에 어떤 파이썬 패키지들을 설치해야 하는지 알려 줍니다. 13 | 14 | 하지만 첫째로, 헤로쿠를 사용하려면 몇 가지 새로운 패키지를 설치 해야 해요. `virtualenv`를 활성화시킨 상태에서 콘솔로 가서 다음과 같이 입력해 보세요.: 15 | 16 | (myvenv) $ pip install dj-database-url gunicorn whitenoise 17 | 18 | 설치가 끝났다면 `djangogirls` 디렉토리로 가서 다음 명령어를 실행해 보세요.: 19 | 20 | (myvenv) $ pip freeze > requirements.txt 21 | 22 | 위 명령어를 실행했다면 `requirements.txt`라는 파일이 생성되었을 거예요. 이 파일 안에는 지금까지 사용했던 패키지들의 리스트가 들어있어요(Django를 비롯한 파이썬 라이브러리가 있을 거예요.). 23 | 24 | > __Note__: `pip freeze` 는 가상환경(virtualenv)에 설치된 파이썬 라이브러리 목록을 출력해줘요. 그리고 `>` 는 `pip freeze` 의 실행결과를 파일에 담아주는 역할을 해요. 어떤일이 일어나는지 `> requirements.txt` 없이 `pip freeze` 를 실행해 보세요. 25 | 26 | 이 파일을 열고 맨 마지막 줄에 다음 내용을 추가해요.: 27 | 28 | psycopg2==2.6.2 29 | 30 | 이 줄은 헤로쿠에서 우리가 만든 애플리케이션이 실행될 수 있게 해줄 거예요. 31 | 32 | ## Procfile 33 | 34 | 헤로쿠는 Procfile 도 필요해요. 이것은 헤로쿠에게 우리의 웹사이트를 시작시키기 위해 실행되어야 할 명령어의 순서를 알려줘요. 코드 편집기를 열고 `djangogirls` 디렉토리 로 가서 `Procfile`이라 불리는 파일을 생성하고 다음 줄을 추가해 보세요.: 35 | 36 | web: gunicorn mysite.wsgi --log-file - 37 | 38 | 이 줄은 우리가 `web` 애플리케이션을 배포할 때 `gunicorn mysite.wsgi`(`gunicorn`은 더 강력한 버전의 `runserver` 명령어에요.) 명령을 실행하는 것을 의미해요. 39 | 40 | 그리고 저장해요. 자 됐어요! 41 | 42 | ## `runtime.txt` 파일 43 | 44 | 우리는 헤로쿠에 어떤 버전의 파이썬을 사용하고 있는지 알려줘야 해요. `djangogirls` 폴더에서 여러분이 사용하고 있는 에디터의 "새로운 파일 만들기" 기능을 사용해서 `runtime.txt`파일을 만들어 주세요. 그리고 파일에 다음 한 줄을 추가해 주세요. 이게 다예요.: 45 | 46 | python-3.5.2 47 | 48 | ## `mysite/local_settings.py` 49 | 50 | 헤로쿠는 PythonAnywhere 보다 제한적이라 로컬 컴퓨터(지금 사용하고 있는 컴퓨터)와 다른 설정을 해야 해요. 51 | 우리의 예제는 SQLite로 되어있지만 헤로쿠는 Postgres 를 사용하려고 할거에요. 그래서 로컬 환경에서만 필요한 별도의 설정 파일을 만들어야 해요. 52 | 53 | 그럼 바로 `mysite/local_settings.py` 파일을 만들어 봐요. 이 파일에서 `DATABASE` 설정을 만들어 줄 텐데요. `mysite/settings.py`에서 했던 것처럼 만들어 주세요.: 54 | 55 | ```python 56 | import os 57 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 58 | 59 | DATABASES = { 60 | 'default': { 61 | 'ENGINE': 'django.db.backends.sqlite3', 62 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 63 | } 64 | } 65 | 66 | DEBUG = True 67 | ``` 68 | 69 | 자, 그리고 저장해요! :) 70 | 71 | ## mysite/settings.py 72 | 73 | 그리고 다음으로 해줄 것은 우리의 웹 사이트 `settings.py` 파일을 수정해 줄 거에요. 에디터를 열고 `mysite/settings.py` 파일을 불러와서 다음 줄을 변경/추가해주세요.: 74 | 75 | ```python 76 | import dj_database_url 77 | 78 | ... 79 | 80 | DEBUG = False 81 | 82 | ALLOWED_HOSTS = ['127.0.0.1', '.herokuapp.com'] 83 | 84 | ... 85 | 86 | DATABASES = { 87 | 'default': { 88 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 89 | 'NAME': 'djangogirls', 90 | 'USER': 'name', 91 | 'PASSWORD': '', 92 | 'HOST': 'localhost', 93 | 'PORT': '', 94 | } 95 | } 96 | 97 | ... 98 | 99 | db_from_env = dj_database_url.config(conn_max_age=500) 100 | DATABASES['default'].update(db_from_env) 101 | ``` 102 | 103 | 이 코드는 헤로쿠에 필요한 설정을 구성할거예요. 104 | 105 | 이제 저장해 주세요. 106 | 107 | ## mysite/wsgi.py 108 | 109 | 110 | `mysite/wsgi.py` 파일을 열고 파일의 끝에 다음 라인을 추가해줘요.: 111 | 112 | ```python 113 | from whitenoise.django import DjangoWhiteNoise 114 | application = DjangoWhiteNoise(application) 115 | ``` 116 | 117 | 좋아요! 118 | 119 | ## 헤로쿠 계정 120 | 121 | 헤로쿠 toolbelt 를 설치해줘야 하는데요. 여기에서 찾을 수 있어요(이미 설치했다면 다음으로 넘어갈게요.): https://toolbelt.heroku.com/ 122 | 123 | > 윈도우에서 헤로쿠 toolbelt 설치 프로그램을 실행할 때 설치할 구성 요소를 묻는 메시지가 나타나면 "사용자 지정 설치"를 선택해 주세요. 그리고 "Git and SSH" 앞에 있는 체크박스를 선택해 주세요. 124 | 125 | > 윈도우에서 명령 프롬프트를 열고 Git과 SSH를 사용하기 위해 다음의 명령어를 실행해 주세요. `PATH`: `setx PATH "%PATH%;C:\Program Files\Git\bin"` 변경된 내용을 확인해 보기 위해 명령 프롬프트를 재시작해주세요. 126 | 127 | > 명령 프롬프트를 재시작 하고 난 뒤에는 `djangogirls` 폴더로 돌아가서 가상환경을 활성화하는 것을 잊지 마세요! (Hint: [Check the Django installation chapter](http://tutorial.djangogirls.org/en/django_installation/index.html#working-with-virtualenv)) 128 | 129 | 여기에서 헤로쿠 무료 계정을 만들고 오세요: https://id.heroku.com/signup/www-home-top 130 | 131 | 다음 명령어로 헤로쿠 계정을 인증받을 수 있어요: 132 | 133 | $ heroku login 134 | 135 | 이 명령은 SSH 키가 없을 때 자동으로 SSH 키를 만들어요. SSH 는 헤로쿠에 코드를 푸시하기 위해 필요해요. 136 | 137 | ## Git 커밋 138 | 139 | 헤로쿠는 배포를 위해 git을 사용해요. PythonAnywhere 와는 달리 Github를 거치지 않고 헤로쿠에 바로 올릴 수 있어요. 그 전에 두 가지를 미리 조정해야해요. 140 | 141 | `djangogirls` 폴더로 가서 `.gitignore` 파일을 열어요. 그리고 `local_settings.py` 를 마지막 줄에 추가해요. git이 `local_settings` 파일을 무시하고 헤로쿠에 올라가지 않고 로컬 컴퓨터에 남아있길 원해서예요. 142 | 143 | *.pyc 144 | db.sqlite3 145 | myvenv 146 | __pycache__ 147 | local_settings.py 148 | 149 | 150 | 그리고 변경사항을 git으로 커밋해봐요. 151 | 152 | $ git status 153 | $ git add -A . 154 | $ git commit -m "additional files and changes for Heroku"애플리케이션 155 | 156 | 157 | ## 애플리케이션 이름을 정해요. 158 | 159 | 웹상에서 `[내가 만든 블로그 이름].herokuapp.com` 주소를 통해 직접 만든 블로그에 들어갈 수 있게 될 거에요. 그래서 누구도 사용하지 않는 이름으로 만들어야 해요. 이 이름은 꼭 장고 `blog` 앱 이나 `mysite` 일 필요는 없어요. 이름은 원하는 대로 지을 수는 있지만 헤로쿠는 사용할 수 있는 문자에 대해 조금 제한적이에요. 소문자, 숫자, 대시(`-`)만 사용할 수 있어요. 160 | 161 | 이름(아마도 이름이나 별명)을 생각해두고 다음 명령어를 실행해 줘요. `djangogirlsblog` 로 되어 있는 이름을 나만의 이름으로 바꿔주세요.: 162 | 163 | $ heroku create djangogirlsblog 164 | 165 | > __Note__: `djangogirlsblog` 를 헤로쿠에 있는 나만의 애플리케이션 이름으로 바꾸는 것을 잊지마세요. 166 | 167 | 이름을 짓는 건 정말 어려운 일이에요. 딱히 떠오르는 이름이 없다면 다음과 같이 실행해 보세요. 168 | 169 | $ heroku create 170 | 171 | 헤로쿠가 우리를 위해 아무도 사용하고 있지 않은 이름을 골라줄 거예요(아마도 `enigmatic-cove-2527` 이런 형태의 이름으로). 172 | 173 | 헤로쿠에서 만든 이름을 변경하고 싶다면 언제든지 아래처럼 명령어를 실행해서 바꿔줄 수 있을 거예요. (`the-new-name` 을 변경할 이름으로 바꿔주세요.) 174 | 175 | $ heroku apps:rename the-new-name 176 | 177 | > __Note__: 애플리케이션 명을 변경하면 꼭 `[the-new-name].herokuapp.com` 와 같이 변경된 이름으로 사이트를 방문해서 확인해 보세요. 178 | 179 | ## 헤로쿠에 배포하기! 180 | 181 | 설정과 설치 과정이 조금 많았죠? 하지만 이 일은 한 번만 해주면 돼요! 이제 배포를 할 수 있어요! 182 | 183 | `heroku create`을 실행했을 때 헤로쿠의 remote를 자동으로 설정했어요. git push로 우리의 애플리케이션을 배포해 보세요. 184 | 185 | $ git push heroku master 186 | 187 | > __Note__: 헤로쿠는 psycopg 를 컴파일하고 설치하기 때문에 처음 실행 될 때는 아마도 *많은* 출력이 있을 거예요. 이 출력 끝에서 `https://yourapplicationname.herokuapp.com/ deployed to Heroku` 이런 출력을 보았다면 아마도 배포가 성공했을 거예요. 188 | 189 | ## 애플리케이션에 접속해 보세요. 190 | 191 | 헤로쿠에 코드를 배포하고 `Procfile`로 프로세스 유형을 지정했어요(`web` 프로세스로요). 192 | 이제 헤로쿠에 `web process` 시작하라고 얘기해 줄 수 있어요. 193 | 194 | 이 명령어를 실행하려면 다음과 같이 해보세요. 195 | 196 | $ heroku ps:scale web=1 197 | 198 | 이건 헤로쿠에게 `web` 프로세스의 인스턴스 하나만 실행하도록 해요. 우리의 블로그 애플리케이션은 꽤 단순하기 때문에 많은 전력을 필요하지 않아요. 그래서 하나의 프로세스만으로도 충분할 거예요. 헤로쿠에게 더 많은 프로세스를 실행하도록 요청할 수도 있어요(단, 헤로쿠는 이 프로세스를 "Dynos"라고 불러요. 이 단어를 보게 된다면 놀라지 마세요.) 하지만 여러 프로세스를 실행하게 된다면 더 이상 무료가 아니에요. 199 | 200 | 브라우저에서 `heroku open`을 통해 앱에 들어갈 수 있어요. 201 | 202 | $ heroku open 203 | 204 | > __Note__: 아마도 오류 페이지가 보일 거예요! 1분만 이것에 대해 얘기해 볼게요. 205 | 206 | 브라우저에서 우리 블로그 애플리케이션 주소인 https://djangogirlsblog.herokuapp.com/에 접속하면 아마도 오류 페이지가 나올 거예요. 207 | 208 | 이 오류는 헤로쿠에 배포를 할 때 비어있는 새로운 데이터베이스를 만들어주었기 때문이에요. PythonAnywhere에서 했던 것처럼 `migrate` 와 `createsuperuser` 명령어를 실행해주세요. 이번에는 헤로쿠를 위한 특별한 명령어를 실행할게요. `heroku run` 이렇게요. 209 | 210 | $ heroku run python manage.py migrate 211 | 212 | $ heroku run python manage.py createsuperuser 213 | 214 | 이 명령 프롬프트는 사용자명과 비밀번호를 다시 물어볼 거예요. 웹사이트의 어드민 페이지에서 사용할 로그인 정보를 입력해주세요. 215 | 216 | 브라우저를 새로고침 하고 다시 가보세요! 이제 다른 두 플랫폼에 어떻게 배포하는지 알게 되었어요. 마음에 드는 플랫폼을 선택하세요. :) -------------------------------------------------------------------------------- /en/optional_postgresql_installation/README.md: -------------------------------------------------------------------------------- 1 | # PostgreSQL installation 2 | 3 | > Part of this chapter is based on tutorials by Geek Girls Carrots (http://django.carrots.pl/). 4 | 5 | > Parts of this chapter is based on the [django-marcador 6 | tutorial](http://django-marcador.keimlink.de/) licensed under Creative Commons 7 | Attribution-ShareAlike 4.0 International License. The django-marcador tutorial 8 | is copyrighted by Markus Zapke-Gründemann et al. 9 | 10 | 11 | ## Windows 12 | 13 | The easiest way to install Postgres on Windows is using a program you can find here: http://www.enterprisedb.com/products-services-training/pgdownload#windows 14 | 15 | Choose the newest version available for your operating system. Download the installer, run it and then follow the instructions available here: http://www.postgresqltutorial.com/install-postgresql/. Take note of the installation directory as you will need it in the next step (typically, it's `C:\Program Files\PostgreSQL\9.3`). 16 | 17 | ## Mac OS X 18 | 19 | The easiest way is to download the free [Postgres.app](http://postgresapp.com/) and install it like any other application on your operating system. 20 | 21 | Download it, drag to the Applications directory and run by double clicking. That's it! 22 | 23 | You'll also have to add the Postgres command line tools to your `PATH` variable, what is described [here](http://postgresapp.com/documentation/cli-tools.html). 24 | 25 | ## Linux 26 | 27 | Installation steps vary from distribution to distribution. Below are the commands for Ubuntu and Fedora, but if you're using a different distro [take a look at the PostgreSQL documentation](https://wiki.postgresql.org/wiki/Detailed_installation_guides#General_Linux). 28 | 29 | ### Ubuntu 30 | 31 | Run the following command: 32 | 33 | sudo apt-get install postgresql postgresql-contrib 34 | 35 | ### Fedora 36 | 37 | Run the following command: 38 | 39 | sudo yum install postgresql93-server 40 | 41 | # Create database 42 | 43 | Next up, we need to create our first database, and a user that can access that database. PostgreSQL lets you create as many databases and users as you like, so if you're running more than one site you should create a database for each one. 44 | 45 | ## Windows 46 | 47 | If you're using Windows, there's a couple more steps we need to complete. For now it's not important for you to understand the configuration we're doing here, but feel free to ask your coach if you're curious as to what's going on. 48 | 49 | 1. Open the Command Prompt (Start menu → All Programs → Accessories → Command Prompt) 50 | 2. Run the following by typing it in and hitting return: `setx PATH "%PATH%;C:\Program Files\PostgreSQL\9.3\bin"`. You can paste things into the Command Prompt by right clicking and selecting `Paste`. Make sure that the path is the same one you noted during installation with `\bin` added at the end. You should see the message `SUCCESS: Specified value was saved.`. 51 | 3. Close and then reopen the Command Prompt. 52 | 53 | ## Create the database 54 | 55 | First, let's launch the Postgres console by running `psql`. Remember how to launch the console? 56 | >On Mac OS X you can do this by launching the `Terminal` application (it's in Applications → Utilities). On Linux, it's probably under Applications → Accessories → Terminal. On Windows you need to go to Start menu → All Programs → Accessories → Command Prompt. Furthermore, on Windows, `psql` might require logging in using the username and password you chose during installation. If `psql` is asking you for a password and doesn't seem to work, try `psql -U -W` first and enter the password later. 57 | 58 | $ psql 59 | psql (9.3.4) 60 | Type "help" for help. 61 | # 62 | 63 | Our `$` now changed into `#`, which means that we're now sending commands to PostgreSQL. Let's create a user with `CREATE USER name;` (remember to use the semicolon): 64 | 65 | # CREATE USER name; 66 | 67 | Replace `name` with your own name. You shouldn't use accented letters or whitespace (e.g. `bożena maria` is invalid - you need to convert it into `bozena_maria`). If it goes well, you should get `CREATE ROLE` response from the console. 68 | 69 | Now it's time to create a database for your Django project: 70 | 71 | # CREATE DATABASE djangogirls OWNER name; 72 | 73 | Remember to replace `name` with the name you've chosen (e.g. `bozena_maria`). This creates an empty database that you can now use in your project. If it goes well, you should get `CREATE DATABASE` response from the console. 74 | 75 | Great - that's databases all sorted! 76 | 77 | # Updating settings 78 | 79 | Find this part in your `mysite/settings.py` file: 80 | 81 | ```python 82 | DATABASES = { 83 | 'default': { 84 | 'ENGINE': 'django.db.backends.sqlite3', 85 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 86 | } 87 | } 88 | ``` 89 | 90 | And replace it with this: 91 | 92 | ```python 93 | DATABASES = { 94 | 'default': { 95 | 'ENGINE': 'django.db.backends.postgresql', 96 | 'NAME': 'djangogirls', 97 | 'USER': 'name', 98 | 'PASSWORD': '', 99 | 'HOST': 'localhost', 100 | 'PORT': '', 101 | } 102 | } 103 | ``` 104 | 105 | Remember to change `name` to the user name that you created earlier in this chapter. 106 | 107 | # Installing PostgreSQL package for Python 108 | 109 | First, install Heroku Toolbelt from https://toolbelt.heroku.com/ While we will need this mostly for deploying your site later on, it also includes Git, which might come in handy already. 110 | 111 | Next up, we need to install a package which lets Python talk to PostgreSQL - this is called `psycopg2`. The installation instructions differ slightly between Windows and Linux/OS X. 112 | 113 | ## Windows 114 | 115 | For Windows, download the pre-built file from http://www.stickpeople.com/projects/python/win-psycopg/ 116 | 117 | Make sure you get the one corresponding to your Python version (3.4 should be the last line) and to the correct architecture (32 bit in the left column or 64 bit in the right column). 118 | 119 | Rename the downloaded file and move it so that it's now available at `C:\psycopg2.exe`. 120 | 121 | Once that's done, enter the following command in the terminal (make sure your `virtualenv` is activated): 122 | 123 | easy_install C:\psycopg2.exe 124 | 125 | ## Linux and OS X 126 | 127 | Run the following in your console: 128 | 129 | (myvenv) ~/djangogirls$ pip install psycopg2 130 | 131 | If that goes well, you'll see something like this 132 | 133 | Downloading/unpacking psycopg2 134 | Installing collected packages: psycopg2 135 | Successfully installed psycopg2 136 | Cleaning up... 137 | 138 | --- 139 | 140 | Once that's completed, run `python -c "import psycopg2"`. If you get no errors, everything's installed successfully. 141 | 142 | # Applying migrations and creating a superuser 143 | 144 | In order to use the newly created database in your website project, you need to apply all the migrations. In your virtual environment run the following code: 145 | 146 | (myvenv) ~/djangogirls$ python manage.py migrate 147 | 148 | To add new posts to your blog, you also need to create a superuser by running the code: 149 | 150 | (myvenv) ~/djangogirls$ python manage.py createsuperuser --username name 151 | 152 | Remember to replace `name` with the username. You will be prompted for email and password. 153 | 154 | Now you can run the server, log into your application with the superuser account and start adding posts to your new database. 155 | -------------------------------------------------------------------------------- /es/optional_postgresql_installation/README.md: -------------------------------------------------------------------------------- 1 | # Opcional: Instalación de PostgreSQL 2 | 3 | > Parte de este capítulo está basado en tutoriales de Geek Girls Carrots (http://django.carrots.pl/). 4 | 5 | > Parte de este capítulo está basado en [django-marcador 6 | tutorial](http://django-marcador.keimlink.de/) Licenciado bajo la licencia Creative Commons Attribution-ShareAlike 4.0. La licencia del tutorial le pertenece a Zapke-Gründemann. 7 | 8 | ## Windows 9 | 10 | La manera mas fácil de instalar Postgres en Windows es usando un programa que puedes encontra aquí: http://www.enterprisedb.com/products-services-training/pgdownload#windows 11 | 12 | Escoge la versión mas nueva disponible para tu sistema operativo. Descarga el instalador, ejecútalo y sigue las instrucciones disponibles aquí http://www.postgresqltutorial.com/install-postgresql/. Toma nota del directorio donde se instaló porque lo necesitarás en el siguiente paso (tipicamente, es `C:\Program Files\PostgreSQL\9.3). 13 | 14 | ## Mac OS X 15 | 16 | La manera mas fácil es descargar gratis [Postgres.app](http://postgresapp.com/) e instalalo como cualquier otra aplicación en tu sistema operativo. 17 | 18 | Descargalo, y arrástralo al directorio de Aplicaciones, y ejecutalo dando doble click en el. ¡Eso es todo! 19 | 20 | Ahora debes agregar las herramientas de línea de comandos de Postgres a tu variable `PATH`, que es descrita [aquí](http://postgresapp.com/documentation/cli-tools.html). 21 | 22 | ## Linux 23 | 24 | Los pasos de instalación varían de distribucióna distribución. A continuación encontraremos comandos para Ubuntu y Fedora, pero si estás usando una distribución diferente [mira la documentación de PostgreSQL](https://wiki.postgresql.org/wiki/Detailed_installation_guides#General_Linux). 25 | 26 | ### Ubuntu 27 | 28 | Ejecuta el siguiente comando: 29 | 30 | ``` 31 | sudo apt-get install postgresql postgresql-contrib 32 | ``` 33 | 34 | ### Fedora 35 | 36 | Ejecuta el siguiente comando: 37 | 38 | ``` 39 | sudo yum install postgresql93-server 40 | ``` 41 | 42 | # Crea la base de datos 43 | 44 | Lo siguiente será crear nuestra primera base de datos, y un usuario que pueda acceder a ella. PostgreSQL nos deja crear tantas bases de datos como usuarios queramos, entonces si vamos a ejecutar mas de un sitio, deberíamos crear una base de datos para cada uno. 45 | 46 | ## Windows 47 | 48 | Si estás usando Windows, aquí hay un par de pasos adicionales que necesitas completas. Por ahora no es importante si no entiendes la configuración que estamos haciendo aquí, pero sientente libre de preguntarle a tu Coach si estas curioso de lo que estás haciendo. 49 | 50 | 1. Abre la línea de comandos (Inicio → Todos los programas → Accesorios → Linea de comandos) 51 | 1. Ejecuta la siguiente configuración y presiona enter:`setx PATH "%PATH%;C:\Program Files\PostgreSQL\9.3\bin"`. Puedes pegar cosas a la línea de comandos dando click derecho y seleccionando `Pegar`. Asegúrate de que el la carpeta sea la misma que anotaste cuando estabas instalando con un `\bin` al final. Deberías ver el mensaje `SUCCESS: Specified value was saved.`. 52 | 1. Cierra y vuelve a abrie la línea de comando 53 | 54 | ## Crea la base de datos 55 | 56 | Primero, vamos a iniciar la consola dePostgres ejecutando `psql`. ¿Recuerdas como lanzar la consola? 57 | 58 | > En Mac OS X debes hacer esto lanzando la aplicación `Terminal` (está en Aplicaciones → Utilidades). En linux, posiblemente esté bajo Aplicaciones → Accesorios → Terminal. En Windows necesitas ir a Inicio → Todos los programas → Accesorios → Línea de Comandos. Mas allá, en Windows, `psql` requiere que ingreses con un nombre de usuario y contraseña, el que escogiste durante la instalación. Si `psql` está pregunando por una contras´ña y no parece funcionar, trata `psql -U -W` primero y luego presioa Enter. 59 | 60 | ``` 61 | $ psql 62 | psql (9.3.4) 63 | Type "help" for help. 64 | # 65 | ``` 66 | 67 | Nuestro `$` ahora ha cambiado a `#`, lo cual significa que ahora estamos enviando comandos a PostgreSQL. Vamos a crear un usuario con `CREATE USER name;` (recuerda usar el punto y coma): 68 | 69 | ``` 70 | # CREATE USER name; 71 | ``` 72 | 73 | Reemplaza `nombre` con tu propio nombre. No deberías usar letras acentadas o espacios en blanco. (ejemplo `bożena maria` es inválida - necesitas convertirla en `bozena_maria`). Si esto va bien, puedes tener una respuesta `CREATE ROLE` de la consola. 74 | 75 | Ahora es tiempo de crear una base de datos para tu proyecto en Django: 76 | 77 | ``` 78 | # CREATE DATABASE djangogirls OWNER name; 79 | ``` 80 | 81 | Recuerda reemplazar `name` con el nombre que hayas escogido (ejemplo `bozena_maria`). Esto crea una base de datos vacía que puedes ahora usar en tus proyectos. Si esto va bien, puedes obtener una respuesta `CREATE DATABASE` de la consola. 82 | 83 | ¡Genial - las bases de dates están en orden! 84 | 85 | # Actualizar la configuración 86 | 87 | Encuentra esta parte en tu archivo `mysite/settings.py`: 88 | 89 | ```python 90 | DATABASES = { 91 | 'default': { 92 | 'ENGINE': 'django.db.backends.sqlite3', 93 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 94 | } 95 | } 96 | ``` 97 | 98 | Y reemplazalo con esto: 99 | 100 | ```python 101 | DATABASES = { 102 | 'default': { 103 | 'ENGINE': 'django.db.backends.postgresql', 104 | 'NAME': 'djangogirls', 105 | 'USER': 'name', 106 | 'PASSWORD': '', 107 | 'HOST': 'localhost', 108 | 'PORT': '', 109 | } 110 | } 111 | ``` 112 | 113 | Recuerda cambiar `name` con el nombre de usuario que creaste antes en este capítulo. 114 | 115 | # Instalando el paquete PostgreSQL para Python 116 | 117 | Primero, instala `Heroku Toolbelt` desde https://toolbelt.heroku.com/ porque lo necesitaremos mas adelante para desplegar tu sitio, esto también include Git, lo cual puede ser util. 118 | 119 | Lo siguiente es instalar un paquete que le permita a Python hablar con PostgreSQL - este es llamado `psycopg2`. Las intrucciones de instalación difieren un poco entre Windows y Linux/OS X. 120 | 121 | ## Windows 122 | 123 | Para Windows, descarga el arhchivo pre construido de http://www.stickpeople.com/projects/python/win-psycopg/ 124 | 125 | Asegúrate de que tienes la versión de Python correspondiente (3.4 debería ser la última línea) y la arquitectura correcta (32 bit en la columna de la izquiera o 64 bit en la columna derecha). 126 | 127 | Renombra el archivo descargado y muévelo para que estédisponible en `C:\psycopg2.exe`. 128 | 129 | Una vez esté terminado, ejecuta el siguiente comando en la terminal (asegúrate que el `virtualenv` esté activado): 130 | 131 | ``` 132 | easy_install C:\psycopg2.exe 133 | ``` 134 | 135 | ## Linux y OS X 136 | 137 | Ejecuta el siguiente código en la consola: 138 | 139 | ``` 140 | (myvenv) ~/djangogirls$ pip install psycopg2 141 | ``` 142 | 143 | Si eso sale bien, verás algo así: 144 | 145 | ``` 146 | Downloading/unpacking psycopg2 147 | Installing collected packages: psycopg2 148 | Successfully installed psycopg2 149 | Cleaning up... 150 | 151 | --- 152 | ``` 153 | 154 | Una vez esto esté completo, ejecuta `python -c "import psycopg2"`. Si no tienes ningún error, todo está instalado correctamente. 155 | 156 | # Aplicando las migraciones y creando un super usuario 157 | 158 | En orden de usar la base de datos recién creada en nuestro proyecto, necesitarás aplicar las migraciones en tu ambiente virtual ejecutando el siguiente código: 159 | 160 | ``` 161 | (myvenv) ~/djangogirls$ python manage.py migrate 162 | ``` 163 | 164 | Para agregar nuevos post en nuestro blog, también necesitas crear un super usuario, ejecutando el siguiente código: 165 | 166 | ``` 167 | (myvenv) ~/djangogirls$ python manage.py createsuperuser --username name 168 | ``` 169 | Recuerda reemplazar `name` con el nombre de usuario. Se te preguntará por un email y una contraseña. 170 | 171 | Ahora puedes ejecutar el servidor. Entra en la aplicación con la cuenta de super usuario y comienza a agregar posts a tu nueva base de datos. 172 | -------------------------------------------------------------------------------- /en/authentication_authorization/README.md: -------------------------------------------------------------------------------- 1 | # Homework: Adding security to your website 2 | 3 | You might have noticed that you didn't have to use your password, apart from back when we used the admin interface. You might also have noticed that this means that anyone can add or edit posts in your blog. I don't know about you, but I don't want just anyone to post on my blog. So let's do something about it. 4 | 5 | ## Authorizing add/edit of posts 6 | 7 | First let's make things secure. We will protect our `post_new`, `post_edit`, `post_draft_list`, `post_remove` and `post_publish` views so that only logged-in users can access them. Django ships with some nice helpers for doing that, called _decorators_. Don't worry about the technicalities now; you can read up on these later. The decorator we want to use is shipped in Django in the module `django.contrib.auth.decorators` and is called `login_required`. 8 | 9 | So edit your `blog/views.py` and add these lines at the top along with the rest of the imports: 10 | 11 | ```python 12 | from django.contrib.auth.decorators import login_required 13 | ``` 14 | 15 | Then add a line before each of the `post_new`, `post_edit`, `post_draft_list`, `post_remove` and `post_publish` views (decorating them) like the following: 16 | 17 | ```python 18 | @login_required 19 | def post_new(request): 20 | [...] 21 | ``` 22 | 23 | That's it! Now try to access `http://localhost:8000/post/new/`. Notice the difference? 24 | 25 | > If you just got the empty form, you are probably still logged in from the chapter on the admin-interface. Go to `http://localhost:8000/admin/logout/` to log out, then go to `http://localhost:8000/post/new` again. 26 | 27 | You should get one of our beloved errors. This one is quite interesting, actually: the decorator we added will redirect you to the login page, but since that's not yet available, it raises a "Page not found (404)". 28 | 29 | Don't forget to add the decorator from above to `post_edit`, `post_remove`, `post_draft_list` and `post_publish` too. 30 | 31 | Hooray, we've reached part of our goal!! Now other people can't create posts on our blog anymore. Unfortunately we can't create posts anymore too. So let's fix that next. 32 | 33 | 34 | ## Log in users 35 | 36 | We could now try to do lots of magical stuff to implement users and passwords and authentication, but doing this correctly is rather complicated. As Django is "batteries included", someone has done the hard work for us, so we will make further use of the authentication tools provided. 37 | 38 | In your `mysite/urls.py` add a url `path('accounts/login/', views.LoginView.as_view(), name='login')`. So the file should now look similar to this: 39 | 40 | ```python 41 | from django.urls import path, include 42 | from django.contrib import admin 43 | 44 | from django.contrib.auth import views 45 | 46 | urlpatterns = [ 47 | path('admin/', admin.site.urls), 48 | path('accounts/login/', views.LoginView.as_view(), name='login'), 49 | path('', include('blog.urls')), 50 | ] 51 | ``` 52 | 53 | Then we need a template for the login page, so create a directory `blog/templates/registration` and a file inside named `login.html`: 54 | 55 | ```django 56 | {% extends "blog/base.html" %} 57 | 58 | {% block content %} 59 | {% if form.errors %} 60 |

Your username and password didn't match. Please try again.

61 | {% endif %} 62 | 63 |
64 | {% csrf_token %} 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 |
{{ form.username.label_tag }}{{ form.username }}
{{ form.password.label_tag }}{{ form.password }}
75 | 76 | 77 | 78 |
79 | {% endblock %} 80 | ``` 81 | 82 | You will see that this also makes use of our _base_ template for the overall look and feel of your blog. 83 | 84 | The nice thing here is that this _just worksTM_. We don't have to deal with handling of the form submission nor with passwords and securing them. Only more thing is left to do. We should add a setting to `mysite/settings.py`: 85 | 86 | ```python 87 | LOGIN_REDIRECT_URL = '/' 88 | ``` 89 | 90 | so that when the login page is accessed directly, it will redirect a successful login to the top-level index (the homepage of our blog). 91 | 92 | ## Improving the layout 93 | 94 | We already set things up so that only authorized users (i.e. us) see the buttons for adding and editing posts. Now we want to make sure a login button appears for everybody else. 95 | 96 | We will add a login button that looks like this: 97 | 98 | ```django 99 | 100 | ``` 101 | 102 | For this we need to edit the templates, so let's open up `blog/templates/blog/base.html` and change it so the part between the `` tags looks like this: 103 | 104 | ```django 105 | 106 | 115 |
116 |
117 |
118 | {% block content %} 119 | {% endblock %} 120 |
121 |
122 |
123 | 124 | ``` 125 | 126 | You might recognize the pattern here. There is an if-condition in the template that checks for authenticated users to show the add and edit buttons. Otherwise it shows a login button. 127 | 128 | ## More on authenticated users 129 | 130 | Let's add some sugar to our templates while we're at it. First we will add some details to show when we are logged in. Edit `blog/templates/blog/base.html` like this: 131 | 132 | ```django 133 | 143 | ``` 144 | 145 | This adds a nice "Hello _<username>_" to remind us who we are logged in as, and that we are authenticated. Also, this adds a link to log out of the blog -- but as you might notice this isn't working yet. Let's fix it! 146 | 147 | We decided to rely on Django to handle login, so let's see if Django can also handle logout for us. Check https://docs.djangoproject.com/en/2.0/topics/auth/default/ and see if you find something. 148 | 149 | Done reading? By now you may be thinking about adding a URL in `mysite/urls.py` pointing to Django's logout view (i.e. `django.contrib.auth.views.logout`), like this: 150 | 151 | ```python 152 | from django.urls import path, include 153 | from django.contrib import admin 154 | 155 | from django.contrib.auth import views 156 | 157 | urlpatterns = [ 158 | path('admin/', admin.site.urls), 159 | path('accounts/login/', views.LoginView.as_view(), name='login'), 160 | path('accounts/logout/', views.LogoutView.as_view(next_page='/'), name='logout'), 161 | path('', include('blog.urls')), 162 | ] 163 | ``` 164 | 165 | That's it! If you followed all of the above up to this point (and did the homework), you now have a blog where you 166 | 167 | - need a username and password to log in, 168 | - need to be logged in to add, edit, publish or delete posts, 169 | - and can log out again. 170 | -------------------------------------------------------------------------------- /es/authentication_authorization/README.md: -------------------------------------------------------------------------------- 1 | # Tarea: Asegura tu sitio 2 | 3 | Puedes haberte dado cuenta que no usaste tu contraseña, además de cuando iniciaste en la interfaz de administrador. También debes haber notado que esto significa que cualquiera puede editar tus post en tu blog. No se tu, pero a mi no me gustaría que cualquiera editara mi blog, así que vamos a hacer algo al respecto. 4 | 5 | ## Autorizando la edición y creación de post. 6 | 7 | Primero vamos a hacer las cosas seguras. Vamos a proteger nuestras vistas `post_new`, `post_edit`, `post_draft_list`, `post_remove` y `post_publish` para que solo usuarios que hayan ingresado puedan acceder a ellas. Django nos da algunas ayudas para hacer esto, llamados _decoradores_. No te preocupes por las tecnicalidades ahora; puedes leer sobre eso después. El decorador que vamos a usar está en el módulo `django.contrib.auth.decorators` y es llamado `login_required`. 8 | 9 | Entonces editemos tu `blog/views.py` y agrega estas lineas al comienzo del archivo junto con las demás importaciones: 10 | 11 | ```python 12 | from django.contrib.auth.decorators import login_required 13 | ``` 14 | 15 | Entonces añade la línea antes de cada una de las vistas `post_new`, `post_edit`, `post_draft_list`, `post_remove` y `post_publish`, (decorándolas) como a continuación: 16 | 17 | ```python 18 | @login_required 19 | def post_new(request): 20 | [...] 21 | ``` 22 | 23 | ¡Eso es todo! Ahora intenta acceder a `http://localhost:8000/post/new/. ¿Notas la diferencia? 24 | 25 | > Si obtienes un formulario vacío, posiblemente sigues logeado desde el capítulo en la interfaz de administrador. Ve a `http://localhost:8000/admin/logout/` para salir, y luego `http://localhost:8000/post/new` de nuevo. 26 | 27 | Puedes obtener uno de nuestros amados errores. Esto es muy interesante: El decoradr que añadimos nos redireccionará a la página de ingreso, pero como no está disponible, retorna un "Page not found (404)". 28 | 29 | No te olvides del decorador encima de `post_edit`, `post_remove`, `post_draft_list` y `post_publish` también. 30 | 31 | !Perfecto!, !hemos alcanzado parte de nuestro objetivo! Ahora otras personas no podrán crear post en nuestro blog. Desafortunadamente, nosortos tampoco podemos crearlos. Así que vamos a arreglarlo a continuación. 32 | 33 | ## Ingresar usuarios 34 | 35 | Ahora podemos intentar hacer muchas cosas mágicas para implementar usuarios y contraseñas y autenticación, pero hacer esto correctamente es complicado. Como django viene con "baterías incluidas", alguien ya ha hecho el trabajo duro por nosotros, así que vamos a utilizarlas. 36 | 37 | En `mysite/urls.py` agrega una nueva entrada `path('accounts/login/', views.login, name='login')`. El contenido del archivo debería verse similar a: 38 | 39 | ```python 40 | from django.urls import include, path 41 | from django.contrib import admin 42 | 43 | from django.contrib.auth import views 44 | 45 | urlpatterns = [ 46 | path('admin/', admin.site.urls), 47 | path('accounts/login/', views.login, name='login'), 48 | path('', include('blog.urls')), 49 | ] 50 | ``` 51 | 52 | Luego necesitamos agregar una plantilla para la página de ingreso, así que crearemos el directorio `blog/templates/registration` y dentro un archivo llamado `login.html`. 53 | 54 | ```django 55 | {% extends "blog/base.html" %} 56 | 57 | {% block content %} 58 | {% if form.errors %} 59 |

Your username and password didn't match. Please try again.

60 | {% endif %} 61 | 62 |
63 | {% csrf_token %} 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
{{ form.username.label_tag }}{{ form.username }}
{{ form.password.label_tag }}{{ form.password }}
74 | 75 | 76 | 77 |
78 | {% endblock %} 79 | ``` 80 | 81 | Verás que también hace uso de la plantilla _base_ para mantener el estilo de tu blog. 82 | 83 | La cosa buena quí es que funciona. No necesitamos lidiar con el manejo del for o las contraseñas y asegurarlas. Solamente una cosa mas para hacer. Entonces vamos a agregar esta configuración en `mysite/settings.py`: 84 | 85 | ```python 86 | LOGIN_REDIRECT_URL = '/' 87 | ``` 88 | 89 | Entonces cuando la página es accesada directamente, la redirecionará a la página de primer nivel, el index (la página de inicio de blog). 90 | 91 | ## Mejorando el diseño 92 | 93 | Ya definimos como autorizar usuarios. Vemos los botones para añadir post. Ahora queremos asegurarnos que el botón de ingreso le aparezca a todos. 94 | 95 | Vamos a agregar el botón de ingreso así: 96 | 97 | ```django 98 | 99 | ``` 100 | 101 | Para esto necesitamos editar la plantilla, así que vamos a abrir `blog/templates/blog/base.html` y cambiarlo, así que la parte en medio de `` se verá así: 102 | 103 | ```django 104 | 105 | 114 |
115 |
116 |
117 | {% block content %} 118 | {% endblock %} 119 |
120 |
121 |
122 | 123 | ``` 124 | 125 | Debes reconocer el patrón aquí. Hay una condición `if` en la plantilla que verifica si el usuario está autenticado para mostrar los botones de agregar y editar. De otra manera muestra el botón de ingreso. 126 | 127 | ## Más para usuarios autenticados 128 | 129 | Vamos a agregarle algo de dulce a nuestras plantillas mientras estamos ahí. Primero vamos a mostrar algunos detalles de cuando ingresamos. Editemos el archivo `blog/templates/blog/base.html` así: 130 | 131 | ```django 132 | 142 | ``` 143 | 144 | Esto agrega un lindo "Hello _<username>_" para recordarle al usuario como ingresó, y que está autenticado. También agrega un enlace de salida del blog -- como puedes ver, aún no funciona. ¡Vamos a arreglarlo! 145 | 146 | Decidimos apoyarnos en Django para manejar el inicio de sesión, así que vamos ver si Django nos permite manejar el cierre de la sesión. Mira https://docs.djangoproject.com/en/2.0/topics/auth/default/ y ve si encuentras algo. 147 | 148 | ¿Terminaste de leer? Por ahora vamos a pensar en agregar una URL en `mysite/urls.py` apuntando a la vista de salida (`django.contrib.auth.views.logout`) así: 149 | 150 | ```python 151 | from django.urls import include, path 152 | from django.contrib import admin 153 | 154 | from django.contrib.auth import views 155 | 156 | urlpatterns = [ 157 | path('admin/', admin.site.urls), 158 | path('accounts/login/', views.LoginView.as_view(), name='login'), 159 | path('accounts/logout/', views.LogoutView.as_view(next_page='/'), name='logout'), 160 | path('', include('blog.urls')), 161 | ] 162 | ``` 163 | 164 | ¡Eso es todo! Si has segido todo lo anterior, en este punto (e hiciste la tarea), tu blog ahora: 165 | 166 | - necesita un nombre de usuario y contraseña para ingresar, 167 | - necesita haber ingresado para agregar, editar, publicar o eliminar posts 168 | - puede salir de nuevo 169 | -------------------------------------------------------------------------------- /ko/manual_pythonanywhere_deploy/README.md: -------------------------------------------------------------------------------- 1 | # 파이썬애니웨어(PythonAnywhere) 수동 배포하기 2 | 3 | 기본 튜토리얼에서 파이썬애니웨어의 "autoconfigure" 스크립트를 통해 우리가 만든 앱을 배포했어요. 여기에는 많은 마법이 숨겨져 있어요. 심화 튜토리얼에서는 파이썬애니웨어에 수동으로 배포하며 자동으로 설정된 스크립트가 실제로 하는 "무대 뒤"의 일을 알게 될 거예요. 4 | 5 | 왜 이 일이 흥미로울까요? 순수한 호기심을 넘어서 언젠가는 다른 호스팅 업체가 운영하는 서버에 배포하고 싶어 질 거예요. 서버를 띄우는 것뿐만 아니라 수동으로 배포하는 단계를 똑같이 밟아야 하지요. 6 | 7 | 수동으로 이 절차를 실행해 보는 것도 장고걸스 코치에게는 좋은 경험이 될 거예요. 워크샵에서 자동 구성 스크립트에 문제가 생길 수도 있을 거예요. 이때 수동으로 실행하는 방법을 알고 있다면 참여자가 배포에 실패했을 때 작동 중인 상태로 되돌리도록 도움을 줄 수 있어요. 8 | 9 | 자, 그럼 좀 더 알아봐요! 10 | 11 | 12 | ## 가정해 보기 13 | 14 | * 아마도 깃허브 리퍼지토리에 익스텐션 일부 코드를 올려놨을 거예요. 필요하다면 배포 챕터 시작 부분을 보세요. 15 | * 그리고 파이썬애니웨어 계정도 있을 거예요. 장고걸스 웹 앱이 이미 실행 중이라면 삭제를 해야 해요. (아니라면 새 계정으로 회원가입을 해야 해요!) 16 | 17 | ## 개요 18 | 19 | 파이썬애니웨어 또는 어느 서버에 배포하든 비슷한 과정을 거친답니다. 20 | 21 | * 서버에서 코드를 가져올 때 `git clone` 과 `git pull` 을 사용할 거예요. 22 | 23 | * 서버에 필요한 것들을 설치하기 위해 가상환경을 사용할 거예요. 마치 우리 컴퓨터에 설치했던 것처럼요. 하지만 파이썬애니웨어에서는 `virtualenvwrapper`라 불리는 바로 가기 기능을 사용할 거예요. 24 | 25 | * 서버에 데이터베이스 설정을 해주세요. 우리 컴퓨터에 있는 데이터베이스와 서버에 있는 데이터베이스는 별개예요. 이를 위해 `manage.py migrate` 과 `manage.py createsuperuser` 를 실행해 주세요. 26 | 27 | * 서버에서 정적 파일을 설정해 보도록 할게요. 내 컴퓨터에서는 `runserver` 를 실행해 정적 파일을 다뤘지만, 서버는 정적 파일을 최적화해서 다루길 원해요. 파이썬애니웨어의 **Web 탭**에서 `collectstatic` 이라는 새로운 명령어를 사용해 정적 파일 구성을 해줄 거예요. 28 | 29 | * 그리고 실제로 장고 앱을 서버와 연결해서 라이브로 인터넷에 공개해요. 우리는 이 과정을 파이썬애니웨어의 **Web 탭**에서 **WSGI 파일** 설정을 해줄 거에요. 30 | 31 | 32 | ## 파이썬애니웨어로 코드를 가져오기 33 | 34 | [파이썬애니웨어 대시보드](https://www.pythonanywhere.com)로 가볼게요. "Bash" 콘솔의 옵션을 선택하세요. Bash는 컴퓨터에 있는 커맨드 라인과 같아요. 35 | 36 | Other란을 가리킴: Bash에서 새로운 콘솔 시작하기 37 | 38 | > **Note** 파이썬애니웨어는 리눅스에서 돌아가요. 그래서 윈도우 사용자라면 로컬 컴퓨터에서 봤던 화면과 다르게 보일 거예요. 39 | 40 | 파이썬애니웨어에 있는 "clone"으로 저장소를 생성해서 깃허브에서 코드를 가져올 거예요. 파이썬애니웨어의 콘솔에서 아래처럼 타이핑하세요. 41 | 42 | {% filename %}PythonAnywhere command-line{% endfilename %} 43 | ``` 44 | $ git clone https://github.com//my-first-blog.git <파이썬애니웨어-사용자-이름>.pythonanywhere.com 45 | ``` 46 | 47 | * `` 대신 사용 중인 깃허브 사용자 이름으로 변경하세요. 48 | * 그리고 ``을 파이썬애니웨어 사용자 이름으로 변경하세요. 49 | 50 | 이 코드는 파이썬애니웨어에 있는 코드를 가져와서 여러분의 미래 웹사이트 주소의 이름을 딴 폴더에 넣어줄 거예요. `tree`를 입력해 확인하세요. 51 | 52 | {% filename %}PythonAnywhere command-line{% endfilename %} 53 | ``` 54 | $ tree 55 | ola.pythonanywhere.com/ 56 | ├── blog 57 | │ ├── __init__.py 58 | │ ├── admin.py 59 | │ ├── migrations 60 | │ │ ├── 0001_initial.py 61 | │ │ └── __init__.py 62 | │ ├── models.py 63 | │ ├── tests.py 64 | │ └── views.py 65 | ├── manage.py 66 | └── mysite 67 | ├── __init__.py 68 | ├── settings.py 69 | ├── urls.py 70 | └── wsgi.py 71 | ``` 72 | 73 | 74 | ### 파이썬애니웨어에 가상환경(virtualenv) 생성하기 75 | 76 | 로컬 컴퓨터에 했던 것처럼 파이썬애니웨어 상에서도 가상환경을 만들어 주세요. Bash 콘솔 창에서 아래와 같이 입력하세요. 77 | 78 | {% filename %}PythonAnywhere command-line{% endfilename %} 79 | ``` 80 | $ mkvirtualenv --python=python3.6 .pythonanywhere.com 81 | Running virtualenv with interpreter /usr/bin/python3.6 82 | [...] 83 | Installing setuptools, pip...done. 84 | ``` 85 | 86 | 파이썬애니웨어가 추천하는 `mkvirtualenv`는 "virtualenvwrapper"라는 도구에서 왔어요. 87 | 로컬 컴퓨터에서 배운 `virtualenv` 명령을 기반으로 만들어진 단축 명령어 모음이에요. 88 | 89 | 명령이 끝나면 가상 환경을 활성화시키세요. 프로젝트 소스코드의 폴더 이름을 짓는 것처럼 미래 웹사이트의 이름을 정했어요. 연습 삼아 가상 환경을 비활성화하고 다시 활성화해보세요. 90 | 91 | 비활성화는 로컬 컴퓨터에서 했던 것처럼 `deactivate`로 하면 될 거예요. virtualenvwrapper에서는 사용하고 있는 가상 환경의 이름만 알면 `workon` 단축 명령어를 사용해서 활성화하세요. 92 | 93 | {% filename %}PythonAnywhere command-line{% endfilename %} 94 | ``` 95 | (ola.pythonanywhere.com) $ deactivate 96 | $ which python 97 | /usr/bin/python 98 | $ workon .pythonanywhere.com 99 | (ola.pythonanywhere.com) $ which python 100 | /home/ola/.virtualenvs/ola.pythonanywhere.com/bin/python 101 | ``` 102 | 103 | 자, 이제 파이썬애니웨어에 있는 가상 환경에 장고를 설치해 볼 거예요. 104 | 105 | {% filename %}PythonAnywhere command-line{% endfilename %} 106 | ``` 107 | (ola.pythonanywhere.com) $ pip install 'django<2' 108 | Collecting django 109 | [...] 110 | Successfully installed django-1.11.9 111 | ``` 112 | 113 | > **Note** `pip install` 설치는 몇 분 정도 걸릴 거예요. 인내심을 갖고 기다려야 해요! 하지만 5분 이상 걸린다면 뭔가 잘못된 거니 코치에게 물어보세요. 114 | 115 | 116 | 117 | 118 | ### 파이썬애니웨어에 데이터베이스 생성하기 119 | 120 | 우리 컴퓨터와 서버에서는 차이점이 있습니다. 그래서 로컬 컴퓨터와 서버의 사용자 계정과 게시물은 달라요. 121 | 122 | 우리 컴퓨터에서 했던 것처럼 `migrate` 와 `createsuperuser`로 데이터베이스를 초기화해주는 단계를 다시 반복해 볼 거예요. 123 | 124 | Bash 콘솔로 가서 가상 환경이 여전히 활성화된 상태인지 확인하고 다음 명령어를 실행해 보세요. (콘솔을 닫았다면 다시 열고 `workon`을 통해 가상 환경을 활성화할 수 있습니다.) 125 | 126 | {% filename %}PythonAnywhere command-line{% endfilename %} 127 | ``` 128 | (ola.pythonanywhere.com) $ python manage.py migrate 129 | Operations to perform: 130 | [...] 131 | Applying sessions.0001_initial... OK 132 | (ola.pythonanywhere.com) $ python manage.py createsuperuser 133 | ``` 134 | 135 | ### 정적 파일 모으기 136 | 137 | 이제 우리는 `collecstatic`라는 새로운 명령어를 배웠어요. 이 명령은 우리의 앱이 (우리가 만든 *블로그*와 내장된 앱인 *관리페이지*를 포함하여) 모든 정적 파일을 수집하도록 해서 서버가 찾을 수 있는 장소에 모아둠: 138 | 139 | {% filename %}PythonAnywhere command-line{% endfilename %} 140 | ``` 141 | (ola.pythonanywhere.com) $ python manage.py collectstatic 142 | You have requested to collect static files at the destination 143 | [...] 144 | Are you sure you want to do this? 145 | [...] 146 | 62 static files copied to '/home/ola/ola.pythonanywhere.com/static'. 147 | ``` 148 | 149 | 150 | ## 블로그를 웹앱으로 게시하기 151 | 152 | 이제 코드가 파이썬애니웨어에 있고 가상 환경도 준비가 되고 데이터베이스도 초기화시켰어요. 이제 웹앱으로 게시할 준비가 되었어요. 153 | 154 | 로고를 클릭해서 파이썬애니웨어의 대시보드로 돌아온 다음 **Web** 탭을 클릭해주세요. 마지막으로 **Add a new web app**을 클릭해 주세요. 155 | 156 | 도메인 이름을 확인한 후에 대화 상자에서 **수동 설정(manual configuration)** (N.B. – 가 *아니라* "장고" 옵션)을 선택해 주세요. 그런 다음 **Python 3.6**을 선택하고 다음을 클릭해서 설정을 완료하세요. 157 | 158 | > **Note** "장고"가 아닌 "수동 설정" 옵션을 선택하세요. 파이썬애니웨어의 기본 장고 설정은 정말 멋지지 않나요. ;-) 159 | 160 | 161 | ### 가상환경 설정하기 162 | 163 | 웹앱의 파이썬애니웨어 설정 화면으로 이동합니다. 서버에 있는 앱을 변경하고자 할 때는 이 화면으로 가야 해요. 164 | 165 | Web 탭의 virtualenv config 부분의 스크린샷으로 경로가 바르게 채워졌는지 확인해 보세요. 166 | 167 | "Virtualenv" 부분에서 "Enter the path to a virtualenv"라는 붉은 텍스트를 클릭하고 `/Web 탭home//my-first-blog/myvenv/`를 입력하세요. 경로를 저장하려면 체크 표시가 있는 파란색 상자를 클릭해 주세요. 168 | 169 | > **Note** 파이썬애니웨어에서 적당한 사용자 이름으로 변경하세요. 입력할 때 실수가 있다면 파이썬애니웨어에서 경고 알림을 표시해 줍니다. 170 | 171 | 172 | ### 정적 파일 매핑 추가하기 173 | 174 | 파이썬애니웨어에게 `/static/`이 어떤 URL 하위에 정적 파일들이 위치할지 알려줘야 해요. Web 탭의 "Static Files(정적 파일)"에서 이 작업을 해요. 175 | 176 | "Enter URL"이라는 빨간색 텍스트를 클릭하고 `/static/`이라고 입력해요. 그리고 파란색 체크박스를 클릭해서 저장해 주세요. 그리고 "Enter path"라는 텍스트를 클릭 하고 아래와 같이 입력 하세요. 177 | 178 | ``` 179 | `/home/<파이썬애니웨어-사용자-이름>/<파이썬애니웨어-사용자-이름.pythonanywhere.com/static` 180 | ``` 181 | 182 | Web 탭 정적파일의 스크린샷으로 URL 과 경로를 올바르게 설정하기 183 | 184 | 185 | ### WSGI 파일 설정하기 186 | 187 | 장고는 파이썬애니웨어가 지원하는 파이썬 "WSGI 프로토콜"을 사용해 웹사이트를 서빙합니다. WSGI 설정파일을 편집해서 파이썬애니웨어가 장고 블로그를 인식하게 해줄게요. 188 | 189 | "WSGI 설정 파일" 링크를 클릭하세요. (페이지 상단의 "Code" 부분 근처에 이런 형태의 경로가 보일 거에요. `/var/www/_pythonanywhere_com_wsgi.py`) 그러면 에디터가 열릴 거예요. 190 | 191 | 모든 내용을 지우고 아래의 내용으로 변경하세요. 192 | 193 | {% filename %}<your-username>_pythonanywhere_com_wsgi.py{% endfilename %} 194 | ```python 195 | import os 196 | import sys 197 | 198 | path = '/home//.pythonanywhere.com') 199 | if path not in sys.path: 200 | sys.path.append(path) 201 | 202 | os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings' 203 | 204 | from django.core.wsgi import get_wsgi_application 205 | application = get_wsgi_application() 206 | ``` 207 | 208 | > **Note** 먼저 했던 것처럼, 파일에서 자신의 파이썬애니웨어 사용자 이름으로 변경하세요. 209 | 210 | 이 파일은 파이썬애니웨어에게 우리의 앱이 있는 곳과 장고 설정 파일의 이름을 알려줘요. 211 | 212 | **저장(Save)** 을 클릭하고 **Web** 탭으로 돌아가세요. 213 | 214 | 이제 거의 다 됐어요! 큰 초록색의 **새로고침(Reload)** 버튼을 누르면 우리의 애플리케이션을 볼 수 있어요. 페이지 상단에 있어요. 215 | 216 | 217 | ## 디버깅팁 218 | 219 | 사이트를 방문했을 때 오류가 표시되면 디버깅 정보를 찾아봐야 하지요. 첫 번째로 볼 위치는 **오류 로그(Error log)** 예요. 파이썬애니웨어의 [Web 탭](https://www.pythonanywhere.com/web_app_setup/)에서 찾을 수 있어요. 만약 오류 메시지를 발견하게 된다면 가장 마지막에서 최근의 오류를 발견할 수 있을 거예요. 보통 이런 문제들이죠. 220 | 221 | - 우리가 콘솔에서 했던 무언가를 잊어버렸을 때 : 가상환경 만들기와 활성화하기, 그리고 그 안에 장고를 설치하기, 데이터베이스 마이그레이션이 잘 되었는지 확인하세요. 222 | 223 | - Web 탭 상의 가상환경 경로 실수 : 문제가 있다면 빨간색의 오류 메시지가 표시될 거예요. 224 | 225 | - WSGI 설정 파일을 잘못 작성한 경우 : `my-first-blog` 폴더 경로가 올바른지 확인하세요. 226 | 227 | - 우리가 만들었던 웹 앱의 가상환경 파이썬 버전이 다를 때 : 모두 파이썬 버전이 3.6이어야 합니다. 228 | 229 | [파이썬애니웨어 위키의 일반적인 디버깅 팁](https://www.pythonanywhere.com/wiki/DebuggingImportError)도 읽어보세요. 230 | 231 | -------------------------------------------------------------------------------- /ja/homework_create_more_models/README.md: -------------------------------------------------------------------------------- 1 | # 宿題:コメントモデルを作ろう 2 | 3 | 現在、ポストモデルしかありません。 あなたの読者からいくつかのフィードバックを受け取り、彼らにコメントさせるのはどうでしょうか? 4 | 5 | ## ブログにコメントモデルを作る 6 | 7 | `blog/models.py`を開き、このコードをファイルの最後に追加しましょう: 8 | 9 | ```python 10 | class Comment(models.Model): 11 | post = models.ForeignKey('blog.Post', on_delete=models.CASCADE, related_name='comments') 12 | author = models.CharField(max_length=200) 13 | text = models.TextField() 14 | created_date = models.DateTimeField(default=timezone.now) 15 | approved_comment = models.BooleanField(default=False) 16 | 17 | def approve(self): 18 | self.approved_comment = True 19 | self.save() 20 | 21 | def __str__(self): 22 | return self.text 23 | ``` 24 | 25 | 各フィールドタイプの意味を思い出す必要がある場合は、チュートリアルの**Djangoモデル**の章に戻ることができます。 26 | 27 | 拡張版チュートリアルでは、新しいフィールドタイプが登場します: 28 | - `models.BooleanField` - こちらはtrue/falseの値が入るフィールドです。 29 | 30 | `models.ForeignKey`の`related_name`引数は、ポストモデルの中からコメントにアクセスできるようにします。 31 | 32 | ## データベースにモデルのテーブルを作る 33 | 34 | 今度は、コメントモデルをデータベースに追加します。 これを行うには、モデルを変更したことをDjangoに伝えなければなりません。 コマンドラインに `python manage.py makemigrations blog`と入力してください。 次のような出力が表示されます: 35 | 36 | (myvenv) ~/djangogirls$ python manage.py makemigrations blog 37 | Migrations for 'blog': 38 | 0002_comment.py: 39 | - Create model Comment 40 | 41 | このコマンドが`blog/migrations/`ディレクトリに別のマイグレーションファイルを作成したことがわかります。 コマンドラインで `python manage.py migrate blog`とタイプして変更を適用する必要があります。 出力は次のようになります: 42 | 43 | ``` 44 | (myvenv) ~/djangogirls$ python manage.py migrate blog 45 | Operations to perform: 46 | Apply all migrations: blog 47 | Running migrations: 48 | Rendering model states... DONE 49 | Applying blog.0002_comment... OK 50 | ``` 51 | 52 | コメントモデルは今データベースに存在します! 管理画面でアクセスできたら素敵ですよね? 53 | 54 | ## 管理画面にコメントモデルを登録する 55 | 56 | 管理画面にコメントモデルを登録するため、`blog/admin.py`ファイルに以下の行を追加してください: 57 | 58 | ```python 59 | admin.site.register(Comment) 60 | ``` 61 | 62 | 追加する位置は、この行の直下です: 63 | 64 | ```python 65 | admin.site.register(Post) 66 | ``` 67 | 68 | コメントモデルをファイルの先頭でインポートすることも忘れないようにしてください: 69 | 70 | ```python 71 | from django.contrib import admin 72 | from .models import Post, Comment 73 | 74 | admin.site.register(Post) 75 | admin.site.register(Comment) 76 | ``` 77 | 78 | コマンドラインで`python manage.py runserver`を入力し、あなたのブラウザで [http://127.0.0.1:8000/admin/](http://127.0.0.1:8000/admin/) に行くと、コメントリストにアクセスすることができ、コメントの追加と削除の機能が使えるようになります。新しいコメント機能で遊んでみましょう! 79 | 80 | ## コメントを見えるようにする 81 | 82 | `blog/templates/blog/post_detail.html`というファイルを開き、`{% endblock %}`タグの前に、以下の行を追加してください: 83 | 84 | ```django 85 |
86 | {% for comment in post.comments.all %} 87 |
88 |
{{ comment.created_date }}
89 | {{ comment.author }} 90 |

{{ comment.text|linebreaks }}

91 |
92 | {% empty %} 93 |

No comments here yet :(

94 | {% endfor %} 95 | ``` 96 | 97 | これで投稿の詳細ページでコメントを見ることができます。 98 | 99 | でも、コメントをもう少し良く見せられるかもしれませんね。 `static/css/blog.css`ファイルの一番下にいくつかのCSSを追加しましょう: 100 | 101 | ```css 102 | .comment { 103 | margin: 20px 0px 20px 20px; 104 | } 105 | ``` 106 | 107 | 投稿一覧ページでコメントについて、訪問者に知らせることもできます。 `blog/templates/blog/post_list.html`ファイルを開き、次の行を追加します: 108 | 109 | ```django 110 | Comments: {{ post.comments.count }} 111 | ``` 112 | 113 | 追加したら、テンプレートは次のようになります: 114 | 115 | ```django 116 | {% extends 'blog/base.html' %} 117 | 118 | {% block content %} 119 | {% for post in posts %} 120 |
121 |
122 | {{ post.published_date }} 123 |
124 |

{{ post.title }}

125 |

{{ post.text|linebreaksbr }}

126 | Comments: {{ post.comments.count }} 127 |
128 | {% endfor %} 129 | {% endblock content %} 130 | ``` 131 | 132 | ## 読者がコメントを書けるようにする 133 | 134 | 今は私たちのブログでコメントを見ることができますが、追加することはできません。 それを変えましょう! 135 | 136 | `blog/forms.py`を開き、以下の行をファイルの最後に追加します: 137 | 138 | ```python 139 | class CommentForm(forms.ModelForm): 140 | 141 | class Meta: 142 | model = Comment 143 | fields = ('author', 'text',) 144 | ``` 145 | 146 | コメントモデルをインポートすることを忘れないでください。以下の行を変更して: 147 | 148 | ```python 149 | from .models import Post 150 | ``` 151 | 152 | このようにします: 153 | 154 | ```python 155 | from .models import Post, Comment 156 | ``` 157 | 158 | 今度は、`blog/templates/blog/post_detail.html`を開き、`{% for comment in post.comments.all %}`の前に以下の行を追加します: 159 | 160 | ```django 161 | Add comment 162 | ``` 163 | 164 | もし投稿詳細ページに行くと、以下のエラーが表示されます: 165 | 166 | ![NoReverseMatch](images/url_error.png) 167 | 168 | 私たちはそれを修正する方法を知っています! `blog/urls.py`を開き、このパターンを`urlpatterns`に追加してください: 169 | 170 | ```python 171 | path('post//comment/', views.add_comment_to_post, name='add_comment_to_post'), 172 | ``` 173 | 174 | ページを更新したら、また違うエラーが出てきます! 175 | 176 | ![AttributeError](images/views_error.png) 177 | 178 | こちらのエラーを修正するために、`blog/views.py`に以下のビューを追加してください: 179 | 180 | ```python 181 | def add_comment_to_post(request, pk): 182 | post = get_object_or_404(Post, pk=pk) 183 | if request.method == "POST": 184 | form = CommentForm(request.POST) 185 | if form.is_valid(): 186 | comment = form.save(commit=False) 187 | comment.post = post 188 | comment.save() 189 | return redirect('post_detail', pk=post.pk) 190 | else: 191 | form = CommentForm() 192 | return render(request, 'blog/add_comment_to_post.html', {'form': form}) 193 | ``` 194 | 195 | `CommentForm`をファイルの先頭でインポートすることを忘れないでください: 196 | 197 | ```python 198 | from .forms import PostForm, CommentForm 199 | ``` 200 | 201 | 今、詳細投稿ページで”Add Comment”というボタンが表示されています。 202 | 203 | ![AddComment](images/add_comment_button.png) 204 | 205 | しかし、そのボタンをクリックしたら、次のように表示されます: 206 | 207 | ![TemplateDoesNotExist](images/template_error.png) 208 | 209 | 210 | 表示されたエラーが伝えているように、テンプレートは存在していないので、`blog/templates/blog/add_comment_to_post.html`という新しいテンプレートを作り、以下のコードを追加してください: 211 | 212 | ```django 213 | {% extends 'blog/base.html' %} 214 | 215 | {% block content %} 216 |

New comment

217 |
{% csrf_token %} 218 | {{ form.as_p }} 219 | 220 |
221 | {% endblock %} 222 | ``` 223 | 224 | わーい! 今あなたの読者は、ブログの投稿をどう思っているかを知らせることができます! 225 | 226 | ## コメントを管理する 227 | 228 | すべてのコメントを表示する必要はありません。 ブログの所有者として、あなたはコメントを承認または削除する選択肢をおそらく望むでしょう。 それについて何かしましょう。 229 | 230 | `blog/templates/blog/post_detail.html`を開き、以下の行を: 231 | 232 | ```django 233 | {% for comment in post.comments.all %} 234 |
235 |
{{ comment.created_date }}
236 | {{ comment.author }} 237 |

{{ comment.text|linebreaks }}

238 |
239 | {% empty %} 240 |

No comments here yet :(

241 | {% endfor %} 242 | ``` 243 | 244 | このように変更してください: 245 | 246 | ```django 247 | {% for comment in post.comments.all %} 248 | {% if user.is_authenticated or comment.approved_comment %} 249 |
250 |
251 | {{ comment.created_date }} 252 | {% if not comment.approved_comment %} 253 | 254 | 255 | {% endif %} 256 |
257 | {{ comment.author }} 258 |

{{ comment.text|linebreaks }}

259 |
260 | {% endif %} 261 | {% empty %} 262 |

No comments here yet :(

263 | {% endfor %} 264 | ``` 265 | 266 | `comment_remove`と` comment_approve`パターンに一致するURLがまだないので、 `NoReverseMatch`が表示されます。 267 | 268 | こちらのエラーを修正するため、以下のURLパターンを`blog/urls.py`に追加してください: 269 | 270 | ```python 271 | path('comment//approve/', views.comment_approve, name='comment_approve'), 272 | path('comment//remove/', views.comment_remove, name='comment_remove'), 273 | ``` 274 | 275 | さて、あなたには`AttributeError`が見えています。このエラーを修正するために、以下のビューを`blog/views.py`に追加してください: 276 | 277 | ```python 278 | @login_required 279 | def comment_approve(request, pk): 280 | comment = get_object_or_404(Comment, pk=pk) 281 | comment.approve() 282 | return redirect('post_detail', pk=comment.post.pk) 283 | 284 | @login_required 285 | def comment_remove(request, pk): 286 | comment = get_object_or_404(Comment, pk=pk) 287 | comment.delete() 288 | return redirect('post_detail', pk=comment.post.pk) 289 | ``` 290 | 291 | ファイルの先頭に `Comment`をインポートする必要があります: 292 | 293 | ```python 294 | from .models import Post, Comment 295 | ``` 296 | 297 | すべて動作します!私たちにできる小さな微調整があります。投稿一覧ページ(の投稿の下)には、現在、投稿が受け取ったすべてのコメントの数が表示されます。*承認された* コメントの数を表示するように変更しましょう。 298 | 299 | これを修正するには、 `blog/templates/blog/post_list.html`を開き、次の行を: 300 | 301 | ```django 302 | Comments: {{ post.comments.count }} 303 | ``` 304 | 305 | 下記のように変更してください: 306 | 307 | ```django 308 | Comments: {{ post.approved_comments.count }} 309 | ``` 310 | 311 | 最後に、このメソッドを `blog/models.py`の` Post`モデルに追加してください: 312 | 313 | ```python 314 | def approved_comments(self): 315 | return self.comments.filter(approved_comment=True) 316 | ``` 317 | 318 | これでコメント機能は終了です! おめでとう! :-) 319 | -------------------------------------------------------------------------------- /ko/homework_create_more_models/README.md: -------------------------------------------------------------------------------- 1 | # 숙제 : 댓글 모델 만들기 2 | 3 | 지금까지 Post 모델만 있었죠. 독자들에게 피드백과 코멘트를 받을 수 있는 댓글을 만들어보면 어떨까요? 4 | 5 | ## Comment 모델 만들기 6 | 7 | `blog/models.py` 파일을 열어, 파일의 맨 마지막에 아래 코드를 추가해주세요. 8 | 9 | ```python 10 | class Comment(models.Model): 11 | post = models.ForeignKey('blog.Post', on_delete=models.CASCADE, related_name='comments') 12 | author = models.CharField(max_length=200) 13 | text = models.TextField() 14 | created_date = models.DateTimeField(default=timezone.now) 15 | approved_comment = models.BooleanField(default=False) 16 | 17 | def approve(self): 18 | self.approved_comment = True 19 | self.save() 20 | 21 | def __str__(self): 22 | return self.text 23 | ``` 24 | 25 | 26 | 각 필드 타입들에 대한 설명은 장고걸스 튜토리얼의 **[Django 모델](https://tutorial.djangogirls.org/ko/django_models/)** 장에서 다뤘으니 다시 읽고 돌아와도 좋아요. 27 | 28 | 이번 장에서는 새로운 필드 타입을 써볼게요. 29 | 30 | - models.BooleanField - 참/거짓(true/false) 필드랍니다. 31 | 32 | `models.ForeignKey`의 `related_name` 옵션은 Post 모델에서 댓글에 액서스할 수 있게 합니다. 33 | 34 | ## 데이터베이스에 새 모델 테이블 생성하기 35 | 36 | 자, 데이터베이스에 Comment 모델을 추가할 시간이에요. 그러려면 장고에게 모델 변경내용을 알려줘야 합니다. `python manage.py makemigrations blog` 명령어를 입력하세요. 37 | 38 | ``` 39 | (myvenv) ~/djangogirls$ python manage.py makemigrations blog 40 | Migrations for 'blog': 41 | 0002_comment.py: 42 | - Create model Comment 43 | ``` 44 | 45 | `blog/migrations` 디렉터리에 새로 마이그레이션 파일이 생성되었어요. 이제 `python manage.py migrate blog` 명령어로 변경내역을 적용합니다. 다음과 같은 출력이 보일 거예요. 46 | 47 | ``` 48 | (myvenv) ~/djangogirls$ python manage.py migrate blog 49 | Operations to perform: 50 | Apply all migrations: blog 51 | Running migrations: 52 | Rendering model states... DONE 53 | Applying blog.0002_comment... OK 54 | ``` 55 | 56 | 이제 Comment 모델이 데이터베이스 안에 생겼네요! 관리자 패널(admin panel)에서 Commnet 모델에 접근할 수 있다면 멋지겠죠? 57 | 58 | ## 관리자 패널에 Comment 모델 등록하기 59 | 60 | 관리자 패널에 모델을 등록하기 위해, `blog/admin.py`로 가서 아래 코드를 추가해주세요. 61 | 62 | 63 | ```python 64 | admin.site.register(Comment) 65 | ``` 66 | 67 | 아래 코드 바로 밑에 추가하면 됩니다. 68 | 69 | ```python 70 | admin.site.register(Post) 71 | ``` 72 | 73 | 파일 맨 처음에 Comment 모델을 import 하는 것을 잊지 마세요. 소스파일은 아래와 같은 내용이어야 합니다. 74 | 75 | ```python 76 | from django.contrib import admin 77 | from .models import Post, Comment 78 | 79 | admin.site.register(Post) 80 | admin.site.register(Comment) 81 | ``` 82 | 83 | 커맨드 라인에서 `python manage.py runserver`를 입력하고 브라우저에서 [http://127.0.0.1:8000/admin/](http://127.0.0.1:8000/admin/)를 열어보세요. Comment 리스트가 보이고, 댓글을 추가 삭제할 수 있을 거예요. 주저하지 말고 댓글을 가지고 놀아보세요. 84 | 85 | ## 댓글 보여주기 86 | 87 | `blog/templates/blog/post_detail.html`로 가서 `{% endblock %}` 태그 바로 전에 아래 코드를 추가하세요. 88 | 89 | ```django 90 |
91 | {% for comment in post.comments.all %} 92 |
93 |
{{ comment.created_date }}
94 | {{ comment.author }} 95 |

{{ comment.text|linebreaks }}

96 |
97 | {% empty %} 98 |

No comments here yet :(

99 | {% endfor %} 100 | ``` 101 | 102 | 자 이제 post_detail 페이지의 댓글을 읽을 수 있어요. 103 | 104 | 105 | 좀 더 이쁘게 보이면 좋겠어요. `static/css/blog.css`파일을 열어 `css` 코드를 추가해보세요. 106 | 107 | 108 | ```css 109 | .comment { 110 | margin: 20px 0px 20px 20px; 111 | } 112 | ``` 113 | 114 | post_list 페이지에서 각 글마다 달린 댓글 개수도 보여줍시다. `blog/templates/blog/post_list.html`에 아래 코드를 추가해주세요. 115 | 116 | ```django 117 | Comments: {{ post.comments.count }} 118 | ``` 119 | 120 | 이제 아래와 같은 코드가 될 거에요. 121 | 122 | ```django 123 | {% extends 'blog/base.html' %} 124 | 125 | {% block content %} 126 | {% for post in posts %} 127 |
128 |
129 | {{ post.published_date }} 130 |
131 |

{{ post.title }}

132 |

{{ post.text|linebreaksbr }}

133 | Comments: {{ post.comments.count }} 134 |
135 | {% endfor %} 136 | {% endblock content %} 137 | ``` 138 | 139 | ## 댓글 작성하기 140 | 141 | 방문자는 댓글을 읽을 수 있지만, 직접 댓글을 남길 수는 없어요. 댓글을 작성할 수 있게 만들어 봅시다. 142 | 143 | 144 | `blog/forms.py`파일의 맨 끝에 아래 코드를 추가하세요. 145 | 146 | ```python 147 | class CommentForm(forms.ModelForm): 148 | 149 | class Meta: 150 | model = Comment 151 | fields = ('author', 'text',) 152 | ``` 153 | 154 | 파일 맨 처음에 Comment 모델을 import 하는 것을 잊지 마세요. 155 | 156 | 아래 코드를 찾아 157 | 158 | ```python 159 | from .models import Post 160 | ``` 161 | 162 | 이렇게 수정하세요. 163 | ```python 164 | from .models import Post, Comment 165 | ``` 166 | 167 | 다음으로 `blog/templates/blog/post_detail.html` 파일에서 `{% for comment in post.comments.all %}` 전에 아래 코드를 추가해주세요. 168 | 169 | ```django 170 | Add comment 171 | ``` 172 | 173 | post_detail 페이지로 가면 에러가 뜰 거에요. 174 | 175 | ![NoReverseMatch](images/url_error.png) 176 | 177 | 어떻게 고쳐야하는지 알고 있을 거에요! `blog/urls.py` 파일에서 `urlpatterns`에 새 패턴을 추가해야죠. 178 | 179 | ```python 180 | url(r'^post/(?P\d+)/comment/$', views.add_comment_to_post, name='add_comment_to_post'), 181 | ``` 182 | 183 | 페이지를 새로고침하면 또 에러가 보일 거에요! 184 | 185 | ![AttributeError](images/views_error.png) 186 | 187 | 에러를 해결하려면 `blog/views.py` 파일에서 새로운 뷰를 추가해야해요. 188 | 189 | ```python 190 | def add_comment_to_post(request, pk): 191 | post = get_object_or_404(Post, pk=pk) 192 | if request.method == "POST": 193 | form = CommentForm(request.POST) 194 | if form.is_valid(): 195 | comment = form.save(commit=False) 196 | comment.post = post 197 | comment.save() 198 | return redirect('post_detail', pk=post.pk) 199 | else: 200 | form = CommentForm() 201 | return render(request, 'blog/add_comment_to_post.html', {'form': form}) 202 | ``` 203 | 204 | 이번에도 파일 맨 처음에 `CommentForm` 을 import 하는 것을 잊지 마세요. 205 | 206 | ```python 207 | from .forms import PostForm, CommentForm 208 | ``` 209 | 210 | 이제 post_detail 페이지로 가보면, "Add comment" 버튼을 확인할 수 있을 거에요. 211 | 212 | ![AddComment](images/add_comment_button.png) 213 | 214 | 하지만, 버튼을 끌릭하면 이런 게 보이겠죠. 215 | 216 | ![TemplateDoesNotExist](images/template_error.png) 217 | 218 | 이 에러는 뷰에서 지정된 템플릿이 없다는 에러에요. `blog/templates/blog/` 경로에 `add_comment_to_post.html`이라는 새 파일을 만들고 아래 코드를 작성하세요. 219 | 220 | ```django 221 | {% extends 'blog/base.html' %} 222 | 223 | {% block content %} 224 |

New comment

225 |
{% csrf_token %} 226 | {{ form.as_p }} 227 | 228 |
229 | {% endblock %} 230 | ``` 231 | 232 | 와! 이제 내 블로그 글을 읽은 사람들이 댓글을 남길 수 있게 되었네요! 233 | 234 | ## 댓글 관리하기 235 | 236 | 현재 블로그에 남겨진 모든 댓글들이 post_detail 페이지에 보이네요. 블로그 관리자가 댓글을 승인하거나 삭제할 수 있는 기능이 필요하겠죠. 이제 이 기능을 만들어 봅시다. 237 | 238 | `blog/templates/blog/post_detail.html` 파일에서 아래 코드를 찾아 239 | 240 | ```django 241 | {% for comment in post.comments.all %} 242 |
243 |
{{ comment.created_date }}
244 | {{ comment.author }} 245 |

{{ comment.text|linebreaks }}

246 |
247 | {% empty %} 248 |

No comments here yet :(

249 | {% endfor %} 250 | ``` 251 | 252 | 이렇게 바꾸세요. 253 | 254 | ```django 255 | {% for comment in post.comments.all %} 256 | {% if user.is_authenticated or comment.approved_comment %} 257 |
258 |
259 | {{ comment.created_date }} 260 | {% if not comment.approved_comment %} 261 | 262 | 263 | {% endif %} 264 |
265 | {{ comment.author }} 266 |

{{ comment.text|linebreaks }}

267 |
268 | {% endif %} 269 | {% empty %} 270 |

No comments here yet :(

271 | {% endfor %} 272 | ``` 273 | 274 | `NoReverseMatch` 예외가 뜰 텐데요. 이를 `comment_remove`와 `comment_approve` 패턴에 매칭되는 url이 없기 때문이에요. 275 | 276 | 에러를 해결하려면 `blog/urls.py` 에 `url` 패턴을 추가해주세요. 277 | 278 | ```python 279 | url(r'^comment/(?P\d+)/approve/$', views.comment_approve, name='comment_approve'), 280 | url(r'^comment/(?P\d+)/remove/$', views.comment_remove, name='comment_remove'), 281 | ``` 282 | 283 | 이제 `AttributeError` 에러가 또 보일 거에요. 에러를 해결하기 위해, `blog/views.py` 에 뷰를 추가해주세요. 284 | 285 | ```python 286 | @login_required 287 | def comment_approve(request, pk): 288 | comment = get_object_or_404(Comment, pk=pk) 289 | comment.approve() 290 | return redirect('post_detail', pk=comment.post.pk) 291 | 292 | @login_required 293 | def comment_remove(request, pk): 294 | comment = get_object_or_404(Comment, pk=pk) 295 | comment.delete() 296 | return redirect('post_detail', pk=comment.post.pk) 297 | ``` 298 | 299 | 파일 맨 처음에 `Comment`를 import하는 것을 잊지 않았겠죠. 300 | 301 | ```python 302 | from .models import Post, Comment 303 | ``` 304 | 305 | 모든 것이 잘 작동되네요! 하지만 마지막 한 가지가 남았어요. 현재 post_list 페이지에서는 등록된 모든 댓글의 개수가 보이는데요. *승인된* 댓글의 개수만 보이게 수정해봅시다. 306 | 307 | `blog/templates/blog/post_list.html`파일에서 아래 코드를 308 | 309 | ```django 310 | Comments: {{ post.comments.count }} 311 | ``` 312 | 313 | 다음과 같이 수정해주세요. 314 | 315 | ```django 316 | Comments: {{ post.approved_comments.count }} 317 | ``` 318 | 319 | 그리고 `blog/models.py` 파일에서 `Post` 모델에 아래 메서드를 추가해주세요. 320 | 321 | ```python 322 | def approved_comments(self): 323 | return self.comments.filter(approved_comment=True) 324 | ``` 325 | 326 | 드디어 댓글 기능을 모두 만들었어요! 함께 축하해요. :-) 327 | 328 | -------------------------------------------------------------------------------- /en/heroku/README.md: -------------------------------------------------------------------------------- 1 | # Deploy to Heroku (as well as PythonAnywhere) 2 | 3 | It's always good for a developer to have a couple of different deployment options under their belt. Why not try deploying your site to Heroku, as well as PythonAnywhere? 4 | 5 | [Heroku](http://heroku.com/) is also free for small applications that don't have too many visitors, but it's a bit more tricky to get deployed. 6 | 7 | We will be following this tutorial: https://devcenter.heroku.com/articles/getting-started-with-django, but we pasted it here so it's easier for you. 8 | 9 | 10 | ## The `requirements.txt` file 11 | 12 | If you didn't create one before, we need to create a `requirements.txt` file to tell Heroku what Python packages need to be installed on our server. 13 | 14 | But first, Heroku needs us to install a few new packages. Go to your console with `virtualenv` activated and type this: 15 | 16 | (myvenv) $ pip install dj-database-url gunicorn whitenoise 17 | 18 | After the installation is finished, go to the `djangogirls` directory and run this command: 19 | 20 | (myvenv) $ pip freeze > requirements.txt 21 | 22 | This will create a file called `requirements.txt` with a list of your installed packages (i.e. Python libraries that you are using, for example Django :)). 23 | 24 | > __Note__: `pip freeze` outputs a list of all the Python libraries installed in your virtualenv, and the `>` takes the output of `pip freeze` and puts it into a file. Try running `pip freeze` without the `> requirements.txt` to see what happens! 25 | 26 | Open this file and add the following line at the bottom: 27 | 28 | psycopg2==2.7.2 29 | 30 | This line is needed for your application to work on Heroku. 31 | 32 | 33 | ## Procfile 34 | 35 | Another thing Heroku wants is a Procfile. This tells Heroku which commands to run in order to start our website. Open up your code editor, create a file called `Procfile` in `djangogirls` directory and add this line: 36 | 37 | web: gunicorn mysite.wsgi --log-file - 38 | 39 | This line means that we're going to be deploying a `web` application, and we'll do that by running the command `gunicorn mysite.wsgi` (`gunicorn` is a program that's like a more powerful version of Django's `runserver` command). 40 | 41 | Then save it. Done! 42 | 43 | ## The `runtime.txt` file 44 | 45 | We also need to tell Heroku which Python version we want to use. This is done by creating a `runtime.txt` in the `djangogirls` directory using your editor's "new file" command, and putting the following text (and nothing else!) inside: 46 | 47 | python-3.6.4 48 | 49 | ## `mysite/local_settings.py` 50 | 51 | Because it's more restrictive than PythonAnywhere, Heroku wants to use different settings from the ones we use on our locally (on our computer). Heroku wants to use Postgres while we use SQLite for example. That's why we need to create a separate file for settings that will only be available for our local environment. 52 | 53 | Go ahead and create `mysite/local_settings.py` file. It should contain your `DATABASE` setup from your `mysite/settings.py` file. Just like that: 54 | 55 | ```python 56 | import os 57 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 58 | 59 | DATABASES = { 60 | 'default': { 61 | 'ENGINE': 'django.db.backends.sqlite3', 62 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 63 | } 64 | } 65 | 66 | DEBUG = True 67 | ``` 68 | 69 | Then just save it! :) 70 | 71 | ## mysite/settings.py 72 | 73 | Another thing we need to do is modify our website's `settings.py` file. Open `mysite/settings.py` in your editor and change/add the following lines: 74 | 75 | ```python 76 | import dj_database_url 77 | 78 | ... 79 | 80 | DEBUG = False 81 | 82 | ALLOWED_HOSTS = ['127.0.0.1', '.herokuapp.com'] 83 | 84 | ... 85 | 86 | DATABASES = { 87 | 'default': { 88 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 89 | 'NAME': 'djangogirls', 90 | 'USER': 'name', 91 | 'PASSWORD': '', 92 | 'HOST': 'localhost', 93 | 'PORT': '', 94 | } 95 | } 96 | 97 | ... 98 | 99 | db_from_env = dj_database_url.config(conn_max_age=500) 100 | DATABASES['default'].update(db_from_env) 101 | ``` 102 | 103 | It'll do necessary configuration for Heroku. 104 | 105 | Then save the file. 106 | 107 | ## mysite/wsgi.py 108 | 109 | Open the `mysite/wsgi.py` file and add these lines at the end: 110 | 111 | ```python 112 | from whitenoise.django import DjangoWhiteNoise 113 | application = DjangoWhiteNoise(application) 114 | ``` 115 | 116 | All right! 117 | 118 | ## Heroku account 119 | 120 | You need to install your Heroku toolbelt which you can find here (you can skip the installation if you've already installed it during setup): https://toolbelt.heroku.com/ 121 | 122 | > When running the Heroku toolbelt installation program on Windows make sure to choose "Custom Installation" when being asked which components to install. In the list of components that shows up after that please additionally check the checkbox in front of "Git and SSH". 123 | 124 | > On Windows you also must run the following command to add Git and SSH to your command prompt's `PATH`: `setx PATH "%PATH%;C:\Program Files\Git\bin"`. Restart the command prompt program afterwards to enable the change. 125 | 126 | > After restarting your command prompt, don't forget to go to your `djangogirls` folder again and activate your virtualenv! (Hint: [Check the Django installation chapter](http://tutorial.djangogirls.org/en/django_installation/index.html#working-with-virtualenv)) 127 | 128 | Please also create a free Heroku account here: https://id.heroku.com/signup/www-home-top 129 | 130 | Then authenticate your Heroku account on your computer by running this command: 131 | 132 | $ heroku login 133 | 134 | In case you don't have an SSH key this command will automatically create one. SSH keys are required to push code to the Heroku. 135 | 136 | ## Git commit 137 | 138 | Heroku uses git for its deployments. Unlike PythonAnywhere, you can push to Heroku directly, without going via Github. But we need to tweak a couple of things first. 139 | 140 | Open the file named `.gitignore` in your `djangogirls` directory and add `local_settings.py` to it. We want git to ignore `local_settings`, so it stays on our local computer and doesn't end up on Heroku. 141 | 142 | *.pyc 143 | db.sqlite3 144 | myvenv 145 | __pycache__ 146 | local_settings.py 147 | 148 | 149 | And we commit our changes 150 | 151 | $ git status 152 | $ git add -A . 153 | $ git commit -m "additional files and changes for Heroku" 154 | 155 | 156 | ## Pick an application name 157 | 158 | We'll be making your blog available on the Web at `[your blog's name].herokuapp.com`, so we need to choose a name that nobody else has taken. This name doesn't need to be related to the Django `blog` app or to `mysite` or anything we've created so far. The name can be anything you want, but Heroku is quite strict as to what characters you can use: you're only allowed to use simple lowercase letters (no capital letters or accents), numbers, and dashes (`-`). 159 | 160 | Once you've thought of a name (maybe something with your name or nickname in it), run this command, replacing `djangogirlsblog` with your own application name: 161 | 162 | $ heroku create djangogirlsblog 163 | 164 | > __Note__: Remember to replace `djangogirlsblog` with the name of your application on Heroku. 165 | 166 | If you can't think of a name, you can instead run 167 | 168 | $ heroku create 169 | 170 | and Heroku will pick an unused name for you (probably something like `enigmatic-cove-2527`). 171 | 172 | If you ever feel like changing the name of your Heroku application, you can do so at any time with this command (replace `the-new-name` with the new name you want to use): 173 | 174 | $ heroku apps:rename the-new-name 175 | 176 | > __Note__: Remember that after you change your application's name, you'll need to visit `[the-new-name].herokuapp.com` to see your site. 177 | 178 | ## Deploy to Heroku! 179 | 180 | That was a lot of configuration and installing, right? But you only need to do that once! Now you can deploy! 181 | 182 | When you ran `heroku create`, it automatically added the Heroku remote for our app to our repository. Now we can do a simple git push to deploy our application: 183 | 184 | $ git push heroku master 185 | 186 | > __Note__: This will probably produce a *lot* of output the first time you run it, as Heroku compiles and installs psycopg. You'll know it's succeeded if you see something like `https://yourapplicationname.herokuapp.com/ deployed to Heroku` near the end of the output. 187 | 188 | ## Visit your application 189 | 190 | You’ve deployed your code to Heroku, and specified the process types in a `Procfile` (we chose a `web` process type earlier). 191 | We can now tell Heroku to start this `web process`. 192 | 193 | To do that, run the following command: 194 | 195 | $ heroku ps:scale web=1 196 | 197 | This tells Heroku to run just one instance of our `web` process. Since our blog application is quite simple, we don't need too much power and so it's fine to run just one process. It's possible to ask Heroku to run more processes (by the way, Heroku calls these processes "Dynos" so don't be surprised if you see this term) but it will no longer be free. 198 | 199 | We can now visit the app in our browser with `heroku open`. 200 | 201 | $ heroku open 202 | 203 | > __Note__: you will see an error page! We'll talk about that in a minute. 204 | 205 | This will open a url like [https://djangogirlsblog.herokuapp.com/]() in your browser, and at the moment you will probably see an error page. 206 | 207 | The error you saw was because we when we deployed to Heroku, we created a new database and it's empty. We need to run the `migrate` and `createsuperuser` commands, just like we did on PythonAnywhere. This time, they come via a special command-line on our own computer, `heroku run`: 208 | 209 | $ heroku run python manage.py migrate 210 | 211 | $ heroku run python manage.py createsuperuser 212 | 213 | The command prompt will ask you to choose a username and a password again. These will be your login details on your live website's admin page. 214 | 215 | 216 | Refresh it in your browser, and there you go! You now know how to deploy to two different hosting platforms. Pick your favourite :) 217 | -------------------------------------------------------------------------------- /es/heroku/README.md: -------------------------------------------------------------------------------- 1 | # Despliega tu sitio en Heroku (También como PythonAnywhere) 2 | 3 | Siempre es bueno como un desarrollador tiene un par de diferentes opciones de despliegue bajo su cinturón. ¿Por qué no tratar de desplegar tu sitio en Heroku, también como PythonAnywhere? 4 | 5 | [Heroku](http://heroku.com/) es también gratis para pequeñas aplicaciones que no tienen muchas visitas, pero es un poco mas complicado desplegar. 6 | 7 | Te guiaremos por el siguiente tutorial: https://devcenter.heroku.com/articles/getting-started-with-django, pero pegamos aquí para hacerlo mas fácil para ti. 8 | 9 | ## El archivo `requirements.txt` 10 | 11 | Si no lo creaste antes, necesitamos crear un archivo `requirements.txt` para decirle a Heroku que paquetes de Python necesitamos que sean instalados en nuestro servidor. 12 | 13 | Pero primero, Heroku necesita que instalemos algunos paquetes. Ve a tu consola con tu `virtualenv` activado y escribe: 14 | 15 | ``` 16 | (myvenv) $ pip install dj-database-url gunicorn whitenoise 17 | ``` 18 | 19 | Después que la instalación está finalizada, ve a la carpeta `djangogirls` y ejecuta este comando: 20 | 21 | ``` 22 | (myvenv) $ pip freeze > requirements.txt 23 | ``` 24 | 25 | Esto creará un archivo llamado `requirements.txt` con una lista de paquetes instalados. (Librerías Python que estás usando, por ejemplo Django :) 26 | 27 | >__Nota__: `pip freeze` imprime la lista de todas las librerías instaladas en tu ambiente virtual, y el `>` toma la salida de `pip freeze` y la coloca en un archivo. ¡Trata ejecutar `pip freeze` sin el `< requirements.txt` para ver lo que pasa! 28 | 29 | Abre este archivo y añada esto al final: 30 | 31 | ``` 32 | psycopg2==2.6.2 33 | ``` 34 | 35 | Esta línea se necesita para que tu aplicación funcione en Heroku. 36 | 37 | 38 | ## Procfile 39 | 40 | Otra cosa que Heroku necesita es un `Procfile`. Esto le dice a Heroku que comandos ejecutar para iniciar nuestro sitio web. Ve a tu editor de código y crea un archivo llamado `Procfile` en la carpeta `djangogirls` y agrega esta línea: 41 | 42 | ``` 43 | web: gunicorn mysite.wsgi --log-file - 44 | ``` 45 | 46 | Esta línea significa que vas a desplegar una aplicación `web` y lo vas a hacer ejecutando el comando `gunicorn mysite.wsgi` (`gunicorn` es un programa que es la versión mas poderosa del comando de django `runserver`). 47 | 48 | Entonces guardalo. ¡Listo! 49 | 50 | ## El archivo `runtime.txt` 51 | 52 | También necesitamos decirle a Heroku que versión de Python vamos a usar. Esto es hecho creando un archivo `runtime.txt` en la carpeta `djangogirls` usando tu editor de texto y colocando el siguiente texto (y nada mas) dentro: 53 | 54 | ``` 55 | python-3.5.2 56 | ``` 57 | 58 | 59 | ## `mysite/local_settings.py` 60 | 61 | Porque es mas restrictivo que PythonAnywhere, Heroku quiere usar diferentes configuraciones de las que nosotros usamos localmente (en nuestro computador). Heroku quiere que usar Postgres mientras que nosotros usamos SQLite, por ejemplo. Por eso necesitamos crear un archivo separado para las configuraciones que estarán disponibles en nuestro ambiente local. 62 | 63 | Ve adelante y crea un archivo `mysite/local_settings.py`. Este debe contener tu configuración de `DATABASE` de tu archivo `mysite/settings.py`. Justo como esto: 64 | 65 | ```python 66 | import os 67 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 68 | 69 | DATABASES = { 70 | 'default': { 71 | 'ENGINE': 'django.db.backends.sqlite3', 72 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 73 | } 74 | } 75 | 76 | DEBUG = True 77 | ``` 78 | 79 | ¡Luego guardalo! :) 80 | 81 | ## mysite/settings.py 82 | 83 | Otra cosa que debemos hacer es modificar nuestro archivo de sitio web `settings.py`. Abre `mysite/settings.py` en tu editor y cambia las siguientes líneas: 84 | 85 | ```python 86 | import dj_database_url 87 | 88 | ... 89 | 90 | DEBUG = False 91 | 92 | ALLOWED_HOSTS = ['127.0.0.1', '.herokuapp.com'] 93 | 94 | ... 95 | 96 | DATABASES = { 97 | 'default': { 98 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 99 | 'NAME': 'djangogirls', 100 | 'USER': 'name', 101 | 'PASSWORD': '', 102 | 'HOST': 'localhost', 103 | 'PORT': '', 104 | } 105 | } 106 | 107 | ... 108 | 109 | db_from_env = dj_database_url.config(conn_max_age=500) 110 | DATABASES['default'].update(db_from_env) 111 | ``` 112 | 113 | Esto hará la configuración necesaria para Heroku. 114 | 115 | Luego guarda el archivo. 116 | 117 | ## mysite/wsgi.py 118 | 119 | Abre el archivo `mysite/wsgi.py` y agrega estas líneas al final: 120 | 121 | ```python 122 | from whitenoise.django import DjangoWhiteNoise 123 | application = DjangoWhiteNoise(application) 124 | ``` 125 | 126 | ¡Todo bien! 127 | 128 | ## Cuenta Heroku 129 | 130 | Necesitas instalar `Heroku toolbelt` el cual encontrarás aquí (puedes saltarte la instalación si ya lo instalaste durante la configuración): https://toolbelt.heroku.com/ 131 | 132 | > Cuando ejecutas la instalación de Heroku toolbelt en windows asegúrate de escoger "Instalación personalizada" cuando te pregunten que componentes instalar. En la lista de componentes que se muestra luego por favor selecciona "Git y SSH" 133 | 134 | > En windows también debes ejecutar el siguiente comando para agregar Git y SSH a tu `PATH` de la línea de comanods: `setx PATH "%PATH%;C:\Program Files\Git\bin"`. Reinicia la línea de comandos después para habilitar el cambio. 135 | 136 | > ¡Después de reiniciar tu línea de comandos, no olvides ir a tu carpeta `djangogirls` de nuevo y habilitar el ambiente virtual! (Truco: Ve al [capítulo de instalación de Django](http://tutorial.djangogirls.org/en/django_installation/index.html#working-with-virtualenv)) 137 | 138 | Por favor también crea una cuenta gratuita de Heroku aquí: https://id.heroku.com/signup/www-home-top 139 | 140 | Luego autenticate con tu cuenta Heroku en tu computador ejecutando este comando: 141 | 142 | ``` 143 | $ heroku login 144 | ``` 145 | 146 | En cas que no tengas una llave SSH este comando automáticamente creará una. Las llaves SSH son requeridas para colocar código en Heroku. 147 | 148 | ## Git commit 149 | 150 | Heroku usa git para sus propios despliegues. A diferencia de PythonAnywhere, puedes hacer push a Heroku directamente, sin Github. Pero necesitamos hacer un par de cosas antes. 151 | 152 | Abre un archivo llamado `.gitignore` en tu carpeta `djangogirls` y agrega `local_settings.py` a el. Queremos que git ignore `local_settings`, entonces este permanece en nuestro computador local y no termina en Heroku. 153 | Open the file named `.gitignore` in your `djangogirls` directory and add `local_settings.py` 154 | 155 | ``` 156 | *.pyc 157 | db.sqlite3 158 | myvenv 159 | __pycache__ 160 | local_settings.py 161 | ``` 162 | 163 | Y guarda los cambios 164 | 165 | ``` 166 | $ git status 167 | $ git add -A . 168 | $ git commit -m "additional files and changes for Heroku" 169 | ``` 170 | 171 | ## Escoge un nombre de aplicación 172 | 173 | Vamos a hacer que tu blog esté disponible en la web en `[tu nombre de blog].herokuapp.com` así vamos a escoger un nombre que nadie mas haya tomado. Este nombre no tiene que estar relacionado con el blog de `Django` o `mysite` o nada de lo que hemos creado hasta ahora. El nombre debe estar en minúscula (sin letras mayúsculas o acentos), nombres y guiones (`-`). 174 | 175 | Una ves tengas un nombre (tal vez algo con tu nombre o nick en el), ejecuta este comando, reemplazaod `djangogirlsblo` con tu propio nombre de aplicación: 176 | 177 | ``` 178 | $ heroku create djangogirlsblog 179 | ``` 180 | 181 | > __Nota__: Recuerda reemplazar `djangogirlsblog` con el nombre de tu aplicación en Heroku. 182 | 183 | Si no puedes pensar en un nombre, puedes en lugar ejecutar: 184 | 185 | ``` 186 | $ heroku create 187 | ``` 188 | 189 | Y heroku escogerá un nombre no disponible por ti (posiblemente algo como `enigmatic-cove-2527`). 190 | 191 | Si tu alguna vez sientes que debes cambiar el nombre de tu aplicación Heroku, puedes hacerlo en cualquier momento con est eomando (reemplaza `the-new-game` con el nuevo nombre que quieres usar): 192 | 193 | ``` 194 | $ heroku apps:rename the-new-name 195 | ``` 196 | 197 | > __Nota__: Recuerda que luego que cambias el nombre de tu aplicación, necesitas visitar `[the-new-name].herokuapp.com` para ver tu sitio 198 | 199 | ## ¡Despliega a Heroku! 200 | 201 | Esto fue mucho de configuración e instalación, ¿cierto? ¡Pero esto solo lo necesitas hacer una vez! ¡Ahora puedes desplegar! 202 | 203 | Cuando tu ejecutas `heroku create`, el automáticamente agregará un `remote` de Heroku a nuestro repositorio de aplicaciones. Ahora simplemente necesitamos hacer push a Heroku para desplegar nuestra aplicación: 204 | 205 | ``` 206 | $ git push heroku master 207 | ``` 208 | 209 | > __Nota__: Esto posiblemente produzca *un montón* de salida si es la primera vez que lo ejecutas, como Heroku compila e instala psycopg. Verás que tu comando funcionó si ves algo como `https://yourapplicationname.herokuapp.com/ deployed to Heroku` cerca al final de output. 210 | 211 | ## Visita tu aplicación 212 | 213 | Tu has desplegado tu código a Heroku, y especificado los tipos de proceso en un archivo `Procfile` (nosotros escogimos un proceso `web` anteriormente). Ahora puedes decirle a Heroku que inicie este proceso `web` 214 | 215 | Para hacerlo, ejecuta el siguiente comando: 216 | 217 | ``` 218 | $ heroku ps:scale web=1 219 | ``` 220 | 221 | Esto le dice a Heroku que ejecute solamente una instancia de nuestro proceso `web`. Desde que nuestra aplicación es muy simple, nosotros no necesitamos mucho poder, y por eso está bien un solo proceso. Es posible solicitar a Heroku que ejecute mas procesos (por cierto, Heroku llama a estos procesos "Dynos" así que no te sorprendas si ves estos términos) pero ya no serán gratis. 222 | 223 | Puedes visitar la app en tu navegador ejecutando `heroku open`. 224 | 225 | ``` 226 | $ heroku open 227 | ``` 228 | 229 | > __Nota__: ¡verás una página de error! Hablaremos de eso en un minuto. 230 | 231 | Esto abrirá una url como [https://djangogirlsblog.herokuapp.com/]() en tu navegador, y por el momento, posiblemente veas una página de error. 232 | 233 | El error que ves es porque cuando desplegamos a Heroku, creamos una nueva base de datos y está vacía. Necesitamos ejecutar los comandos `migrate` y `createsuperuser`, justo como hicimos en PythonAnywhere. Esta vez, ellos vienen en una versión especial en nuestro computador, `heroku run`: 234 | 235 | ``` 236 | $ heroku run python manage.py migrate 237 | 238 | $ heroku run python manage.py createsuperuser 239 | ``` 240 | 241 | Este comando te preguntará que escojas un nombre de usuario y contraseña de nuevo. Estas serán tus credenciales de acceso a la página de administración de tu sitio. 242 | 243 | ¡Refresca en tu navegador y ahí estás! Ahora sabes como desplegar a dos diferentes plataformas de hosting. Escoge tu favorita :) 244 | -------------------------------------------------------------------------------- /en/homework_create_more_models/README.md: -------------------------------------------------------------------------------- 1 | # Homework: create comment model 2 | 3 | Currently, we only have a Post model. What about receiving some feedback from your readers and letting them comment? 4 | 5 | ## Creating comment blog model 6 | 7 | Let's open `blog/models.py` and append this piece of code to the end of file: 8 | 9 | ```python 10 | class Comment(models.Model): 11 | post = models.ForeignKey('blog.Post', on_delete=models.CASCADE, related_name='comments') 12 | author = models.CharField(max_length=200) 13 | text = models.TextField() 14 | created_date = models.DateTimeField(default=timezone.now) 15 | approved_comment = models.BooleanField(default=False) 16 | 17 | def approve(self): 18 | self.approved_comment = True 19 | self.save() 20 | 21 | def __str__(self): 22 | return self.text 23 | ``` 24 | 25 | You can go back to the **Django models** chapter in the tutorial if you need a refresher on what each of the field types mean. 26 | 27 | In this tutorial extension we have a new type of field: 28 | - `models.BooleanField` - this is true/false field. 29 | 30 | The `related_name` option in `models.ForeignKey` allows us to have access to comments from within the Post model. 31 | 32 | ## Create tables for models in your database 33 | 34 | Now it's time to add our comment model to the database. To do this we have to tell Django that we made changes to our model. Type `python manage.py makemigrations blog` in your command line. You should see output like this: 35 | 36 | (myvenv) ~/djangogirls$ python manage.py makemigrations blog 37 | Migrations for 'blog': 38 | 0002_comment.py: 39 | - Create model Comment 40 | 41 | You can see that this command created another migration file for us in the `blog/migrations/` directory. Now we need to apply those changes by typing `python manage.py migrate blog` in the command line. The output should look like this: 42 | 43 | ``` 44 | (myvenv) ~/djangogirls$ python manage.py migrate blog 45 | Operations to perform: 46 | Apply all migrations: blog 47 | Running migrations: 48 | Rendering model states... DONE 49 | Applying blog.0002_comment... OK 50 | ``` 51 | 52 | Our Comment model exists in the database now! Wouldn't it be nice if we had access to it in our admin panel? 53 | 54 | ## Register Comment model in admin panel 55 | 56 | To register the Comment model in the admin panel, go to `blog/admin.py` and add this line: 57 | 58 | ```python 59 | admin.site.register(Comment) 60 | ``` 61 | 62 | directly under this line: 63 | 64 | ```python 65 | admin.site.register(Post) 66 | ``` 67 | 68 | Remember to import the Comment model at the top of the file, too, like this: 69 | 70 | ```python 71 | from django.contrib import admin 72 | from .models import Post, Comment 73 | 74 | admin.site.register(Post) 75 | admin.site.register(Comment) 76 | ``` 77 | 78 | If you type `python manage.py runserver` on the command line and go to [http://127.0.0.1:8000/admin/](http://127.0.0.1:8000/admin/) in your browser, you should have access to the list of comments, and also the capability to add and remove comments. Play around with the new comments feature! 79 | 80 | ## Make our comments visible 81 | 82 | Go to the `blog/templates/blog/post_detail.html` file and add the following lines before the `{% endblock %}` tag: 83 | 84 | ```django 85 |
86 | {% for comment in post.comments.all %} 87 |
88 |
{{ comment.created_date }}
89 | {{ comment.author }} 90 |

{{ comment.text|linebreaks }}

91 |
92 | {% empty %} 93 |

No comments here yet :(

94 | {% endfor %} 95 | ``` 96 | 97 | Now we can see the comments section on pages with post details. 98 | 99 | But it could look a little bit better, so let's add some CSS to the bottom of the `static/css/blog.css` file: 100 | 101 | ```css 102 | .comment { 103 | margin: 20px 0px 20px 20px; 104 | } 105 | ``` 106 | 107 | We can also let visitors know about comments on the post list page. Go to the `blog/templates/blog/post_list.html` file and add the line: 108 | 109 | ```django 110 | Comments: {{ post.comments.count }} 111 | ``` 112 | 113 | After that our template should look like this: 114 | 115 | ```django 116 | {% extends 'blog/base.html' %} 117 | 118 | {% block content %} 119 | {% for post in posts %} 120 |
121 |
122 | {{ post.published_date }} 123 |
124 |

{{ post.title }}

125 |

{{ post.text|linebreaksbr }}

126 | Comments: {{ post.comments.count }} 127 |
128 | {% endfor %} 129 | {% endblock content %} 130 | ``` 131 | 132 | ## Let your readers write comments 133 | 134 | Right now we can see comments on our blog, but we can't add them. Let's change that! 135 | 136 | Go to `blog/forms.py` and add the following lines to the end of the file: 137 | 138 | ```python 139 | class CommentForm(forms.ModelForm): 140 | 141 | class Meta: 142 | model = Comment 143 | fields = ('author', 'text',) 144 | ``` 145 | 146 | Remember to import the Comment model, changing the line: 147 | 148 | ```python 149 | from .models import Post 150 | ``` 151 | 152 | into: 153 | 154 | ```python 155 | from .models import Post, Comment 156 | ``` 157 | 158 | Now, go to `blog/templates/blog/post_detail.html` and before the line `{% for comment in post.comments.all %}`, add: 159 | 160 | ```django 161 | Add comment 162 | ``` 163 | 164 | If you go to the post detail page you should see this error: 165 | 166 | ![NoReverseMatch](images/url_error.png) 167 | 168 | We know how to fix that! Go to `blog/urls.py` and add this pattern to `urlpatterns`: 169 | 170 | ```python 171 | path('post//comment/', views.add_comment_to_post, name='add_comment_to_post'), 172 | ``` 173 | 174 | Refresh the page, and we get a different error! 175 | 176 | ![AttributeError](images/views_error.png) 177 | 178 | To fix this error, add this view to `blog/views.py`: 179 | 180 | ```python 181 | def add_comment_to_post(request, pk): 182 | post = get_object_or_404(Post, pk=pk) 183 | if request.method == "POST": 184 | form = CommentForm(request.POST) 185 | if form.is_valid(): 186 | comment = form.save(commit=False) 187 | comment.post = post 188 | comment.save() 189 | return redirect('post_detail', pk=post.pk) 190 | else: 191 | form = CommentForm() 192 | return render(request, 'blog/add_comment_to_post.html', {'form': form}) 193 | ``` 194 | 195 | Remember to import `CommentForm` at the beginning of the file: 196 | 197 | ```python 198 | from .forms import PostForm, CommentForm 199 | ``` 200 | 201 | 202 | Now, on the post detail page, you should see the "Add Comment" button. 203 | 204 | ![AddComment](images/add_comment_button.png) 205 | 206 | However, when you click that button, you'll see: 207 | 208 | ![TemplateDoesNotExist](images/template_error.png) 209 | 210 | 211 | Like the error tells us, the template doesn't exist yet. So, let's create a new one at `blog/templates/blog/add_comment_to_post.html` and add the following code: 212 | 213 | ```django 214 | {% extends 'blog/base.html' %} 215 | 216 | {% block content %} 217 |

New comment

218 |
{% csrf_token %} 219 | {{ form.as_p }} 220 | 221 |
222 | {% endblock %} 223 | ``` 224 | 225 | Yay! Now your readers can let you know what they think of your blog posts! 226 | 227 | ## Moderating your comments 228 | 229 | Not all of the comments should be displayed. As the blog owner, you probably want the option to approve or delete comments. Let's do something about it. 230 | 231 | Go to `blog/templates/blog/post_detail.html` and change lines: 232 | 233 | ```django 234 | {% for comment in post.comments.all %} 235 |
236 |
{{ comment.created_date }}
237 | {{ comment.author }} 238 |

{{ comment.text|linebreaks }}

239 |
240 | {% empty %} 241 |

No comments here yet :(

242 | {% endfor %} 243 | ``` 244 | 245 | to: 246 | 247 | ```django 248 | {% for comment in post.comments.all %} 249 | {% if user.is_authenticated or comment.approved_comment %} 250 |
251 |
252 | {{ comment.created_date }} 253 | {% if not comment.approved_comment %} 254 | 255 | 256 | {% endif %} 257 |
258 | {{ comment.author }} 259 |

{{ comment.text|linebreaks }}

260 |
261 | {% endif %} 262 | {% empty %} 263 |

No comments here yet :(

264 | {% endfor %} 265 | ``` 266 | 267 | You should see `NoReverseMatch`, because no URL matches the `comment_remove` and `comment_approve` patterns... yet! 268 | 269 | To fix the error, add these URL patterns to `blog/urls.py`: 270 | 271 | ```python 272 | path('comment//approve/', views.comment_approve, name='comment_approve'), 273 | path('comment//remove/', views.comment_remove, name='comment_remove'), 274 | ``` 275 | 276 | Now, you should see `AttributeError`. To fix this error, add these views in `blog/views.py`: 277 | 278 | ```python 279 | @login_required 280 | def comment_approve(request, pk): 281 | comment = get_object_or_404(Comment, pk=pk) 282 | comment.approve() 283 | return redirect('post_detail', pk=comment.post.pk) 284 | 285 | @login_required 286 | def comment_remove(request, pk): 287 | comment = get_object_or_404(Comment, pk=pk) 288 | comment.delete() 289 | return redirect('post_detail', pk=comment.post.pk) 290 | ``` 291 | 292 | 293 | You'll need to import `Comment` at the top of the file: 294 | 295 | ```python 296 | from .models import Post, Comment 297 | ``` 298 | 299 | Everything works! There is one small tweak we can make. In our post list page -- under posts -- we currently see the number of all the comments the blog post has received. Let's change that to show the number of *approved* comments there. 300 | 301 | To fix this, go to `blog/templates/blog/post_list.html` and change the line: 302 | 303 | ```django 304 | Comments: {{ post.comments.count }} 305 | ``` 306 | 307 | to: 308 | 309 | ```django 310 | Comments: {{ post.approved_comments.count }} 311 | ``` 312 | 313 | Finally, add this method to the `Post` model in `blog/models.py`: 314 | 315 | ```python 316 | def approved_comments(self): 317 | return self.comments.filter(approved_comment=True) 318 | ``` 319 | 320 | Now your comment feature is finished! Congrats! :-) 321 | -------------------------------------------------------------------------------- /es/homework_create_more_models/README.md: -------------------------------------------------------------------------------- 1 | # Tarea: Crea un modelo de comentarios 2 | 3 | Actualmente, solamente tenemos un modelo de Post, ¿Qué si queremos recibir retro alimentación de tus lectores y dejarlos comentar? 4 | 5 | ## Crea un modelo de comentarios para el blog 6 | 7 | Vamos a abrir `blog/models.py` y pega esta pieza de código al final del archivo: 8 | 9 | ```python 10 | class Comment(models.Model): 11 | post = models.ForeignKey('blog.Post', on_delete=models.CASCADE, related_name='comments') 12 | author = models.CharField(max_length=200) 13 | text = models.TextField() 14 | created_date = models.DateTimeField(default=timezone.now) 15 | approved_comment = models.BooleanField(default=False) 16 | 17 | def approve(self): 18 | self.approved_comment = True 19 | self.save() 20 | 21 | def __str__(self): 22 | return self.text 23 | ``` 24 | 25 | Puedes ir atrás al modelo **Modelos en Django** de este tutorial si necesitas refrescar lo que cada tipo de campo significa. 26 | 27 | En esta extensión del tutorial vamos a tener un nuevo tipo de campo: 28 | 29 | - `models.BooleanField` - this is true/false field. 30 | 31 | La opción `related_name` en `models.ForeignKey` nos permite acceder acceso a los comentarios desde el modelo Post. 32 | 33 | ## Creando tablas para los modelos en tu base de datos. 34 | 35 | Es tiempo de agregar nuestro modelo de comentarios a la base de datos. Para hacer esto le vamos a decir a Django que haga los cambios a nuestro modelo. Escribe `python manage.py makemigrations blog` en tu linea de comendos. Deberías ver algo como esto: 36 | 37 | ``` 38 | (myvenv) ~/djangogirls$ python manage.py makemigrations blog 39 | Migrations for 'blog': 40 | 0002_comment.py: 41 | - Create model Comment 42 | ``` 43 | 44 | Como puedes ver, se ha creado otro archivo de migración en la carpeta `blog/migrations/`. Ahora necesitamos aplicar estos cambios escribiendo `python manage.py migrate blog` en la línea de comandos. La salida debería verse así: 45 | 46 | ``` 47 | (myvenv) ~/djangogirls$ python manage.py migrate blog 48 | Operations to perform: 49 | Apply all migrations: blog 50 | Running migrations: 51 | Rendering model states... DONE 52 | Applying blog.0002_comment... OK 53 | ``` 54 | 55 | ¡Nuestro modelo de comentarios existe ahora en la base de datos! ¿No sería genial acceder a el desde nuestro panel de administración? 56 | 57 | ## Registra el modelo de comentarios en el panel de administrador 58 | 59 | Para registrar el modelo de Comentario en el panel de administrador, ve a `blog/admin.py` y agrega esta línea: 60 | 61 | ```python 62 | admin.site.register(Comment) 63 | ``` 64 | Justo debajo de esta línea: 65 | 66 | ```python 67 | admin.site.register(Post) 68 | ``` 69 | 70 | Recuerda que es importante importar el modelo de comentarios al comienzo del archivo, como esto: 71 | 72 | ```python 73 | from django.contrib import admin 74 | from .models import Post, Comment 75 | 76 | admin.site.register(Post) 77 | admin.site.register(Comment) 78 | ``` 79 | 80 | Si escribes `python manage.py runserver` en la línea de comandos y vas a [http://127.0.0.1:8000/admin/](http://127.0.0.1:8000/admin/) en tu navegador, ahora podras acceder a la lista de comentarios, y también tendrás la posibilidad de agregar y eliminar comentarios. ¡Juega con la nueva característica de comentarios! 81 | 82 | ## Has tus comentarios visibles 83 | 84 | Ve al archivo `blog/templates/blog/post_detail.html` y agrega el siguiente código antes de la etiqueta `{% endblock %}`: 85 | 86 | ```django 87 |
88 | {% for comment in post.comments.all %} 89 |
90 |
{{ comment.created_date }}
91 | {{ comment.author }} 92 |

{{ comment.text|linebreaks }}

93 |
94 | {% empty %} 95 |

No comments here yet :(

96 | {% endfor %} 97 | ``` 98 | 99 | Ahora puedes ver la sección de comentarios en los detalles del post. 100 | 101 | Pero puede verse un poco mejor, así que vamos a agregar algún css al final del archivo `static/css/blog.css`: 102 | 103 | ```css 104 | .comment { 105 | margin: 20px 0px 20px 20px; 106 | } 107 | ``` 108 | 109 | También vamos a dejar a los visitantes sobre los comentarios que dejan en la página. Ve al archivo `blog/templates/blog/post_list.html` y agrega una lína como esta: 110 | 111 | ```django 112 | Comments: {{ post.comments.count }} 113 | ``` 114 | 115 | Después de esto, tu plantilla debería verse así: 116 | 117 | ```django 118 | {% extends 'blog/base.html' %} 119 | 120 | {% block content %} 121 | {% for post in posts %} 122 |
123 |
124 | {{ post.published_date }} 125 |
126 |

{{ post.title }}

127 |

{{ post.text|linebreaksbr }}

128 | Comments: {{ post.comments.count }} 129 |
130 | {% endfor %} 131 | {% endblock content %} 132 | ``` 133 | 134 | ## Deja a tus lectores escribir comentarios 135 | 136 | Ahora podemos ver los comentarios hechos en nuestro blog, pero no podemos agregarlos. ¡Vamos a cambiar eso! 137 | 138 | Ve a `blog/forms.py` y agrega las siguientes líneas al final del archivo: 139 | 140 | ```python 141 | class CommentForm(forms.ModelForm): 142 | 143 | class Meta: 144 | model = Comment 145 | fields = ('author', 'text',) 146 | ``` 147 | 148 | Recuerda importar el modelo Comentario, cambiando la línea: 149 | 150 | ```python 151 | from .models import Post 152 | ``` 153 | 154 | en: 155 | 156 | ```python 157 | from .models import Post, Comment 158 | ``` 159 | 160 | Ahora vamos a `blog/templates/blog/post_detail.html` y antes de la línea `{% for comment in post.comments.all %}`, agrega: 161 | 162 | ```django 163 | Add comment 164 | ``` 165 | 166 | Si quieres ir a los detalles del post, deberías ver este error: 167 | 168 | ![NoReverseMatch](images/url_error.png) 169 | 170 | ¡Ahora sabemos como arreglarlo! Ve a `blog/urls.py` y agrega esto a `urlpatterns`: 171 | 172 | ```python 173 | url(r'^post/(?P\d+)/comment/$', views.add_comment_to_post, name='add_comment_to_post'), 174 | ``` 175 | 176 | ¡Refresca la página y ahora tenemos un erro diferente! 177 | 178 | ![AttributeError](images/views_error.png) 179 | 180 | Para agregar este error, agrega esto a `blog/views.py`: 181 | 182 | ```python 183 | def add_comment_to_post(request, pk): 184 | post = get_object_or_404(Post, pk=pk) 185 | if request.method == "POST": 186 | form = CommentForm(request.POST) 187 | if form.is_valid(): 188 | comment = form.save(commit=False) 189 | comment.post = post 190 | comment.save() 191 | return redirect('post_detail', pk=post.pk) 192 | else: 193 | form = CommentForm() 194 | return render(request, 'blog/add_comment_to_post.html', {'form': form}) 195 | ``` 196 | 197 | Recuerda importar `CommentForm` al comienzo del archivo: 198 | 199 | ```python 200 | from .forms import PostForm, CommentForm 201 | ``` 202 | 203 | Ahora vamos a los detalles de la página y deberíamos ver el botón "Add Comment": 204 | 205 | ![AddComment](images/add_comment_button.png) 206 | 207 | Sin embargo, cuando des click en el botón verás: 208 | 209 | ![TemplateDoesNotExist](images/template_error.png) 210 | 211 | Como el error nos dice, la plantilla no existe aún. Entonces vamos a crear una en `blog/templates/blog/add_comment_to_post.html` y agregar el siguiente código: 212 | 213 | ```django 214 | {% extends 'blog/base.html' %} 215 | 216 | {% block content %} 217 |

New comment

218 |
{% csrf_token %} 219 | {{ form.as_p }} 220 | 221 |
222 | {% endblock %} 223 | ``` 224 | 225 | ¡Genial! !Ahora nuestroe lectores puede agregar lo que piensan en nuestros post! 226 | 227 | ## Moderando los comentarios 228 | 229 | No todos los comentarios deberían ser mostrados. Como dueño del blog, posiblemtente quieras la opción de aprovar o eliminar comentarios. Vamos a hacer algo sobre esto. 230 | 231 | Ve a `blog/templates/blog/post_detail.html` y cambia las líneas: 232 | 233 | ```django 234 | {% for comment in post.comments.all %} 235 |
236 |
{{ comment.created_date }}
237 | {{ comment.author }} 238 |

{{ comment.text|linebreaks }}

239 |
240 | {% empty %} 241 |

No comments here yet :(

242 | {% endfor %} 243 | ``` 244 | 245 | a: 246 | 247 | ```django 248 | {% for comment in post.comments.all %} 249 | {% if user.is_authenticated or comment.approved_comment %} 250 |
251 |
252 | {{ comment.created_date }} 253 | {% if not comment.approved_comment %} 254 | 255 | 256 | {% endif %} 257 |
258 | {{ comment.author }} 259 |

{{ comment.text|linebreaks }}

260 |
261 | {% endif %} 262 | {% empty %} 263 |

No comments here yet :(

264 | {% endfor %} 265 | ``` 266 | 267 | Deberías ver un `NoReverseMatch`, porque no hay ninguna URL que concuerde con `comment_remove` y `comment_approve` aún 268 | 269 | Para arreglar este error, agregemos estas urls a `blog/urls.py`: 270 | 271 | ```python 272 | url(r'^comment/(?P\d+)/approve/$', views.comment_approve, name='comment_approve'), 273 | url(r'^comment/(?P\d+)/remove/$', views.comment_remove, name='comment_remove'), 274 | ``` 275 | 276 | Ahora debereias ver un `AttributeError`. Para arreglar este error, agrega las siguiente vistas en `blog/views.py`: 277 | 278 | ```python 279 | @login_required 280 | def comment_approve(request, pk): 281 | comment = get_object_or_404(Comment, pk=pk) 282 | comment.approve() 283 | return redirect('post_detail', pk=comment.post.pk) 284 | 285 | @login_required 286 | def comment_remove(request, pk): 287 | comment = get_object_or_404(Comment, pk=pk) 288 | comment.delete() 289 | return redirect('post_detail', pk=comment.post.pk) 290 | ``` 291 | 292 | Necesitas importar `Comment` al comienzo del archivo: 293 | 294 | ```python 295 | from .models import Post, Comment 296 | ``` 297 | 298 | ¡Todo funciona! Hay un pequeño cambio que podemos hacer. en nuestra página de lista -- debajo de posts -- actualmente vemos el número de los comentarios que el post ha recibifo. Vamos a cambiar esto para ver el número de comentarios aprobados. 299 | 300 | Para arreglar esto, ve a `blog/templates/blog/post_list.html` y cambia la línea: 301 | 302 | ```django 303 | Comments: {{ post.comments.count }} 304 | ``` 305 | 306 | a: 307 | 308 | ```django 309 | Comments: {{ post.approved_comments.count }} 310 | ``` 311 | 312 | finalmente, añade el método al modelo `Post` en `blog/models.py`: 313 | 314 | ```python 315 | def approved_comments(self): 316 | return self.comments.filter(approved_comment=True) 317 | ``` 318 | Ahora puedes ver la característica de comentarios finalizada ¡Felicitaciones! :-) 319 | -------------------------------------------------------------------------------- /en/manual_pythonanywhere_deploy/README.md: -------------------------------------------------------------------------------- 1 | # Deploying to PythonAnywhere manually 2 | 3 | In the main tutorial, we deployed our app using PythonAnywhere's "autoconfigure" script, which did a lot of magic for us. In this extension we'll take a peek "behind the scenes" and find out what autoconfigure script actually did, by learning how to deploy our code manually to PythonAnywhere. 4 | 5 | Why might you be interested in doing this? Beyond pure curiosity, the steps involved in a manual deployment are more or less the same steps you'd need to go through if you were trying to deploy to a different hosting provider, including running your own server, which is something you might want to do some day. 6 | 7 | Running through the procedure manually is also a good idea for DjangoGirls coaches. If something goes wrong with the autoconfigure script in one of the workshops, knowing how to do it manually could help you get one of the attendees out of a broken state and back to a working deployment. 8 | 9 | So, read on! 10 | 11 | 12 | ## Assumptions 13 | 14 | * This extension assumes you already have some code on GitHub. See the begining of the deploy chapter for instructions on that, if you need. 15 | * It also assumes you have a PythonAnywhere account. If it already has a djangogirls webapp running in it, you'll need to delete that first (or just sign up for a new account!) 16 | 17 | ## Overview 18 | 19 | Deploying to PythonAnywhere, or indeed to any server, involves pretty much the same steps: 20 | 21 | * Getting your code onto the server. We'll use `git clone` and `git pull` for this 22 | 23 | * Installing your dependencies on the server. We'll use a virtualenv for this, just like on your own computer, but we'll use a shortcut recommended by PythonAnywhere called `virtualenvwrapper` 24 | 25 | * Configuring your database on the server. The database on your own computer and the one on the server are separate. We'll use `manage.py migrate` and `manage.py createsuperuser` for this. 26 | 27 | * Configuring your static files on the server. On your own laptop, `runserver` takes care of static files, but servers like to manage things differently in order to optimise serving static files. Here we'll use a new command called `collectstatic`, and configure the static files through PythonAnywhere's **Web tab**. 28 | 29 | * And actually hooking up our Django app to be served, live, on the public Internet. We do this on PythonAnywhere's **Web tab**, by configuring something they call a **WSGI file**. 30 | 31 | 32 | ## Pulling our code down on PythonAnywhere 33 | 34 | Go to the [PythonAnywhere Dashboard](https://www.pythonanywhere.com), and choose the option to start a "Bash" console – that's the PythonAnywhere version of a command-line, just like the one on your computer. 35 | 36 | pointing at Other: Bash in Start a new Console 37 | 38 | > **Note** PythonAnywhere is based on Linux, so if you're on Windows, the console will look a little different from the one on your computer. 39 | 40 | Let's pull down our code from GitHub and onto PythonAnywhere by creating a "clone" of our repo. Type the following into the console on PythonAnywhere: 41 | 42 | {% filename %}PythonAnywhere command-line{% endfilename %} 43 | ``` 44 | $ git clone https://github.com//my-first-blog.git .pythonanywhere.com 45 | ``` 46 | 47 | * Use your actual GitHub username in place of `` 48 | * And use your actual PythonAnywhere username in place of ``) 49 | 50 | This will pull down a copy of your code onto PythonAnywhere, and put it into a folder named after your (future) website address. Check it out by typing `tree`: 51 | 52 | {% filename %}PythonAnywhere command-line{% endfilename %} 53 | ``` 54 | $ tree 55 | ola.pythonanywhere.com/ 56 | ├── blog 57 | │ ├── __init__.py 58 | │ ├── admin.py 59 | │ ├── migrations 60 | │ │ ├── 0001_initial.py 61 | │ │ └── __init__.py 62 | │ ├── models.py 63 | │ ├── tests.py 64 | │ └── views.py 65 | ├── manage.py 66 | └── mysite 67 | ├── __init__.py 68 | ├── settings.py 69 | ├── urls.py 70 | └── wsgi.py 71 | ``` 72 | 73 | 74 | ### Creating a virtualenv on PythonAnywhere 75 | 76 | Just like you did on your own computer, you need to create a virtualenv on PythonAnywhere. In the Bash console, type: 77 | 78 | {% filename %}PythonAnywhere command-line{% endfilename %} 79 | ``` 80 | $ mkvirtualenv --python=python3.6 .pythonanywhere.com 81 | Running virtualenv with interpreter /usr/bin/python3.6 82 | [...] 83 | Installing setuptools, pip...done. 84 | ``` 85 | 86 | `mkvirtualenv` comes from a tool called "virtualenvwrapper", which PythonAnywhere recommends. It's a set of shortcuts built around the normal `virtualenv` command that you've already learned about using on your own computer. 87 | 88 | When the command finishes, your virtualenv should be active; we've named it after your future website address, just like we named the project source code folder. Let's try de-activating and re-activating the virtualenv, just for practice. 89 | 90 | Deactivating is `deactivate`, just like on your computer, but to activate we use the virtualenvwrapper shortcut command `workon`, which just needs the name of your virtualenv: 91 | 92 | {% filename %}PythonAnywhere command-line{% endfilename %} 93 | ``` 94 | (ola.pythonanywhere.com) $ deactivate 95 | $ which python 96 | /usr/bin/python 97 | $ workon .pythonanywhere.com 98 | (ola.pythonanywhere.com) $ which python 99 | /home/ola/.virtualenvs/ola.pythonanywhere.com/bin/python 100 | ``` 101 | 102 | Now let's install Django into our virtualenv on PythonAnywhere 103 | 104 | {% filename %}PythonAnywhere command-line{% endfilename %} 105 | ``` 106 | (ola.pythonanywhere.com) $ pip install 'django<2' 107 | Collecting django 108 | [...] 109 | Successfully installed django-1.11.9 110 | ``` 111 | 112 | > **Note** The `pip install` step can take a couple of minutes. Patience, patience! But if it takes more than five minutes, something is wrong. Ask your coach. 113 | 114 | 115 | 116 | 117 | ### Creating the database on PythonAnywhere 118 | 119 | Here's another thing that's different between your own computer and the server: it uses a different database. So the user accounts and posts can be different on the server and on your computer. 120 | 121 | Just as we did on your own computer, we repeat the step to initialize the database on the server, with `migrate` and `createsuperuser`: 122 | 123 | Go back to your Bash console, make sure your virtualenv is still active, and then run these commands. (If you've closed your console, you can open a new one, and use the `workon` command to activate your virtualenv). 124 | 125 | {% filename %}PythonAnywhere command-line{% endfilename %} 126 | ``` 127 | (ola.pythonanywhere.com) $ python manage.py migrate 128 | Operations to perform: 129 | [...] 130 | Applying sessions.0001_initial... OK 131 | (ola.pythonanywhere.com) $ python manage.py createsuperuser 132 | ``` 133 | 134 | ### Collecting static files 135 | 136 | Now we learn a new command, `collecstatic`, whose job it is to collect all the static files from your apps (including apps you've written like *blog*, and built-in Django apps like the *admin*), and put them in one place, so the server can find them: 137 | 138 | {% filename %}PythonAnywhere command-line{% endfilename %} 139 | ``` 140 | (ola.pythonanywhere.com) $ python manage.py collectstatic 141 | You have requested to collect static files at the destination 142 | [...] 143 | Are you sure you want to do this? 144 | [...] 145 | 62 static files copied to '/home/ola/ola.pythonanywhere.com/static'. 146 | ``` 147 | 148 | 149 | ## Publishing our blog as a web app 150 | 151 | Now our code is on PythonAnywhere, our virtualenv is ready, and the database is initialized. We're ready to publish it as a web app! 152 | 153 | Click back to the PythonAnywhere dashboard by clicking on its logo, and then click on the **Web** tab. Finally, hit **Add a new web app**. 154 | 155 | After confirming your domain name, choose **manual configuration** (N.B. – *not* the "Django" option) in the dialog. Next choose **Python 3.6**, and click Next to finish the wizard. 156 | 157 | > **Note** Make sure you choose the "Manual configuration" option, not the "Django" one. We're too cool for the default PythonAnywhere Django setup. ;-) 158 | 159 | 160 | ### Setting the virtualenv 161 | 162 | You'll be taken to the PythonAnywhere config screen for your webapp, which is where you'll need to go whenever you want to make changes to the app on the server. 163 | 164 | screenshot of web tab virtualenv config section with path correctly filled in 165 | 166 | In the "Virtualenv" section, click the red text that says "Enter the path to a virtualenv", and enter `/home//my-first-blog/myvenv/`. Click the blue box with the checkmark to save the path before moving on. 167 | 168 | > **Note** Substitute your own PythonAnywhere username as appropriate. If you make a mistake, PythonAnywhere will show you a little warning. 169 | 170 | 171 | ### Adding the static files mapping 172 | 173 | We need to tell PythonAnywhere that static files which live under the URL `/static/` are all located in a file inside your source code folder called static. We do that in the "Static Files" section on the Web tab. 174 | 175 | Click the red text that says "Enter URL", and enter `/static/`, and click the blue checkbox to save. Then click the text that says "Enter path", and enter `/home// 178 | 179 | 180 | 181 | 182 | ### Configuring the WSGI file 183 | 184 | Django works using the "WSGI protocol", a standard for serving websites using Python, which PythonAnywhere supports. The way we configure PythonAnywhere to recognize our Django blog is by editing a WSGI configuration file. 185 | 186 | Click on the "WSGI configuration file" link (in the "Code" section near the top of the page – it'll be named something like `/var/www/_pythonanywhere_com_wsgi.py`), and you'll be taken to an editor. 187 | 188 | Delete all the contents and replace them with the following: 189 | 190 | {% filename %}<your-username>_pythonanywhere_com_wsgi.py{% endfilename %} 191 | ```python 192 | import os 193 | import sys 194 | 195 | path = '/home//.pythonanywhere.com') 196 | if path not in sys.path: 197 | sys.path.append(path) 198 | 199 | os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings' 200 | 201 | from django.core.wsgi import get_wsgi_application 202 | application = get_wsgi_application() 203 | ``` 204 | 205 | > **Note** As always, substitute your own PythonAnywhere username in this file. 206 | 207 | 208 | This file's job is to tell PythonAnywhere where our web app lives and what the Django settings file's name is. 209 | 210 | Hit **Save** and then go back to the **Web** tab. 211 | 212 | We're all done! Hit the big green **Reload** button and you'll be able to go view your application. You'll find a link to it at the top of the page. 213 | 214 | 215 | ## Debugging tips 216 | 217 | If you see an error when you try to visit your site, the first place to look for some debugging info is in your **error log**. You'll find a link to this on the PythonAnywhere [Web tab](https://www.pythonanywhere.com/web_app_setup/). See if there are any error messages in there; the most recent ones are at the bottom. Common problems include: 218 | 219 | - Forgetting one of the steps we did in the console: creating the virtualenv, activating it, installing Django into it, migrating the database. 220 | 221 | - Making a mistake in the virtualenv path on the Web tab – there will usually be a little red error message on there, if there is a problem. 222 | 223 | - Making a mistake in the WSGI configuration file – did you get the path to your my-first-blog folder right? 224 | 225 | - Did you pick the same version of Python for your virtualenv as you did for your web app? Both should be 3.6. 226 | 227 | There are also some [general debugging tips on the PythonAnywhere wiki](https://www.pythonanywhere.com/wiki/DebuggingImportError). 228 | 229 | -------------------------------------------------------------------------------- /ja/manual_pythonanywhere_deploy/README.md: -------------------------------------------------------------------------------- 1 | # Deploying to PythonAnywhere manually 2 | 3 | In the main tutorial, we deployed our app using PythonAnywhere's "autoconfigure" script, which did a lot of magic for us. In this extension we'll take a peek "behind the scenes" and find out what autoconfigure script actually did, by learning how to deploy our code manually to PythonAnywhere. 4 | 5 | Why might you be interested in doing this? Beyond pure curiosity, the steps involved in a manual deployment are more or less the same steps you'd need to go through if you were trying to deploy to a different hosting provider, including running your own server, which is something you might want to do some day. 6 | 7 | Running through the procedure manually is also a good idea for DjangoGirls coaches. If something goes wrong with the autoconfigure script in one of the workshops, knowing how to do it manually could help you get one of the attendees out of a broken state and back to a working deployment. 8 | 9 | So, read on! 10 | 11 | 12 | ## Assumptions 13 | 14 | * This extension assumes you already have some code on GitHub. See the begining of the deploy chapter for instructions on that, if you need. 15 | * It also assumes you have a PythonAnywhere account. If it already has a djangogirls webapp running in it, you'll need to delete that first (or just sign up for a new account!) 16 | 17 | ## Overview 18 | 19 | Deploying to PythonAnywhere, or indeed to any server, involves pretty much the same steps: 20 | 21 | * Getting your code onto the server. We'll use `git clone` and `git pull` for this 22 | 23 | * Installing your dependencies on the server. We'll use a virtualenv for this, just like on your own computer, but we'll use a shortcut recommended by PythonAnywhere called `virtualenvwrapper` 24 | 25 | * Configuring your database on the server. The database on your own computer and the one on the server are separate. We'll use `manage.py migrate` and `manage.py createsuperuser` for this. 26 | 27 | * Configuring your static files on the server. On your own laptop, `runserver` takes care of static files, but servers like to manage things differently in order to optimise serving static files. Here we'll use a new command called `collectstatic`, and configure the static files through PythonAnywhere's **Web tab**. 28 | 29 | * And actually hooking up our Django app to be served, live, on the public Internet. We do this on PythonAnywhere's **Web tab**, by configuring something they call a **WSGI file**. 30 | 31 | 32 | ## Pulling our code down on PythonAnywhere 33 | 34 | Go to the [PythonAnywhere Dashboard](https://www.pythonanywhere.com), and choose the option to start a "Bash" console – that's the PythonAnywhere version of a command-line, just like the one on your computer. 35 | 36 | pointing at Other: Bash in Start a new Console 37 | 38 | > **Note** PythonAnywhere is based on Linux, so if you're on Windows, the console will look a little different from the one on your computer. 39 | 40 | Let's pull down our code from GitHub and onto PythonAnywhere by creating a "clone" of our repo. Type the following into the console on PythonAnywhere: 41 | 42 | {% filename %}PythonAnywhere command-line{% endfilename %} 43 | ``` 44 | $ git clone https://github.com//my-first-blog.git .pythonanywhere.com 45 | ``` 46 | 47 | * Use your actual GitHub username in place of `` 48 | * And use your actual PythonAnywhere username in place of ``) 49 | 50 | This will pull down a copy of your code onto PythonAnywhere, and put it into a folder named after your (future) website address. Check it out by typing `tree`: 51 | 52 | {% filename %}PythonAnywhere command-line{% endfilename %} 53 | ``` 54 | $ tree 55 | ola.pythonanywhere.com/ 56 | ├── blog 57 | │ ├── __init__.py 58 | │ ├── admin.py 59 | │ ├── migrations 60 | │ │ ├── 0001_initial.py 61 | │ │ └── __init__.py 62 | │ ├── models.py 63 | │ ├── tests.py 64 | │ └── views.py 65 | ├── manage.py 66 | └── mysite 67 | ├── __init__.py 68 | ├── settings.py 69 | ├── urls.py 70 | └── wsgi.py 71 | ``` 72 | 73 | 74 | ### Creating a virtualenv on PythonAnywhere 75 | 76 | Just like you did on your own computer, you need to create a virtualenv on PythonAnywhere. In the Bash console, type: 77 | 78 | {% filename %}PythonAnywhere command-line{% endfilename %} 79 | ``` 80 | $ mkvirtualenv --python=python3.6 .pythonanywhere.com 81 | Running virtualenv with interpreter /usr/bin/python3.6 82 | [...] 83 | Installing setuptools, pip...done. 84 | ``` 85 | 86 | `mkvirtualenv` comes from a tool called "virtualenvwrapper", which PythonAnywhere recommends. It's a set of shortcuts built around the normal `virtualenv` command that you've already learned about using on your own computer. 87 | 88 | When the command finishes, your virtualenv should be active; we've named it after your future website address, just like we named the project source code folder. Let's try de-activating and re-activating the virtualenv, just for practice. 89 | 90 | Deactivating is `deactivate`, just like on your computer, but to activate we use the virtualenvwrapper shortcut command `workon`, which just needs the name of your virtualenv: 91 | 92 | {% filename %}PythonAnywhere command-line{% endfilename %} 93 | ``` 94 | (ola.pythonanywhere.com) $ deactivate 95 | $ which python 96 | /usr/bin/python 97 | $ workon .pythonanywhere.com 98 | (ola.pythonanywhere.com) $ which python 99 | /home/ola/.virtualenvs/ola.pythonanywhere.com/bin/python 100 | ``` 101 | 102 | Now let's install Django into our virtualenv on PythonAnywhere 103 | 104 | {% filename %}PythonAnywhere command-line{% endfilename %} 105 | ``` 106 | (ola.pythonanywhere.com) $ pip install 'django<2' 107 | Collecting django 108 | [...] 109 | Successfully installed django-1.11.9 110 | ``` 111 | 112 | > **Note** The `pip install` step can take a couple of minutes. Patience, patience! But if it takes more than five minutes, something is wrong. Ask your coach. 113 | 114 | 115 | 116 | 117 | ### Creating the database on PythonAnywhere 118 | 119 | Here's another thing that's different between your own computer and the server: it uses a different database. So the user accounts and posts can be different on the server and on your computer. 120 | 121 | Just as we did on your own computer, we repeat the step to initialize the database on the server, with `migrate` and `createsuperuser`: 122 | 123 | Go back to your Bash console, make sure your virtualenv is still active, and then run these commands. (If you've closed your console, you can open a new one, and use the `workon` command to activate your virtualenv). 124 | 125 | {% filename %}PythonAnywhere command-line{% endfilename %} 126 | ``` 127 | (ola.pythonanywhere.com) $ python manage.py migrate 128 | Operations to perform: 129 | [...] 130 | Applying sessions.0001_initial... OK 131 | (ola.pythonanywhere.com) $ python manage.py createsuperuser 132 | ``` 133 | 134 | ### Collecting static files 135 | 136 | Now we learn a new command, `collecstatic`, whose job it is to collect all the static files from your apps (including apps you've written like *blog*, and built-in Django apps like the *admin*), and put them in one place, so the server can find them: 137 | 138 | {% filename %}PythonAnywhere command-line{% endfilename %} 139 | ``` 140 | (ola.pythonanywhere.com) $ python manage.py collectstatic 141 | You have requested to collect static files at the destination 142 | [...] 143 | Are you sure you want to do this? 144 | [...] 145 | 62 static files copied to '/home/ola/ola.pythonanywhere.com/static'. 146 | ``` 147 | 148 | 149 | ## Publishing our blog as a web app 150 | 151 | Now our code is on PythonAnywhere, our virtualenv is ready, and the database is initialized. We're ready to publish it as a web app! 152 | 153 | Click back to the PythonAnywhere dashboard by clicking on its logo, and then click on the **Web** tab. Finally, hit **Add a new web app**. 154 | 155 | After confirming your domain name, choose **manual configuration** (N.B. – *not* the "Django" option) in the dialog. Next choose **Python 3.6**, and click Next to finish the wizard. 156 | 157 | > **Note** Make sure you choose the "Manual configuration" option, not the "Django" one. We're too cool for the default PythonAnywhere Django setup. ;-) 158 | 159 | 160 | ### Setting the virtualenv 161 | 162 | You'll be taken to the PythonAnywhere config screen for your webapp, which is where you'll need to go whenever you want to make changes to the app on the server. 163 | 164 | screenshot of web tab virtualenv config section with path correctly filled in 165 | 166 | In the "Virtualenv" section, click the red text that says "Enter the path to a virtualenv", and enter `/home//my-first-blog/myvenv/`. Click the blue box with the checkmark to save the path before moving on. 167 | 168 | > **Note** Substitute your own PythonAnywhere username as appropriate. If you make a mistake, PythonAnywhere will show you a little warning. 169 | 170 | 171 | ### Adding the static files mapping 172 | 173 | We need to tell PythonAnywhere that static files which live under the URL `/static/` are all located in a file inside your source code folder called static. We do that in the "Static Files" section on the Web tab. 174 | 175 | Click the red text that says "Enter URL", and enter `/static/`, and click the blue checkbox to save. Then click the text that says "Enter path", and enter `/home// 178 | 179 | 180 | 181 | 182 | ### Configuring the WSGI file 183 | 184 | Django works using the "WSGI protocol", a standard for serving websites using Python, which PythonAnywhere supports. The way we configure PythonAnywhere to recognize our Django blog is by editing a WSGI configuration file. 185 | 186 | Click on the "WSGI configuration file" link (in the "Code" section near the top of the page – it'll be named something like `/var/www/_pythonanywhere_com_wsgi.py`), and you'll be taken to an editor. 187 | 188 | Delete all the contents and replace them with the following: 189 | 190 | {% filename %}<your-username>_pythonanywhere_com_wsgi.py{% endfilename %} 191 | ```python 192 | import os 193 | import sys 194 | 195 | path = '/home//.pythonanywhere.com') 196 | if path not in sys.path: 197 | sys.path.append(path) 198 | 199 | os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings' 200 | 201 | from django.core.wsgi import get_wsgi_application 202 | application = get_wsgi_application() 203 | ``` 204 | 205 | > **Note** As always, substitute your own PythonAnywhere username in this file. 206 | 207 | 208 | This file's job is to tell PythonAnywhere where our web app lives and what the Django settings file's name is. 209 | 210 | Hit **Save** and then go back to the **Web** tab. 211 | 212 | We're all done! Hit the big green **Reload** button and you'll be able to go view your application. You'll find a link to it at the top of the page. 213 | 214 | 215 | ## Debugging tips 216 | 217 | If you see an error when you try to visit your site, the first place to look for some debugging info is in your **error log**. You'll find a link to this on the PythonAnywhere [Web tab](https://www.pythonanywhere.com/web_app_setup/). See if there are any error messages in there; the most recent ones are at the bottom. Common problems include: 218 | 219 | - Forgetting one of the steps we did in the console: creating the virtualenv, activating it, installing Django into it, migrating the database. 220 | 221 | - Making a mistake in the virtualenv path on the Web tab – there will usually be a little red error message on there, if there is a problem. 222 | 223 | - Making a mistake in the WSGI configuration file – did you get the path to your my-first-blog folder right? 224 | 225 | - Did you pick the same version of Python for your virtualenv as you did for your web app? Both should be 3.6. 226 | 227 | There are also some [general debugging tips on the PythonAnywhere wiki](https://www.pythonanywhere.com/wiki/DebuggingImportError). 228 | 229 | --------------------------------------------------------------------------------