├── Chapter02 ├── requirement.txt ├── my_app │ ├── product │ │ ├── __init__.py │ │ ├── models.py │ │ └── views.py │ ├── static │ │ └── css │ │ │ └── main.css │ ├── templates │ │ ├── product.html │ │ ├── home.html │ │ └── base.html │ └── __init__.py ├── run.py ├── README.md └── setup.py ├── Chapter06 ├── my_app │ ├── auth │ │ ├── __init__.py │ │ └── models.py │ ├── static │ │ └── css │ │ │ └── main.css │ ├── templates │ │ ├── home.html │ │ ├── register.html │ │ ├── base.html │ │ └── login.html │ └── __init__.py └── run.py ├── Chapter08 ├── my_app │ ├── auth │ │ ├── __init__.py │ │ └── models.py │ ├── templates │ │ ├── some-template.html │ │ ├── edit.html │ │ ├── admin-home.html │ │ ├── home.html │ │ ├── login.html │ │ ├── user-update-admin.html │ │ ├── register.html │ │ ├── user-create-admin.html │ │ ├── users-list-admin.html │ │ └── base.html │ ├── static │ │ └── css │ │ │ └── main.css │ └── __init__.py └── run.py ├── Chapter01 ├── my_app │ ├── hello │ │ ├── __init__.py │ │ ├── models.py │ │ └── views.py │ └── __init__.py ├── run.py ├── README.md └── setup.py ├── Chapter03 ├── my_app │ ├── catalog │ │ ├── __init__.py │ │ ├── models.py │ │ └── views.py │ └── __init__.py └── run.py ├── Chapter04 ├── my_app │ ├── catalog │ │ ├── __init__.py │ │ ├── models.py │ │ └── views.py │ ├── static │ │ └── css │ │ │ └── main.css │ ├── templates │ │ ├── product.html │ │ ├── categories.html │ │ ├── 404.html │ │ ├── category.html │ │ ├── home.html │ │ ├── products.html │ │ ├── product-create.html │ │ └── base.html │ └── __init__.py └── run.py ├── Chapter05 ├── my_app │ ├── catalog │ │ ├── __init__.py │ │ ├── models.py │ │ └── views.py │ ├── static │ │ └── css │ │ │ └── main.css │ ├── templates │ │ ├── categories.html │ │ ├── product.html │ │ ├── 404.html │ │ ├── category-create.html │ │ ├── category.html │ │ ├── home.html │ │ ├── product-create.html │ │ ├── products.html │ │ └── base.html │ └── __init__.py └── run.py ├── Chapter07 ├── my_app │ ├── catalog │ │ ├── __init__.py │ │ ├── models.py │ │ └── views.py │ ├── static │ │ └── css │ │ │ └── main.css │ ├── templates │ │ ├── product.html │ │ ├── categories.html │ │ ├── 404.html │ │ ├── category.html │ │ ├── home.html │ │ ├── products.html │ │ ├── product-create.html │ │ └── base.html │ └── __init__.py └── run.py ├── Chapter09 ├── my_app │ ├── catalog │ │ ├── __init__.py │ │ ├── models.py │ │ └── views.py │ ├── babel.cfg │ ├── static │ │ └── css │ │ │ └── main.css │ ├── translations │ │ └── fr │ │ │ └── LC_MESSAGES │ │ │ ├── messages.mo │ │ │ └── messages.po │ ├── templates │ │ ├── categories.html │ │ ├── product.html │ │ ├── 404.html │ │ ├── category-create.html │ │ ├── category.html │ │ ├── home.html │ │ ├── product-create.html │ │ ├── products.html │ │ └── base.html │ ├── __init__.py │ └── messages.pot └── run.py ├── Chapter10 ├── my_app │ ├── catalog │ │ ├── __init__.py │ │ ├── models.py │ │ └── views.py │ ├── babel.cfg │ ├── static │ │ └── css │ │ │ └── main.css │ ├── translations │ │ └── fr │ │ │ └── LC_MESSAGES │ │ │ ├── messages.mo │ │ │ └── messages.po │ ├── templates │ │ ├── categories.html │ │ ├── 404.html │ │ ├── product.html │ │ ├── category-create.html │ │ ├── category.html │ │ ├── home.html │ │ ├── product-create.html │ │ ├── products.html │ │ └── base.html │ ├── __init__.py │ └── messages.pot ├── run.py ├── generate_profile.py └── app_tests.py ├── Chapter11 ├── my_app │ ├── catalog │ │ ├── __init__.py │ │ └── models.py │ ├── babel.cfg │ ├── static │ │ └── css │ │ │ └── main.css │ ├── translations │ │ └── fr │ │ │ └── LC_MESSAGES │ │ │ ├── messages.mo │ │ │ └── messages.po │ ├── templates │ │ ├── categories.html │ │ ├── 404.html │ │ ├── product.html │ │ ├── category-create.html │ │ ├── category.html │ │ ├── home.html │ │ ├── product-create.html │ │ ├── products.html │ │ └── base.html │ ├── messages.pot │ └── __init__.py ├── run.py ├── Procfile ├── application.py ├── MANIFEST.in ├── uwsgi.ini ├── generate_profile.py ├── nginx-wsgi.conf ├── app.wsgi ├── tornado_server.py ├── apache_wsgi.conf ├── requirements.txt ├── setup.py └── app_tests.py ├── Chapter12 ├── my_app │ ├── catalog │ │ ├── __init__.py │ │ ├── models.py │ │ └── views.py │ ├── static │ │ └── css │ │ │ └── main.css │ ├── templates │ │ ├── product.html │ │ ├── category-create-email-text.html │ │ ├── categories.html │ │ ├── category-create-email-html.html │ │ ├── 404.html │ │ ├── category.html │ │ ├── home.html │ │ ├── products.html │ │ ├── product-create.html │ │ └── base.html │ └── __init__.py └── run.py ├── Chapter13 ├── Docker │ ├── my_app │ │ ├── catalog │ │ │ ├── __init__.py │ │ │ └── models.py │ │ ├── babel.cfg │ │ ├── static │ │ │ └── css │ │ │ │ └── main.css │ │ ├── translations │ │ │ └── fr │ │ │ │ └── LC_MESSAGES │ │ │ │ ├── messages.mo │ │ │ │ └── messages.po │ │ ├── templates │ │ │ ├── categories.html │ │ │ ├── 404.html │ │ │ ├── product.html │ │ │ ├── category-create.html │ │ │ ├── category.html │ │ │ ├── home.html │ │ │ ├── product-create.html │ │ │ ├── products.html │ │ │ └── base.html │ │ ├── messages.pot │ │ └── __init__.py │ ├── Procfile │ ├── run.py │ ├── application.py │ ├── MANIFEST.in │ ├── uwsgi.ini │ ├── generate_profile.py │ ├── Dockerfile │ ├── nginx-wsgi.conf │ ├── app.wsgi │ ├── tornado_server.py │ ├── apache_wsgi.conf │ ├── requirements.txt │ ├── setup.py │ └── app_tests.py └── Lambda │ ├── my_app │ ├── hello │ │ ├── __init__.py │ │ ├── models.py │ │ └── views.py │ └── __init__.py │ ├── run.py │ ├── zappa_settings.json │ ├── README.md │ └── setup.py ├── LICENSE └── README.md /Chapter02/requirement.txt: -------------------------------------------------------------------------------- 1 | ccy 2 | -------------------------------------------------------------------------------- /Chapter06/my_app/auth/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter08/my_app/auth/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter01/my_app/hello/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter02/my_app/product/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter03/my_app/catalog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter04/my_app/catalog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter05/my_app/catalog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter07/my_app/catalog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter09/my_app/catalog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter10/my_app/catalog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter11/my_app/catalog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter12/my_app/catalog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter13/Docker/my_app/catalog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter13/Lambda/my_app/hello/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter02/run.py: -------------------------------------------------------------------------------- 1 | from my_app import app 2 | app.run(debug=True) 3 | -------------------------------------------------------------------------------- /Chapter03/run.py: -------------------------------------------------------------------------------- 1 | from my_app import app 2 | app.run(debug=True) 3 | -------------------------------------------------------------------------------- /Chapter04/run.py: -------------------------------------------------------------------------------- 1 | from my_app import app 2 | app.run(debug=True) 3 | -------------------------------------------------------------------------------- /Chapter05/run.py: -------------------------------------------------------------------------------- 1 | from my_app import app 2 | app.run(debug=True) 3 | -------------------------------------------------------------------------------- /Chapter06/run.py: -------------------------------------------------------------------------------- 1 | from my_app import app 2 | app.run(debug=True) 3 | -------------------------------------------------------------------------------- /Chapter07/run.py: -------------------------------------------------------------------------------- 1 | from my_app import app 2 | app.run(debug=True) 3 | -------------------------------------------------------------------------------- /Chapter08/run.py: -------------------------------------------------------------------------------- 1 | from my_app import app 2 | app.run(debug=True) 3 | -------------------------------------------------------------------------------- /Chapter09/run.py: -------------------------------------------------------------------------------- 1 | from my_app import app 2 | app.run(debug=True) 3 | -------------------------------------------------------------------------------- /Chapter10/run.py: -------------------------------------------------------------------------------- 1 | from my_app import app 2 | app.run(debug=True) 3 | -------------------------------------------------------------------------------- /Chapter11/run.py: -------------------------------------------------------------------------------- 1 | from my_app import app 2 | app.run(debug=True) 3 | -------------------------------------------------------------------------------- /Chapter12/run.py: -------------------------------------------------------------------------------- 1 | from my_app import app 2 | app.run(debug=True) 3 | -------------------------------------------------------------------------------- /Chapter11/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn -w 4 my_app:app --log-level debug 2 | -------------------------------------------------------------------------------- /Chapter13/Docker/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn -w 4 my_app:app --log-level debug 2 | -------------------------------------------------------------------------------- /Chapter01/run.py: -------------------------------------------------------------------------------- 1 | from my_app import app 2 | app.env="development" 3 | app.run(debug=True) 4 | -------------------------------------------------------------------------------- /Chapter13/Docker/run.py: -------------------------------------------------------------------------------- 1 | from my_app import app 2 | app.run(debug=True, host='0.0.0.0') 3 | -------------------------------------------------------------------------------- /Chapter13/Lambda/run.py: -------------------------------------------------------------------------------- 1 | from my_app import app 2 | app.env="development" 3 | app.run(debug=True) 4 | -------------------------------------------------------------------------------- /Chapter01/my_app/hello/models.py: -------------------------------------------------------------------------------- 1 | MESSAGES = { 2 | 'default': 'Hello to the World of Flask!', 3 | } 4 | -------------------------------------------------------------------------------- /Chapter08/my_app/templates/some-template.html: -------------------------------------------------------------------------------- 1 | This is a template just for demonstration of Flask Admin Views. 2 | -------------------------------------------------------------------------------- /Chapter13/Lambda/my_app/hello/models.py: -------------------------------------------------------------------------------- 1 | MESSAGES = { 2 | 'default': 'Hello to the World of Flask!', 3 | } 4 | -------------------------------------------------------------------------------- /Chapter11/application.py: -------------------------------------------------------------------------------- 1 | from my_app import app as application 2 | import sys, logging 3 | logging.basicConfig(stream = sys.stderr) 4 | -------------------------------------------------------------------------------- /Chapter09/my_app/babel.cfg: -------------------------------------------------------------------------------- 1 | [python: catalog/**.py] 2 | [jinja2: templates/**.html] 3 | extensions=jinja2.ext.autoescape,jinja2.ext.with_ 4 | -------------------------------------------------------------------------------- /Chapter10/my_app/babel.cfg: -------------------------------------------------------------------------------- 1 | [python: catalog/**.py] 2 | [jinja2: templates/**.html] 3 | extensions=jinja2.ext.autoescape,jinja2.ext.with_ 4 | -------------------------------------------------------------------------------- /Chapter11/my_app/babel.cfg: -------------------------------------------------------------------------------- 1 | [python: catalog/**.py] 2 | [jinja2: templates/**.html] 3 | extensions=jinja2.ext.autoescape,jinja2.ext.with_ 4 | -------------------------------------------------------------------------------- /Chapter13/Docker/application.py: -------------------------------------------------------------------------------- 1 | from my_app import app as application 2 | import sys, logging 3 | logging.basicConfig(stream = sys.stderr) 4 | -------------------------------------------------------------------------------- /Chapter11/MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include my_app/templates * 2 | recursive-include my_app/static * 3 | recursive-include my_app/translations * 4 | -------------------------------------------------------------------------------- /Chapter13/Docker/my_app/babel.cfg: -------------------------------------------------------------------------------- 1 | [python: catalog/**.py] 2 | [jinja2: templates/**.html] 3 | extensions=jinja2.ext.autoescape,jinja2.ext.with_ 4 | -------------------------------------------------------------------------------- /Chapter13/Docker/MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include my_app/templates * 2 | recursive-include my_app/static * 3 | recursive-include my_app/translations * 4 | -------------------------------------------------------------------------------- /Chapter02/my_app/static/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | } 4 | .top-pad { 5 | padding: 40px 15px; 6 | text-align: center; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /Chapter04/my_app/static/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | } 4 | .top-pad { 5 | padding: 40px 15px; 6 | text-align: center; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /Chapter05/my_app/static/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | } 4 | .top-pad { 5 | padding: 40px 15px; 6 | text-align: center; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /Chapter06/my_app/static/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | } 4 | .top-pad { 5 | padding: 40px 15px; 6 | text-align: center; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /Chapter07/my_app/static/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | } 4 | .top-pad { 5 | padding: 40px 15px; 6 | text-align: center; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /Chapter08/my_app/static/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | } 4 | .top-pad { 5 | padding: 40px 15px; 6 | text-align: center; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /Chapter09/my_app/static/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | } 4 | .top-pad { 5 | padding: 40px 15px; 6 | text-align: center; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /Chapter10/my_app/static/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | } 4 | .top-pad { 5 | padding: 40px 15px; 6 | text-align: center; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /Chapter11/my_app/static/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | } 4 | .top-pad { 5 | padding: 40px 15px; 6 | text-align: center; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /Chapter12/my_app/static/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | } 4 | .top-pad { 5 | padding: 40px 15px; 6 | text-align: center; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /Chapter01/my_app/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from my_app.hello.views import hello 3 | 4 | app = Flask(__name__) 5 | app.register_blueprint(hello) 6 | -------------------------------------------------------------------------------- /Chapter13/Docker/my_app/static/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | } 4 | .top-pad { 5 | padding: 40px 15px; 6 | text-align: center; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /Chapter11/uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | socket = :9090 3 | plugin = python 4 | wsgi-file = /home/ubuntu/workspace/cookbook11/Chapter-11/app.wsgi 5 | processes = 3 6 | -------------------------------------------------------------------------------- /Chapter13/Docker/uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | socket = :9090 3 | plugin = python 4 | wsgi-file = /home/ubuntu/workspace/cookbook11/Chapter-11/app.wsgi 5 | processes = 3 6 | -------------------------------------------------------------------------------- /Chapter13/Lambda/my_app/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from my_app.hello.views import hello 3 | 4 | app = Flask(__name__) 5 | app.register_blueprint(hello) 6 | -------------------------------------------------------------------------------- /Chapter09/my_app/translations/fr/LC_MESSAGES/messages.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Flask-Framework-Cookbook-Second-Edition/HEAD/Chapter09/my_app/translations/fr/LC_MESSAGES/messages.mo -------------------------------------------------------------------------------- /Chapter10/my_app/translations/fr/LC_MESSAGES/messages.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Flask-Framework-Cookbook-Second-Edition/HEAD/Chapter10/my_app/translations/fr/LC_MESSAGES/messages.mo -------------------------------------------------------------------------------- /Chapter11/my_app/translations/fr/LC_MESSAGES/messages.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Flask-Framework-Cookbook-Second-Edition/HEAD/Chapter11/my_app/translations/fr/LC_MESSAGES/messages.mo -------------------------------------------------------------------------------- /Chapter13/Docker/my_app/translations/fr/LC_MESSAGES/messages.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Flask-Framework-Cookbook-Second-Edition/HEAD/Chapter13/Docker/my_app/translations/fr/LC_MESSAGES/messages.mo -------------------------------------------------------------------------------- /Chapter10/generate_profile.py: -------------------------------------------------------------------------------- 1 | from werkzeug.contrib.profiler import ProfilerMiddleware 2 | from my_app import app 3 | 4 | app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions = [10]) 5 | app.run(debug=True) 6 | -------------------------------------------------------------------------------- /Chapter11/generate_profile.py: -------------------------------------------------------------------------------- 1 | from werkzeug.contrib.profiler import ProfilerMiddleware 2 | from my_app import app 3 | 4 | app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions = [10]) 5 | app.run(debug=True) 6 | -------------------------------------------------------------------------------- /Chapter13/Docker/generate_profile.py: -------------------------------------------------------------------------------- 1 | from werkzeug.contrib.profiler import ProfilerMiddleware 2 | from my_app import app 3 | 4 | app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions = [10]) 5 | app.run(debug=True) 6 | -------------------------------------------------------------------------------- /Chapter08/my_app/templates/edit.html: -------------------------------------------------------------------------------- 1 | {% extends 'admin/model/edit.html' %} 2 | 3 | {% block tail %} 4 | {{ super() }} 5 | 6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /Chapter13/Docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY requirements.txt requirements.txt 6 | RUN pip install -r requirements.txt 7 | 8 | COPY . . 9 | 10 | ENTRYPOINT [ "python" ] 11 | CMD [ "run.py" ] 12 | -------------------------------------------------------------------------------- /Chapter11/nginx-wsgi.conf: -------------------------------------------------------------------------------- 1 | server { 2 | location / { 3 | include uwsgi_params; 4 | uwsgi_pass 127.0.0.1:9090; 5 | } 6 | location /static/uploads/ { 7 | alias /home/ubuntu/workspace/cookbook11/Chapter-11/flask_test_uploads/; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Chapter11/app.wsgi: -------------------------------------------------------------------------------- 1 | activate_this = '/home/ubuntu/workspace/cookbook11/bin/activate_this.py' 2 | exec(open(activate_this).read(), dict(__file__=activate_this)) 3 | 4 | from my_app import app as application 5 | import sys, logging 6 | logging.basicConfig(stream = sys.stderr) 7 | -------------------------------------------------------------------------------- /Chapter13/Docker/nginx-wsgi.conf: -------------------------------------------------------------------------------- 1 | server { 2 | location / { 3 | include uwsgi_params; 4 | uwsgi_pass 127.0.0.1:9090; 5 | } 6 | location /static/uploads/ { 7 | alias /home/ubuntu/workspace/cookbook11/Chapter-11/flask_test_uploads/; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Chapter13/Docker/app.wsgi: -------------------------------------------------------------------------------- 1 | activate_this = '/home/ubuntu/workspace/cookbook11/bin/activate_this.py' 2 | exec(open(activate_this).read(), dict(__file__=activate_this)) 3 | 4 | from my_app import app as application 5 | import sys, logging 6 | logging.basicConfig(stream = sys.stderr) 7 | -------------------------------------------------------------------------------- /Chapter11/tornado_server.py: -------------------------------------------------------------------------------- 1 | from tornado.wsgi import WSGIContainer 2 | from tornado.httpserver import HTTPServer 3 | from tornado.ioloop import IOLoop 4 | from my_app import app 5 | 6 | http_server = HTTPServer(WSGIContainer(app)) 7 | http_server.listen(5000) 8 | IOLoop.instance().start() 9 | -------------------------------------------------------------------------------- /Chapter13/Docker/tornado_server.py: -------------------------------------------------------------------------------- 1 | from tornado.wsgi import WSGIContainer 2 | from tornado.httpserver import HTTPServer 3 | from tornado.ioloop import IOLoop 4 | from my_app import app 5 | 6 | http_server = HTTPServer(WSGIContainer(app)) 7 | http_server.listen(5000) 8 | IOLoop.instance().start() 9 | -------------------------------------------------------------------------------- /Chapter04/my_app/templates/product.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

{{ product.name }} {{ product.category.name }}

6 |

{{ product.company }}

7 |

{{ product.price }}

8 |
9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /Chapter07/my_app/templates/product.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

{{ product.name }} {{ product.category.name }}

6 |

{{ product.company }}

7 |

{{ product.price }}

8 |
9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /Chapter12/my_app/templates/product.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

{{ product.name }} {{ product.category.name }}

6 |

{{ product.company }}

7 |

{{ product.price }}

8 |
9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /Chapter13/Lambda/zappa_settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dev": { 3 | "app_function": "my_app.__init__.app", 4 | "aws_region": "ap-south-1", 5 | "profile_name": "personal", 6 | "project_name": "chapter-13", 7 | "runtime": "python3.6", 8 | "s3_bucket": "zappa-chupnt4b0" 9 | } 10 | } -------------------------------------------------------------------------------- /Chapter12/my_app/templates/category-create-email-text.html: -------------------------------------------------------------------------------- 1 | A new category has been added to the catalog. 2 | 3 | The name of the category is {{ category.name }}. 4 | Click on the URL below to acces the same: 5 | {{ url_for('catalog.category', id=category.id, _external = True) }} 6 | 7 | This is an automated email. Do not reply to it. 8 | -------------------------------------------------------------------------------- /Chapter04/my_app/templates/categories.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | {% for category in categories %} 6 | 7 |

{{ category.name }}

8 |
9 | {% endfor %} 10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /Chapter05/my_app/templates/categories.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | {% for category in categories %} 6 | 7 |

{{ category.name }}

8 |
9 | {% endfor %} 10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /Chapter07/my_app/templates/categories.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | {% for category in categories %} 6 | 7 |

{{ category.name }}

8 |
9 | {% endfor %} 10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /Chapter09/my_app/templates/categories.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | {% for category in categories %} 6 | 7 |

{{ category.name }}

8 |
9 | {% endfor %} 10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /Chapter10/my_app/templates/categories.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | {% for category in categories %} 6 | 7 |

{{ category.name }}

8 |
9 | {% endfor %} 10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /Chapter11/my_app/templates/categories.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | {% for category in categories %} 6 | 7 |

{{ category.name }}

8 |
9 | {% endfor %} 10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /Chapter12/my_app/templates/categories.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | {% for category in categories %} 6 | 7 |

{{ category.name }}

8 |
9 | {% endfor %} 10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /Chapter02/README.md: -------------------------------------------------------------------------------- 1 | # Flask Framework Cookbook 2 | 3 | ## Chapter 2 - Templating with Jinja2 4 | 5 | * Introduction 6 | * Bootstrap layout 7 | * Block composition and layout inheritance 8 | * Creating a custom context processor 9 | * Creating a custom Jinja2 filter 10 | * Creating a custom macro for forms 11 | * Advanced date and time formatting 12 | -------------------------------------------------------------------------------- /Chapter12/my_app/templates/category-create-email-html.html: -------------------------------------------------------------------------------- 1 |

A new category has been added to the catalog.

2 | 3 |

The name of the category is 4 |

{{ category.name }}

5 | . 6 |

7 | 8 |

This is an automated email. Do not reply to it.

9 | -------------------------------------------------------------------------------- /Chapter13/Docker/my_app/templates/categories.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | {% for category in categories %} 6 | 7 |

{{ category.name }}

8 |
9 | {% endfor %} 10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /Chapter02/my_app/templates/product.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

{{ full_name(product) }}

6 |

{{ product['name'] }} 7 | {{ product['category'] }} 8 |

9 |

{{ product['price']|format_currency }}

10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /Chapter05/my_app/templates/product.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | 6 |

{{ product.name }} {{ product.category.name }}

7 |

{{ product.price }}

8 |
9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /Chapter09/my_app/templates/product.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | 6 |

{{ product.name }} {{ product.category.name }}

7 |

{{ product.price }}

8 |
9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /Chapter04/my_app/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

Hola Friend! Looks like in your quest you have reached a location which does not exist yet.

6 |

To continue, either check your map location (URL) or go back home

7 |
8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /Chapter05/my_app/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

Hola Friend! Looks like in your quest you have reached a location which does not exist yet.

6 |

To continue, either check your map location (URL) or go back home

7 |
8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /Chapter07/my_app/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

Hola Friend! Looks like in your quest you have reached a location which does not exist yet.

6 |

To continue, either check your map location (URL) or go back home

7 |
8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /Chapter09/my_app/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

Hola Friend! Looks like in your quest you have reached a location which does not exist yet.

6 |

To continue, either check your map location (URL) or go back home

7 |
8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /Chapter10/my_app/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

Hola Friend! Looks like in your quest you have reached a location which does not exist yet.

6 |

To continue, either check your map location (URL) or go back home

7 |
8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /Chapter11/my_app/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

Hola Friend! Looks like in your quest you have reached a location which does not exist yet.

6 |

To continue, either check your map location (URL) or go back home

7 |
8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /Chapter12/my_app/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

Hola Friend! Looks like in your quest you have reached a location which does not exist yet.

6 |

To continue, either check your map location (URL) or go back home

7 |
8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /Chapter03/my_app/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_mongoengine import MongoEngine 3 | from redis import Redis 4 | 5 | 6 | app = Flask(__name__) 7 | app.config['MONGODB_SETTINGS'] = {'DB': 'my_catalog'} 8 | app.debug = True 9 | db = MongoEngine(app) 10 | 11 | redis = Redis() 12 | 13 | from my_app.catalog.views import catalog 14 | app.register_blueprint(catalog) 15 | -------------------------------------------------------------------------------- /Chapter04/my_app/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_sqlalchemy import SQLAlchemy 3 | 4 | app = Flask(__name__) 5 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db' 6 | db = SQLAlchemy(app) 7 | 8 | app.secret_key = 'some_random_key' 9 | 10 | from my_app.catalog.views import catalog 11 | app.register_blueprint(catalog) 12 | 13 | db.create_all() 14 | -------------------------------------------------------------------------------- /Chapter13/Docker/my_app/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

Hola Friend! Looks like in your quest you have reached a location which does not exist yet.

6 |

To continue, either check your map location (URL) or go back home

7 |
8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /Chapter10/my_app/templates/product.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | 6 |

{{ product.name }} {{ product.category.name }}

7 |

{{ product.price }}

8 |

{{ product.user_timezone }}

9 |
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /Chapter11/my_app/templates/product.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | 6 |

{{ product.name }} {{ product.category.name }}

7 |

{{ product.price }}

8 |

{{ product.user_timezone }}

9 |
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /Chapter02/my_app/__init__.py: -------------------------------------------------------------------------------- 1 | import ccy 2 | from flask import Flask, request 3 | from my_app.product.views import product_blueprint 4 | 5 | app = Flask(__name__) 6 | app.register_blueprint(product_blueprint) 7 | 8 | 9 | @app.template_filter('format_currency') 10 | def format_currency_filter(amount): 11 | currency_code = ccy.countryccy(request.accept_languages.best[-2:]) or 'USD' 12 | return '{0} {1}'.format(currency_code, amount) 13 | -------------------------------------------------------------------------------- /Chapter07/my_app/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_sqlalchemy import SQLAlchemy 3 | from flask_restful import Api 4 | 5 | app = Flask(__name__) 6 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db' 7 | db = SQLAlchemy(app) 8 | api = Api(app) 9 | 10 | app.secret_key = 'some_random_key' 11 | 12 | from my_app.catalog.views import catalog 13 | app.register_blueprint(catalog) 14 | 15 | db.create_all() 16 | -------------------------------------------------------------------------------- /Chapter13/Docker/my_app/templates/product.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | 6 |

{{ product.name }} {{ product.category.name }}

7 |

{{ product.price }}

8 |

{{ product.user_timezone }}

9 |
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /Chapter01/README.md: -------------------------------------------------------------------------------- 1 | # Flask Framework Cookbook 2 | 3 | ## Chapter 1 - Flask Configurations 4 | 5 | * Introduction 6 | * Environment setup with virtualenv 7 | * Handling basic configurations 8 | * Class-based settings 9 | * Organization of static files 10 | * Being deployment specific with instance folders 11 | * Composition of views and models 12 | * Creating a modular web app with blueprints 13 | * Making a Flask app installable using setuptools 14 | -------------------------------------------------------------------------------- /Chapter02/my_app/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block container %} 4 |
5 | {% for id, product in products.items() %} 6 |
7 |

8 | {{ product['name'] }} 9 | $ {{ product['price'] }} 10 |

11 |
12 | {% endfor %} 13 |
14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /Chapter05/my_app/templates/category-create.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |
6 | {{ form.csrf_token }} 7 |
{{ form.name.label }}: {{ form.name() }}
8 | 9 |
10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /Chapter08/my_app/templates/admin-home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block container %} 4 |

Welcome to the Admin Demo

5 |

Hey {{ current_user.username }}!!

6 | List of all users 7 |
8 | Create a new user 9 |
10 | Click here to logout 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /Chapter09/my_app/templates/category-create.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |
6 | {{ form.csrf_token }} 7 |
{{ form.name.label }}: {{ form.name() }}
8 | 9 |
10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /Chapter10/my_app/templates/category-create.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |
6 | {{ form.csrf_token }} 7 |
{{ form.name.label }}: {{ form.name() }}
8 | 9 |
10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /Chapter11/my_app/templates/category-create.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |
6 | {{ form.csrf_token }} 7 |
{{ form.name.label }}: {{ form.name() }}
8 | 9 |
10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /Chapter13/Lambda/README.md: -------------------------------------------------------------------------------- 1 | # Flask Framework Cookbook 2 | 3 | ## Chapter 1 - Flask Configurations 4 | 5 | * Introduction 6 | * Environment setup with virtualenv 7 | * Handling basic configurations 8 | * Class-based settings 9 | * Organization of static files 10 | * Being deployment specific with instance folders 11 | * Composition of views and models 12 | * Creating a modular web app with blueprints 13 | * Making a Flask app installable using setuptools 14 | -------------------------------------------------------------------------------- /Chapter03/my_app/catalog/models.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from my_app import db 3 | 4 | 5 | class Product(db.Document): 6 | created_at = db.DateTimeField( 7 | default=datetime.datetime.now, required=True 8 | ) 9 | key = db.StringField(max_length=255, required=True) 10 | name = db.StringField(max_length=255, required=True) 11 | price = db.DecimalField() 12 | 13 | def __repr__(self): 14 | return '' % self.id 15 | -------------------------------------------------------------------------------- /Chapter13/Docker/my_app/templates/category-create.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |
6 | {{ form.csrf_token }} 7 |
{{ form.name.label }}: {{ form.name() }}
8 | 9 |
10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /Chapter06/my_app/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block container %} 4 |

Welcome to the Authentication Demo

5 | {% if current_user.is_authenticated %} 6 |

Hey {{ current_user.username }}!!

7 | Click here to logout 8 | {% else %} 9 | Click here to login or register 10 | {% endif %} 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /Chapter08/my_app/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block container %} 4 |

Welcome to the Authentication Demo

5 | {% if current_user.is_authenticated %} 6 |

Hey {{ current_user.username }}!!

7 | Click here to logout 8 | {% else %} 9 | Click here to login or register 10 | {% endif %} 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /Chapter04/my_app/templates/category.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

{{ category.name }}

6 |
7 | {% for product in category.products %} 8 |

9 | {{ product.name }} 10 | $ {{ product.price }} 11 |

12 | {% endfor %} 13 |
14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /Chapter05/my_app/templates/category.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

{{ category.name }}

6 |
7 | {% for product in category.products %} 8 |

9 | {{ product.name }} 10 | $ {{ product.price }} 11 |

12 | {% endfor %} 13 |
14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /Chapter07/my_app/templates/category.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

{{ category.name }}

6 |
7 | {% for product in category.products %} 8 |

9 | {{ product.name }} 10 | $ {{ product.price }} 11 |

12 | {% endfor %} 13 |
14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /Chapter09/my_app/templates/category.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

{{ category.name }}

6 |
7 | {% for product in category.products %} 8 |

9 | {{ product.name }} 10 | $ {{ product.price }} 11 |

12 | {% endfor %} 13 |
14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /Chapter10/my_app/templates/category.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

{{ category.name }}

6 |
7 | {% for product in category.products %} 8 |

9 | {{ product.name }} 10 | $ {{ product.price }} 11 |

12 | {% endfor %} 13 |
14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /Chapter11/my_app/templates/category.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

{{ category.name }}

6 |
7 | {% for product in category.products %} 8 |

9 | {{ product.name }} 10 | $ {{ product.price }} 11 |

12 | {% endfor %} 13 |
14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /Chapter12/my_app/templates/category.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

{{ category.name }}

6 |
7 | {% for product in category.products %} 8 |

9 | {{ product.name }} 10 | $ {{ product.price }} 11 |

12 | {% endfor %} 13 |
14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /Chapter13/Docker/my_app/templates/category.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |

{{ category.name }}

6 |
7 | {% for product in category.products %} 8 |

9 | {{ product.name }} 10 | $ {{ product.price }} 11 |

12 | {% endfor %} 13 |
14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /Chapter04/my_app/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block container %} 4 |

Welcome to the Catalog Home

5 | 6 | Click here to see the catalog 7 | 8 | {% endblock %} 9 | 10 | {% block scripts %} 11 | 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /Chapter05/my_app/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block container %} 4 |

Welcome to the Catalog Home

5 | 6 | Click here to see the catalog 7 | 8 | {% endblock %} 9 | 10 | {% block scripts %} 11 | 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /Chapter07/my_app/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block container %} 4 |

Welcome to the Catalog Home

5 | 6 | Click here to see the catalog 7 | 8 | {% endblock %} 9 | 10 | {% block scripts %} 11 | 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /Chapter12/my_app/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block container %} 4 |

Welcome to the Catalog Home

5 | 6 | Click here to see the catalog 7 | 8 | {% endblock %} 9 | 10 | {% block scripts %} 11 | 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /Chapter09/my_app/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block container %} 4 |

{{ _('Welcome to the Catalog Home') }}

5 | 6 | {{ _('Click here to see the catalog ') }} 7 | 8 | {% endblock %} 9 | 10 | {% block scripts %} 11 | 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /Chapter10/my_app/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block container %} 4 |

{{ _('Welcome to the Catalog Home') }}

5 | 6 | {{ _('Click here to see the catalog ') }} 7 | 8 | {% endblock %} 9 | 10 | {% block scripts %} 11 | 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /Chapter11/my_app/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block container %} 4 |

{{ _('Welcome to the Catalog Home') }}

5 | 6 | {{ _('Click here to see the catalog ') }} 7 | 8 | {% endblock %} 9 | 10 | {% block scripts %} 11 | 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /Chapter13/Docker/my_app/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block container %} 4 |

{{ _('Welcome to the Catalog Home') }}

5 | 6 | {{ _('Click here to see the catalog ') }} 7 | 8 | {% endblock %} 9 | 10 | {% block scripts %} 11 | 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /Chapter01/my_app/hello/views.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | from my_app.hello.models import MESSAGES 3 | 4 | hello = Blueprint('hello', __name__) 5 | 6 | 7 | @hello.route('/') 8 | @hello.route('/hello') 9 | def hello_world(): 10 | return MESSAGES['default'] 11 | 12 | 13 | @hello.route('/show/') 14 | def get_message(key): 15 | return MESSAGES.get(key) or "%s not found!" % key 16 | 17 | 18 | @hello.route('/add//') 19 | def add_or_update_message(key, message): 20 | MESSAGES[key] = message 21 | return "%s Added/Updated" % key 22 | -------------------------------------------------------------------------------- /Chapter02/my_app/product/models.py: -------------------------------------------------------------------------------- 1 | PRODUCTS = { 2 | 'iphone': { 3 | 'name': 'iPhone 5S', 4 | 'category': 'Phones', 5 | 'price': 699, 6 | }, 7 | 'galaxy': { 8 | 'name': 'Samsung Galaxy 5', 9 | 'category': 'Phones', 10 | 'price': 649, 11 | }, 12 | 'ipad-air': { 13 | 'name': 'iPad Air', 14 | 'category': 'Tablets', 15 | 'price': 649, 16 | }, 17 | 'ipad-mini': { 18 | 'name': 'iPad Mini', 19 | 'category': 'Tablets', 20 | 'price': 549 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Chapter08/my_app/templates/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |
9 | {{ form.csrf_token }} 10 |
{{ form.username.label }}: {{ form.username() }}
11 |
{{ form.password.label }}: {{ form.password() }}
12 | 13 |
14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /Chapter13/Lambda/my_app/hello/views.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | from my_app.hello.models import MESSAGES 3 | 4 | hello = Blueprint('hello', __name__) 5 | 6 | 7 | @hello.route('/') 8 | @hello.route('/hello') 9 | def hello_world(): 10 | return MESSAGES['default'] 11 | 12 | 13 | @hello.route('/show/') 14 | def get_message(key): 15 | return MESSAGES.get(key) or "%s not found!" % key 16 | 17 | 18 | @hello.route('/add//') 19 | def add_or_update_message(key, message): 20 | MESSAGES[key] = message 21 | return "%s Added/Updated" % key 22 | -------------------------------------------------------------------------------- /Chapter08/my_app/templates/user-update-admin.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |
9 | {{ form.csrf_token }} 10 |
{{ form.username.label }}: {{ form.username() }}
11 |
{{ form.admin.label }}: {{ form.admin() }}
12 | 13 |
14 |
15 | {% endblock %} 16 | 17 | -------------------------------------------------------------------------------- /Chapter05/my_app/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | from flask import Flask 3 | from flask_sqlalchemy import SQLAlchemy 4 | 5 | 6 | ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif']) 7 | 8 | app = Flask(__name__) 9 | app.config['UPLOAD_FOLDER'] = os.path.realpath('.') + '/my_app/static/uploads' 10 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db' 11 | app.config['WTF_CSRF_SECRET_KEY'] = 'random key for form' 12 | db = SQLAlchemy(app) 13 | 14 | app.secret_key = 'some_random_key' 15 | 16 | from my_app.catalog.views import catalog 17 | app.register_blueprint(catalog) 18 | 19 | db.create_all() 20 | -------------------------------------------------------------------------------- /Chapter11/apache_wsgi.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | WSGIScriptAlias / /home/ubuntu/workspace/cookbook11/Chapter-11/app.wsgi 4 | 5 | 6 | Require all granted 7 | Allow from all 8 | 9 | 10 | Alias /static/uploads/ "/home/ubuntu/workspace/cookbook11/Chapter-11/flask_test_uploads/" 11 | 12 | Require all granted 13 | Options Indexes 14 | Allow from all 15 | IndexOptions FancyIndexing 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Chapter13/Docker/apache_wsgi.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | WSGIScriptAlias / /home/ubuntu/workspace/cookbook11/Chapter-11/app.wsgi 4 | 5 | 6 | Require all granted 7 | Allow from all 8 | 9 | 10 | Alias /static/uploads/ "/home/ubuntu/workspace/cookbook11/Chapter-11/flask_test_uploads/" 11 | 12 | Require all granted 13 | Options Indexes 14 | Allow from all 15 | IndexOptions FancyIndexing 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Chapter06/my_app/templates/register.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |
9 | {{ form.csrf_token }} 10 |
{{ form.username.label }}: {{ form.username() }}
11 |
{{ form.password.label }}: {{ form.password() }}
12 |
{{ form.confirm.label }}: {{ form.confirm() }}
13 | 14 |
15 |
16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /Chapter08/my_app/templates/register.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |
9 | {{ form.csrf_token }} 10 |
{{ form.username.label }}: {{ form.username() }}
11 |
{{ form.password.label }}: {{ form.password() }}
12 |
{{ form.confirm.label }}: {{ form.confirm() }}
13 | 14 |
15 |
16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /Chapter11/requirements.txt: -------------------------------------------------------------------------------- 1 | Babel==2.6.0 2 | blinker==1.4 3 | boto3==1.9.145 4 | botocore==1.12.145 5 | certifi==2019.3.9 6 | chardet==3.0.4 7 | Click==7.0 8 | docutils==0.14 9 | Flask==1.0.2 10 | Flask-Babel==0.12.2 11 | Flask-SQLAlchemy==2.4.0 12 | Flask-WTF==0.14.2 13 | geoip2==2.9.0 14 | gunicorn==19.9.0 15 | idna==2.8 16 | itsdangerous==1.1.0 17 | Jinja2==2.10.1 18 | jmespath==0.9.4 19 | MarkupSafe==1.1.1 20 | maxminddb==1.4.1 21 | newrelic==4.20.0.120 22 | python-dateutil==2.8.0 23 | pytz==2019.1 24 | requests==2.21.0 25 | s3transfer==0.2.0 26 | sentry-sdk==0.7.14 27 | six==1.12.0 28 | SQLAlchemy==1.3.3 29 | urllib3==1.24.3 30 | Werkzeug==0.15.2 31 | WTForms==2.2.1 32 | -------------------------------------------------------------------------------- /Chapter08/my_app/templates/user-create-admin.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |
9 | {{ form.csrf_token }} 10 |
{{ form.username.label }}: {{ form.username() }}
11 |
{{ form.password.label }}: {{ form.password() }}
12 |
{{ form.admin.label }}: {{ form.admin() }}
13 | 14 |
15 |
16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /Chapter13/Docker/requirements.txt: -------------------------------------------------------------------------------- 1 | Babel==2.6.0 2 | blinker==1.4 3 | boto3==1.9.145 4 | botocore==1.12.145 5 | certifi==2019.3.9 6 | chardet==3.0.4 7 | Click==7.0 8 | docutils==0.14 9 | Flask==1.0.2 10 | Flask-Babel==0.12.2 11 | Flask-SQLAlchemy==2.4.0 12 | Flask-WTF==0.14.2 13 | geoip2==2.9.0 14 | gunicorn==19.9.0 15 | idna==2.8 16 | itsdangerous==1.1.0 17 | Jinja2==2.10.1 18 | jmespath==0.9.4 19 | MarkupSafe==1.1.1 20 | maxminddb==1.4.1 21 | newrelic==4.20.0.120 22 | python-dateutil==2.8.0 23 | pytz==2019.1 24 | requests==2.21.0 25 | s3transfer==0.2.0 26 | sentry-sdk==0.7.14 27 | six==1.12.0 28 | SQLAlchemy==1.3.3 29 | urllib3==1.24.3 30 | Werkzeug==0.15.2 31 | WTForms==2.2.1 32 | -------------------------------------------------------------------------------- /Chapter09/my_app/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | from flask import Flask 3 | from flask_sqlalchemy import SQLAlchemy 4 | from flask_babel import Babel 5 | 6 | 7 | ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif']) 8 | 9 | ALLOWED_LANGUAGES = { 10 | 'en': 'English', 11 | 'fr': 'French', 12 | } 13 | 14 | app = Flask(__name__) 15 | app.config['UPLOAD_FOLDER'] = os.path.realpath('.') + '/my_app/static/uploads' 16 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db' 17 | app.config['WTF_CSRF_SECRET_KEY'] = 'random key for form' 18 | db = SQLAlchemy(app) 19 | 20 | 21 | babel = Babel(app) 22 | 23 | app.secret_key = 'some_random_key' 24 | 25 | from my_app.catalog.views import catalog 26 | app.register_blueprint(catalog) 27 | 28 | db.create_all() 29 | -------------------------------------------------------------------------------- /Chapter05/my_app/templates/product-create.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |
9 | {{ form.csrf_token }} 10 |
{{ form.name.label }}: {{ form.name() }}
11 |
{{ form.price.label }}: {{ form.price() }}
12 |
{{ form.category.label }}: {{ form.category() }}
13 |
{{ form.image.label }}: {{ form.image(style='display:inline;') }}
14 | 15 |
16 |
17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /Chapter08/my_app/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_sqlalchemy import SQLAlchemy 3 | from flask_login import LoginManager 4 | from flask_admin import Admin 5 | 6 | 7 | app = Flask(__name__) 8 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db' 9 | app.config['WTF_CSRF_SECRET_KEY'] = 'random key for form' 10 | db = SQLAlchemy(app) 11 | 12 | app.secret_key = 'some_random_key' 13 | 14 | login_manager = LoginManager() 15 | login_manager.init_app(app) 16 | login_manager.login_view = 'auth.login' 17 | 18 | import my_app.auth.views as views 19 | admin = Admin(app, index_view=views.MyAdminIndexView()) 20 | admin.add_view(views.UserAdminView(views.User, db.session)) 21 | 22 | from my_app.auth.views import auth 23 | app.register_blueprint(auth) 24 | 25 | db.create_all() 26 | -------------------------------------------------------------------------------- /Chapter09/my_app/templates/product-create.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |
9 | {{ form.csrf_token }} 10 |
{{ form.name.label }}: {{ form.name() }}
11 |
{{ form.price.label }}: {{ form.price() }}
12 |
{{ form.category.label }}: {{ form.category() }}
13 |
{{ form.image.label }}: {{ form.image(style='display:inline;') }}
14 | 15 |
16 |
17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /Chapter10/my_app/templates/product-create.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |
9 | {{ form.csrf_token }} 10 |
{{ form.name.label }}: {{ form.name() }}
11 |
{{ form.price.label }}: {{ form.price() }}
12 |
{{ form.category.label }}: {{ form.category() }}
13 |
{{ form.image.label }}: {{ form.image(style='display:inline;') }}
14 | 15 |
16 |
17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /Chapter11/my_app/templates/product-create.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |
9 | {{ form.csrf_token }} 10 |
{{ form.name.label }}: {{ form.name() }}
11 |
{{ form.price.label }}: {{ form.price() }}
12 |
{{ form.category.label }}: {{ form.category() }}
13 |
{{ form.image.label }}: {{ form.image(style='display:inline;') }}
14 | 15 |
16 |
17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /Chapter13/Docker/my_app/templates/product-create.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |
9 | {{ form.csrf_token }} 10 |
{{ form.name.label }}: {{ form.name() }}
11 |
{{ form.price.label }}: {{ form.price() }}
12 |
{{ form.category.label }}: {{ form.category() }}
13 |
{{ form.image.label }}: {{ form.image(style='display:inline;') }}
14 | 15 |
16 |
17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /Chapter02/my_app/product/views.py: -------------------------------------------------------------------------------- 1 | from werkzeug.exceptions import abort 2 | from flask import render_template 3 | from flask import Blueprint 4 | from my_app.product.models import PRODUCTS 5 | 6 | product_blueprint = Blueprint('product', __name__) 7 | 8 | 9 | @product_blueprint.context_processor 10 | def some_processor(): 11 | def full_name(product): 12 | return '{0} / {1}'.format(product['category'], product['name']) 13 | return {'full_name': full_name} 14 | 15 | 16 | @product_blueprint.route('/') 17 | @product_blueprint.route('/home') 18 | def home(): 19 | return render_template('home.html', products=PRODUCTS) 20 | 21 | @product_blueprint.route('/product/') 22 | def product(key): 23 | product = PRODUCTS.get(key) 24 | if not product: 25 | abort(404) 26 | return render_template('product.html', product=product) 27 | -------------------------------------------------------------------------------- /Chapter04/my_app/templates/products.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | {% for product in products.items %} 6 |
7 |

8 | {{ product.name }} 9 | $ {{ product.price }} 10 |

11 |
12 | {% endfor %} 13 | {% if products.has_prev %} 14 | 15 | {{"<< Previous Page"}} 16 | 17 | {% else %} 18 | {{"<< Previous Page"}} 19 | {% endif %} | 20 | {% if products.has_next %} 21 | 22 | {{"Next page >>"}} 23 | 24 | {% else %} 25 | {{"Next page >>"}} 26 | {% endif %} 27 |
28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /Chapter05/my_app/templates/products.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | {% for product in products.items %} 6 |
7 |

8 | {{ product.name }} 9 | $ {{ product.price }} 10 |

11 |
12 | {% endfor %} 13 | {% if products.has_prev %} 14 | 15 | {{"<< Previous Page"}} 16 | 17 | {% else %} 18 | {{"<< Previous Page"}} 19 | {% endif %} | 20 | {% if products.has_next %} 21 | 22 | {{"Next page >>"}} 23 | 24 | {% else %} 25 | {{"Next page >>"}} 26 | {% endif %} 27 |
28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /Chapter07/my_app/templates/products.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | {% for product in products.items %} 6 |
7 |

8 | {{ product.name }} 9 | $ {{ product.price }} 10 |

11 |
12 | {% endfor %} 13 | {% if products.has_prev %} 14 | 15 | {{"<< Previous Page"}} 16 | 17 | {% else %} 18 | {{"<< Previous Page"}} 19 | {% endif %} | 20 | {% if products.has_next %} 21 | 22 | {{"Next page >>"}} 23 | 24 | {% else %} 25 | {{"Next page >>"}} 26 | {% endif %} 27 |
28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /Chapter12/my_app/templates/products.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | {% for product in products.items %} 6 |
7 |

8 | {{ product.name }} 9 | $ {{ product.price }} 10 |

11 |
12 | {% endfor %} 13 | {% if products.has_prev %} 14 | 15 | {{"<< Previous Page"}} 16 | 17 | {% else %} 18 | {{"<< Previous Page"}} 19 | {% endif %} | 20 | {% if products.has_next %} 21 | 22 | {{"Next page >>"}} 23 | 24 | {% else %} 25 | {{"Next page >>"}} 26 | {% endif %} 27 |
28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /Chapter04/my_app/catalog/models.py: -------------------------------------------------------------------------------- 1 | from my_app import db 2 | 3 | class Product(db.Model): 4 | id = db.Column(db.Integer, primary_key=True) 5 | name = db.Column(db.String(255)) 6 | price = db.Column(db.Float) 7 | category_id = db.Column(db.Integer, db.ForeignKey('category.id')) 8 | category = db.relationship( 9 | 'Category', backref=db.backref('products', lazy='dynamic') 10 | ) 11 | 12 | def __init__(self, name, price, category): 13 | self.name = name 14 | self.price = price 15 | self.category = category 16 | 17 | def __repr__(self): 18 | return '' % self.id 19 | 20 | 21 | class Category(db.Model): 22 | id = db.Column(db.Integer, primary_key=True) 23 | name = db.Column(db.String(100)) 24 | 25 | def __init__(self, name): 26 | self.name = name 27 | 28 | def __repr__(self): 29 | return '' % self.id 30 | -------------------------------------------------------------------------------- /Chapter07/my_app/catalog/models.py: -------------------------------------------------------------------------------- 1 | from my_app import db 2 | 3 | class Product(db.Model): 4 | id = db.Column(db.Integer, primary_key=True) 5 | name = db.Column(db.String(255)) 6 | price = db.Column(db.Float) 7 | category_id = db.Column(db.Integer, db.ForeignKey('category.id')) 8 | category = db.relationship( 9 | 'Category', backref=db.backref('products', lazy='dynamic') 10 | ) 11 | 12 | def __init__(self, name, price, category=None): 13 | self.name = name 14 | self.price = price 15 | self.category = category 16 | 17 | def __repr__(self): 18 | return '' % self.id 19 | 20 | 21 | class Category(db.Model): 22 | id = db.Column(db.Integer, primary_key=True) 23 | name = db.Column(db.String(100)) 24 | 25 | def __init__(self, name): 26 | self.name = name 27 | 28 | def __repr__(self): 29 | return '' % self.id 30 | -------------------------------------------------------------------------------- /Chapter01/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | import os 4 | from setuptools import setup 5 | 6 | setup( 7 | name = 'my_app', 8 | version='1.0', 9 | license='GNU General Public License v3', 10 | author='Shalabh Aggarwal', 11 | author_email='contact@shalabhaggarwal.com', 12 | description='Hello world application for Flask', 13 | packages=['my_app'], 14 | platforms='any', 15 | install_requires=[ 16 | 'flask', 17 | ], 18 | classifiers=[ 19 | 'Development Status :: 4 - Beta', 20 | 'Environment :: Web Environment', 21 | 'Intended Audience :: Developers', 22 | 'License :: OSI Approved :: GNU General Public License v3', 23 | 'Operating System :: OS Independent', 24 | 'Programming Language :: Python', 25 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 26 | 'Topic :: Software Development :: Libraries :: Python Modules' 27 | ], 28 | ) 29 | -------------------------------------------------------------------------------- /Chapter02/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | import os 4 | from setuptools import setup 5 | 6 | setup( 7 | name = 'my_app', 8 | version='1.0', 9 | license='GNU General Public License v3', 10 | author='Shalabh Aggarwal', 11 | author_email='contact@shalabhaggarwal.com', 12 | description='Hello world application for Flask', 13 | packages=['my_app'], 14 | platforms='any', 15 | install_requires=[ 16 | 'flask', 17 | ], 18 | classifiers=[ 19 | 'Development Status :: 4 - Beta', 20 | 'Environment :: Web Environment', 21 | 'Intended Audience :: Developers', 22 | 'License :: OSI Approved :: GNU General Public License v3', 23 | 'Operating System :: OS Independent', 24 | 'Programming Language :: Python', 25 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 26 | 'Topic :: Software Development :: Libraries :: Python Modules' 27 | ], 28 | ) 29 | -------------------------------------------------------------------------------- /Chapter13/Lambda/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | import os 4 | from setuptools import setup 5 | 6 | setup( 7 | name = 'my_app', 8 | version='1.0', 9 | license='GNU General Public License v3', 10 | author='Shalabh Aggarwal', 11 | author_email='contact@shalabhaggarwal.com', 12 | description='Hello world application for Flask', 13 | packages=['my_app'], 14 | platforms='any', 15 | install_requires=[ 16 | 'flask', 17 | ], 18 | classifiers=[ 19 | 'Development Status :: 4 - Beta', 20 | 'Environment :: Web Environment', 21 | 'Intended Audience :: Developers', 22 | 'License :: OSI Approved :: GNU General Public License v3', 23 | 'Operating System :: OS Independent', 24 | 'Programming Language :: Python', 25 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 26 | 'Topic :: Software Development :: Libraries :: Python Modules' 27 | ], 28 | ) 29 | -------------------------------------------------------------------------------- /Chapter09/my_app/templates/products.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | {% for product in products.items %} 6 |
7 |

8 | {{ product.name }} 9 | $ {{ product.price }} 10 |

11 |
12 | {% endfor %} 13 | {% if products.has_prev %} 14 | 15 | {{_("<< Previous Page")}} 16 | 17 | {% else %} 18 | {{_("No Previous Page")}} 19 | {% endif %} 20 | {{ ngettext('%(num)d page', '%(num)d pages', products.pages) }} | 21 | {% if products.has_next %} 22 | 23 | {{_("Next page >>")}} 24 | 25 | {% else %} 26 | {{_("No Next page")}} 27 | {% endif %} 28 |
29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /Chapter10/my_app/templates/products.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | {% for product in products.items %} 6 |
7 |

8 | {{ product.name }} 9 | $ {{ product.price }} 10 |

11 |
12 | {% endfor %} 13 | {% if products.has_prev %} 14 | 15 | {{_("<< Previous Page")}} 16 | 17 | {% else %} 18 | {{_("No Previous Page")}} 19 | {% endif %} 20 | {{ ngettext('%(num)d page', '%(num)d pages', products.pages) }} | 21 | {% if products.has_next %} 22 | 23 | {{_("Next Page >>")}} 24 | 25 | {% else %} 26 | {{_("No Next Page")}} 27 | {% endif %} 28 |
29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /Chapter11/my_app/templates/products.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | {% for product in products.items %} 6 |
7 |

8 | {{ product.name }} 9 | $ {{ product.price }} 10 |

11 |
12 | {% endfor %} 13 | {% if products.has_prev %} 14 | 15 | {{_("<< Previous Page")}} 16 | 17 | {% else %} 18 | {{_("No Previous Page")}} 19 | {% endif %} 20 | {{ ngettext('%(num)d page', '%(num)d pages', products.pages) }} | 21 | {% if products.has_next %} 22 | 23 | {{_("Next Page >>")}} 24 | 25 | {% else %} 26 | {{_("No Next Page")}} 27 | {% endif %} 28 |
29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /Chapter13/Docker/my_app/templates/products.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | {% for product in products.items %} 6 |
7 |

8 | {{ product.name }} 9 | $ {{ product.price }} 10 |

11 |
12 | {% endfor %} 13 | {% if products.has_prev %} 14 | 15 | {{_("<< Previous Page")}} 16 | 17 | {% else %} 18 | {{_("No Previous Page")}} 19 | {% endif %} 20 | {{ ngettext('%(num)d page', '%(num)d pages', products.pages) }} | 21 | {% if products.has_next %} 22 | 23 | {{_("Next Page >>")}} 24 | 25 | {% else %} 26 | {{_("No Next Page")}} 27 | {% endif %} 28 |
29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /Chapter08/my_app/templates/users-list-admin.html: -------------------------------------------------------------------------------- 1 | {% extends 'admin-home.html' %} 2 | 3 | {% block container %} 4 |

Hey {{ current_user.username }}!! Below is the list of users in system

5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {% for user in users %} 18 | 19 | 20 | 21 | 22 | 25 | 28 | 29 | {% endfor %} 30 | 31 |
IDUsernameIs Admin ?
{{ user.id }}{{ user.username }}{{ user.admin }} 23 | Edit 24 | 26 | Delete 27 |
32 | {% endblock %} 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Chapter02/my_app/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Flask Framework Cookbook 8 | 9 | 10 | 11 | 12 | 19 |
20 | {% block container %}{% endblock %} 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Chapter04/my_app/templates/product-create.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |
10 |
11 | 12 |
13 | 14 |
15 |
16 |
17 | 18 |
19 | 20 |
21 |
22 |
23 | 24 |
25 | 26 |
27 |
28 | 29 |
30 |
31 | {% endblock %} 32 | 33 | -------------------------------------------------------------------------------- /Chapter07/my_app/templates/product-create.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |
10 |
11 | 12 |
13 | 14 |
15 |
16 |
17 | 18 |
19 | 20 |
21 |
22 |
23 | 24 |
25 | 26 |
27 |
28 | 29 |
30 |
31 | {% endblock %} 32 | 33 | -------------------------------------------------------------------------------- /Chapter03/my_app/catalog/views.py: -------------------------------------------------------------------------------- 1 | from decimal import Decimal 2 | from flask import request, Blueprint, jsonify 3 | from my_app.catalog.models import Product 4 | 5 | catalog = Blueprint('catalog', __name__) 6 | 7 | 8 | @catalog.route('/') 9 | @catalog.route('/home') 10 | def home(): 11 | return "Welcome to the Catalog Home." 12 | 13 | 14 | @catalog.route('/product/') 15 | def product(key): 16 | product = Product.objects.get_or_404(key=key) 17 | return 'Product - %s, $%s' % (product.name, product.price) 18 | 19 | 20 | @catalog.route('/products') 21 | def products(): 22 | products = Product.objects.all() 23 | res = {} 24 | for product in products: 25 | res[product.key] = { 26 | 'name': product.name, 27 | 'price': str(product.price), 28 | } 29 | return jsonify(res) 30 | 31 | 32 | @catalog.route('/product-create', methods=['POST',]) 33 | def create_product(): 34 | name = request.form.get('name') 35 | key = request.form.get('key') 36 | price = request.form.get('price') 37 | product = Product( 38 | name=name, 39 | key=key, 40 | price=Decimal(price) 41 | ) 42 | product.save() 43 | return 'Product created.' 44 | -------------------------------------------------------------------------------- /Chapter11/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | import os 4 | from setuptools import setup 5 | 6 | setup( 7 | name = 'my_app', 8 | version='1.0', 9 | license='GNU General Public License v3', 10 | author='Shalabh Aggarwal', 11 | author_email='contact@shalabhaggarwal.com', 12 | description='Catalog application for Flask', 13 | packages=[ 14 | 'my_app', 15 | 'my_app.catalog', 16 | ], 17 | platforms='any', 18 | install_requires=[ 19 | 'Flask>=0.10.1', 20 | 'flask-sqlalchemy', 21 | 'flask-wtf', 22 | 'flask-babel', 23 | 'sentry-sdk', 24 | 'blinker', 25 | 'geoip2', 26 | ], 27 | include_package_data=True, 28 | classifiers=[ 29 | 'Development Status :: 4 - Beta', 30 | 'Environment :: Web Environment', 31 | 'Intended Audience :: Developers', 32 | 'License :: OSI Approved :: GNU General Public License v3', 33 | 'Operating System :: OS Independent', 34 | 'Programming Language :: Python', 35 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 36 | 'Topic :: Software Development :: Libraries :: Python Modules' 37 | ], 38 | zip_safe = False, 39 | ) 40 | -------------------------------------------------------------------------------- /Chapter13/Docker/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | import os 4 | from setuptools import setup 5 | 6 | setup( 7 | name = 'my_app', 8 | version='1.0', 9 | license='GNU General Public License v3', 10 | author='Shalabh Aggarwal', 11 | author_email='contact@shalabhaggarwal.com', 12 | description='Catalog application for Flask', 13 | packages=[ 14 | 'my_app', 15 | 'my_app.catalog', 16 | ], 17 | platforms='any', 18 | install_requires=[ 19 | 'Flask>=0.10.1', 20 | 'flask-sqlalchemy', 21 | 'flask-wtf', 22 | 'flask-babel', 23 | 'sentry-sdk', 24 | 'blinker', 25 | 'geoip2', 26 | ], 27 | include_package_data=True, 28 | classifiers=[ 29 | 'Development Status :: 4 - Beta', 30 | 'Environment :: Web Environment', 31 | 'Intended Audience :: Developers', 32 | 'License :: OSI Approved :: GNU General Public License v3', 33 | 'Operating System :: OS Independent', 34 | 'Programming Language :: Python', 35 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 36 | 'Topic :: Software Development :: Libraries :: Python Modules' 37 | ], 38 | zip_safe = False, 39 | ) 40 | -------------------------------------------------------------------------------- /Chapter06/my_app/__init__.py: -------------------------------------------------------------------------------- 1 | import ldap 2 | from flask import Flask 3 | from flask_sqlalchemy import SQLAlchemy 4 | from flask_login import LoginManager 5 | 6 | 7 | app = Flask(__name__) 8 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db' 9 | app.config['WTF_CSRF_SECRET_KEY'] = 'random key for form' 10 | app.config["FACEBOOK_OAUTH_CLIENT_ID"] = 'some facebook client ID' 11 | app.config["FACEBOOK_OAUTH_CLIENT_SECRET"] = 'some facebook client secret' 12 | app.config["GOOGLE_OAUTH_CLIENT_ID"] = "my google oauth client ID" 13 | app.config["GOOGLE_OAUTH_CLIENT_SECRET"] = "my google oauth client secret" 14 | app.config["OAUTHLIB_RELAX_TOKEN_SCOPE"] = True 15 | app.config["TWITTER_OAUTH_CLIENT_KEY"] = "twitter app api key" 16 | app.config["TWITTER_OAUTH_CLIENT_SECRET"] = "twitter app secret key" 17 | app.config['LDAP_PROVIDER_URL'] = 'ldap://localhost' 18 | db = SQLAlchemy(app) 19 | 20 | app.secret_key = 'some_random_key' 21 | 22 | login_manager = LoginManager() 23 | login_manager.init_app(app) 24 | login_manager.login_view = 'auth.login' 25 | 26 | 27 | def get_ldap_connection(): 28 | conn = ldap.initialize(app.config['LDAP_PROVIDER_URL']) 29 | return conn 30 | 31 | 32 | from my_app.auth.views import auth, facebook_blueprint, google_blueprint, twitter_blueprint 33 | app.register_blueprint(auth) 34 | app.register_blueprint(facebook_blueprint) 35 | app.register_blueprint(google_blueprint) 36 | app.register_blueprint(twitter_blueprint) 37 | 38 | 39 | db.create_all() 40 | -------------------------------------------------------------------------------- /Chapter12/my_app/templates/product-create.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 |
10 |
11 | 12 |
13 | 14 |
15 |
16 |
17 | 18 |
19 | 20 |
21 |
22 |
23 | 24 |
25 | 26 |
27 |
28 |
29 | 30 |
31 | 32 |
33 |
34 | 35 |
36 |
37 | {% endblock %} 38 | 39 | -------------------------------------------------------------------------------- /Chapter06/my_app/auth/models.py: -------------------------------------------------------------------------------- 1 | from werkzeug.security import generate_password_hash, check_password_hash 2 | from flask_wtf import FlaskForm 3 | from wtforms import TextField, PasswordField 4 | from wtforms.validators import InputRequired, EqualTo 5 | from my_app import db 6 | 7 | 8 | class User(db.Model): 9 | id = db.Column(db.Integer, primary_key=True) 10 | username = db.Column(db.String(100)) 11 | pwdhash = db.Column(db.String()) 12 | 13 | def __init__(self, username, password): 14 | self.username = username 15 | self.pwdhash = generate_password_hash(password) 16 | 17 | def check_password(self, password): 18 | return check_password_hash(self.pwdhash, password) 19 | 20 | @property 21 | def is_authenticated(self): 22 | return True 23 | 24 | @property 25 | def is_active(self): 26 | return True 27 | 28 | @property 29 | def is_anonymous(self): 30 | return False 31 | 32 | def get_id(self): 33 | return str(self.id) 34 | 35 | 36 | class RegistrationForm(FlaskForm): 37 | username = TextField('Username', [InputRequired()]) 38 | password = PasswordField( 39 | 'Password', [ 40 | InputRequired(), EqualTo('confirm', message='Passwords must match') 41 | ] 42 | ) 43 | confirm = PasswordField('Confirm Password', [InputRequired()]) 44 | 45 | 46 | class LoginForm(FlaskForm): 47 | username = TextField('Username', [InputRequired()]) 48 | password = PasswordField('Password', [InputRequired()]) 49 | -------------------------------------------------------------------------------- /Chapter06/my_app/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Flask Framework Cookbook 8 | 9 | 10 | 11 | 12 | 19 |
20 |
21 |
22 | {% for category, message in get_flashed_messages(with_categories=true) %} 23 |
24 | 25 | {{ message }} 26 |
27 | {% endfor %} 28 |
29 | {% block container %}{% endblock %} 30 |
31 | 32 | 33 | 34 | 35 | {% block scripts %} 36 | {% endblock %} 37 | 38 | 39 | -------------------------------------------------------------------------------- /Chapter04/my_app/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Flask Framework Cookbook 8 | 9 | 10 | 11 | 12 | 19 |
20 |
21 |
22 | {% for category, message in get_flashed_messages(with_categories=true) %} 23 |
24 | 25 | {{ message }} 26 |
27 | {% endfor %} 28 |
29 | {% block container %}{% endblock %} 30 |
31 | 32 | 33 | 34 | 35 | {% block scripts %} 36 | {% endblock %} 37 | 38 | 39 | -------------------------------------------------------------------------------- /Chapter05/my_app/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Flask Framework Cookbook 8 | 9 | 10 | 11 | 12 | 19 |
20 |
21 |
22 | {% for category, message in get_flashed_messages(with_categories=true) %} 23 |
24 | 25 | {{ message }} 26 |
27 | {% endfor %} 28 |
29 | {% block container %}{% endblock %} 30 |
31 | 32 | 33 | 34 | 35 | {% block scripts %} 36 | {% endblock %} 37 | 38 | 39 | -------------------------------------------------------------------------------- /Chapter07/my_app/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Flask Framework Cookbook 8 | 9 | 10 | 11 | 12 | 19 |
20 |
21 |
22 | {% for category, message in get_flashed_messages(with_categories=true) %} 23 |
24 | 25 | {{ message }} 26 |
27 | {% endfor %} 28 |
29 | {% block container %}{% endblock %} 30 |
31 | 32 | 33 | 34 | 35 | {% block scripts %} 36 | {% endblock %} 37 | 38 | 39 | -------------------------------------------------------------------------------- /Chapter09/my_app/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Flask Framework Cookbook 8 | 9 | 10 | 11 | 12 | 19 |
20 |
21 |
22 | {% for category, message in get_flashed_messages(with_categories=true) %} 23 |
24 | 25 | {{ message }} 26 |
27 | {% endfor %} 28 |
29 | {% block container %}{% endblock %} 30 |
31 | 32 | 33 | 34 | 35 | {% block scripts %} 36 | {% endblock %} 37 | 38 | 39 | -------------------------------------------------------------------------------- /Chapter12/my_app/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Flask Framework Cookbook 8 | 9 | 10 | 11 | 12 | 19 |
20 |
21 |
22 | {% for category, message in get_flashed_messages(with_categories=true) %} 23 |
24 | 25 | {{ message }} 26 |
27 | {% endfor %} 28 |
29 | {% block container %}{% endblock %} 30 |
31 | 32 | 33 | 34 | 35 | {% block scripts %} 36 | {% endblock %} 37 | 38 | 39 | -------------------------------------------------------------------------------- /Chapter10/my_app/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Flask Framework Cookbook 8 | 9 | 10 | 11 | 12 | 13 | 20 |
21 |
22 |
23 | {% for category, message in get_flashed_messages(with_categories=true) %} 24 |
25 | 26 | {{ message }} 27 |
28 | {% endfor %} 29 |
30 | {% block container %}{% endblock %} 31 |
32 | 33 | 34 | 35 | 36 | {% block scripts %} 37 | {% endblock %} 38 | 39 | 40 | -------------------------------------------------------------------------------- /Chapter11/my_app/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Flask Framework Cookbook 8 | 9 | 10 | 11 | 12 | 13 | 20 |
21 |
22 |
23 | {% for category, message in get_flashed_messages(with_categories=true) %} 24 |
25 | 26 | {{ message }} 27 |
28 | {% endfor %} 29 |
30 | {% block container %}{% endblock %} 31 |
32 | 33 | 34 | 35 | 36 | {% block scripts %} 37 | {% endblock %} 38 | 39 | 40 | -------------------------------------------------------------------------------- /Chapter13/Docker/my_app/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Flask Framework Cookbook 8 | 9 | 10 | 11 | 12 | 13 | 20 |
21 |
22 |
23 | {% for category, message in get_flashed_messages(with_categories=true) %} 24 |
25 | 26 | {{ message }} 27 |
28 | {% endfor %} 29 |
30 | {% block container %}{% endblock %} 31 |
32 | 33 | 34 | 35 | 36 | {% block scripts %} 37 | {% endblock %} 38 | 39 | 40 | -------------------------------------------------------------------------------- /Chapter12/my_app/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_sqlalchemy import SQLAlchemy 3 | from elasticsearch import Elasticsearch 4 | from flask_caching import Cache 5 | from flask_mail import Mail 6 | from celery import Celery 7 | 8 | 9 | 10 | app = Flask(__name__) 11 | app.config['SERVER_NAME'] = 'localhost:5000' 12 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db' 13 | app.config['WHOOSH_BASE'] = '/tmp/whoosh' 14 | db = SQLAlchemy(app) 15 | cache = Cache(app, config={'CACHE_TYPE': 'simple'}) 16 | 17 | app.config['MAIL_SERVER'] = 'smtp.gmail.com' 18 | app.config['MAIL_PORT'] = 587 19 | app.config['MAIL_USE_TLS'] = True 20 | app.config['MAIL_USERNAME'] = 'gmail_username' 21 | app.config['MAIL_PASSWORD'] = 'gmail_password' 22 | app.config['MAIL_DEFAULT_SENDER'] = ('Sender name', 'sender email') 23 | mail = Mail(app) 24 | 25 | app.secret_key = 'some_random_key' 26 | 27 | es = Elasticsearch('http://localhost:9200/') 28 | es.indices.create('catalog', ignore=400) 29 | 30 | app.config.update( 31 | CELERY_BROKER_URL='redis://localhost:6379', 32 | CELERY_RESULT_BACKEND='redis://localhost:6379' 33 | ) 34 | 35 | def make_celery(app): 36 | celery = Celery( 37 | app.import_name, broker=app.config['CELERY_BROKER_URL'] 38 | ) 39 | celery.conf.update(app.config) 40 | TaskBase = celery.Task 41 | class ContextTask(TaskBase): 42 | abstract = True 43 | def __call__(self, *args, **kwargs): 44 | with app.app_context(): 45 | return TaskBase.__call__(self, *args, **kwargs) 46 | celery.Task = ContextTask 47 | return celery 48 | 49 | celery = make_celery(app) 50 | 51 | from my_app.catalog.views import catalog 52 | app.register_blueprint(catalog) 53 | 54 | db.create_all() 55 | -------------------------------------------------------------------------------- /Chapter08/my_app/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Flask Framework Cookbook 8 | 9 | 10 | 11 | 12 | 24 |
25 |
26 |
27 | {% for category, message in get_flashed_messages(with_categories=true) %} 28 |
29 | 30 | {{ message }} 31 |
32 | {% endfor %} 33 |
34 | {% block container %}{% endblock %} 35 |
36 | 37 | 38 | 39 | 40 | {% block scripts %} 41 | {% endblock %} 42 | 43 | 44 | -------------------------------------------------------------------------------- /Chapter10/my_app/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | from flask import Flask 3 | from flask_sqlalchemy import SQLAlchemy 4 | from flask_babel import Babel 5 | import sentry_sdk 6 | from sentry_sdk.integrations.flask import FlaskIntegration 7 | 8 | 9 | ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif']) 10 | 11 | ALLOWED_LANGUAGES = { 12 | 'en': 'English', 13 | 'fr': 'French', 14 | } 15 | 16 | RECEPIENTS = ['some_receiver@gmail.com'] 17 | 18 | sentry_sdk.init( 19 | dsn="https://1234:5678@fake-sentry-server/1", 20 | integrations=[FlaskIntegration()] 21 | ) 22 | 23 | app = Flask(__name__) 24 | app.config['UPLOAD_FOLDER'] = os.path.realpath('.') + '/my_app/static/uploads' 25 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db' 26 | app.config['WTF_CSRF_SECRET_KEY'] = 'random key for form' 27 | db = SQLAlchemy(app) 28 | 29 | app.config['LOG_FILE'] = 'application.log' 30 | 31 | 32 | if not app.debug: 33 | import logging 34 | logging.basicConfig(level=logging.INFO) 35 | from logging import FileHandler, Formatter 36 | from logging.handlers import SMTPHandler 37 | file_handler = FileHandler(app.config['LOG_FILE']) 38 | app.logger.addHandler(file_handler) 39 | mail_handler = SMTPHandler( 40 | ("smtp.gmail.com", 587), 'sender@gmail.com', RECEPIENTS, 41 | 'Error occurred in your application', 42 | ('some_email@gmail.com', 'some_gmail_password'), secure=()) 43 | mail_handler.setLevel(logging.ERROR) 44 | # app.logger.addHandler(mail_handler) 45 | for handler in [file_handler, mail_handler]: 46 | handler.setFormatter(Formatter( 47 | '%(asctime)s %(levelname)s: %(message)s ' 48 | '[in %(pathname)s:%(lineno)d]' 49 | )) 50 | 51 | 52 | babel = Babel(app) 53 | 54 | app.secret_key = 'some_random_key' 55 | 56 | from my_app.catalog.views import catalog 57 | app.register_blueprint(catalog) 58 | 59 | db.create_all() 60 | -------------------------------------------------------------------------------- /Chapter12/my_app/catalog/models.py: -------------------------------------------------------------------------------- 1 | import flask_whooshalchemy 2 | from blinker import Namespace 3 | from my_app import es 4 | from my_app import db, app 5 | 6 | 7 | catalog_signals = Namespace() 8 | product_created = catalog_signals.signal('product-created') 9 | category_created = catalog_signals.signal('category-created') 10 | 11 | 12 | class Product(db.Model): 13 | __searchable__ = ['name', 'company'] 14 | 15 | id = db.Column(db.Integer, primary_key=True) 16 | name = db.Column(db.String(255)) 17 | price = db.Column(db.Float) 18 | company = db.Column(db.String(255)) 19 | category_id = db.Column(db.Integer, db.ForeignKey('category.id')) 20 | category = db.relationship( 21 | 'Category', backref=db.backref('products', lazy='dynamic') 22 | ) 23 | 24 | def __init__(self, name, price, category, company=''): 25 | self.name = name 26 | self.price = price 27 | self.category = category 28 | self.company = company 29 | 30 | def __repr__(self): 31 | return '' % self.id 32 | 33 | 34 | def add_product_index_to_es(sender, product): 35 | es.index(index='catalog', body={ 36 | 'name': product.name, 37 | 'category': product.category.name 38 | }, id=product.id) 39 | es.indices.refresh(index='catalog') 40 | 41 | product_created.connect(add_product_index_to_es, app) 42 | 43 | 44 | class Category(db.Model): 45 | __searchable__ = ['name'] 46 | 47 | id = db.Column(db.Integer, primary_key=True) 48 | name = db.Column(db.String(100)) 49 | 50 | def __init__(self, name): 51 | self.name = name 52 | 53 | def __repr__(self): 54 | return '' % self.id 55 | 56 | 57 | def add_category_index_to_es(sender, category): 58 | es.index(index='catalog', body={ 59 | 'name': category.name, 60 | }, id=category.id) 61 | es.indices.refresh('catalog') 62 | 63 | category_created.connect(add_category_index_to_es, app) 64 | -------------------------------------------------------------------------------- /Chapter06/my_app/templates/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block container %} 4 |
5 | 10 |
11 |
12 |
13 |
17 | {{ form.csrf_token }} 18 |
{{ form.username.label }}: {{ form.username() }}
19 |
{{ form.password.label }}: {{ form.password() }}
20 | 21 |
22 |
23 |
24 | Login via Facebook 25 |
26 | Login via Google 27 |
28 | Login via Twitter 29 |
30 |
31 |
32 |
36 | {{ form.csrf_token }} 37 |
{{ form.username.label }}: {{ form.username() }}
38 |
{{ form.password.label }}: {{ form.password() }}
39 | 40 |
41 |
42 |
43 |
44 | {% endblock %} 45 | -------------------------------------------------------------------------------- /Chapter09/my_app/messages.pot: -------------------------------------------------------------------------------- 1 | # Translations template for PROJECT. 2 | # Copyright (C) 2014 ORGANIZATION 3 | # This file is distributed under the same license as the PROJECT project. 4 | # FIRST AUTHOR , 2014. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PROJECT VERSION\n" 10 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 11 | "POT-Creation-Date: 2014-10-22 12:19+0530\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=utf-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Generated-By: Babel 1.3\n" 19 | 20 | #: my_app/catalog/models.py:42 my_app/catalog/models.py:105 21 | msgid "Name" 22 | msgstr "" 23 | 24 | #: my_app/catalog/models.py:74 25 | msgid "Not a valid choice" 26 | msgstr "" 27 | 28 | #: my_app/catalog/models.py:78 29 | msgid "Price" 30 | msgstr "" 31 | 32 | #: my_app/catalog/models.py:82 33 | msgid "Category" 34 | msgstr "" 35 | 36 | #: my_app/catalog/models.py:84 37 | msgid "Product Image" 38 | msgstr "" 39 | 40 | #: my_app/catalog/views.py:86 41 | #, python-format 42 | msgid "The product %(name)s has been created" 43 | msgstr "" 44 | 45 | #: my_app/catalog/views.py:128 46 | #, python-format 47 | msgid "The category %(name)s has been created" 48 | msgstr "" 49 | 50 | #: my_app/templates/home.html:4 51 | msgid "Welcome to the Catalog Home" 52 | msgstr "" 53 | 54 | #: my_app/templates/home.html:6 55 | msgid "Click here to see the catalog " 56 | msgstr "" 57 | 58 | #: my_app/templates/products.html:15 59 | msgid "<< Previous Page" 60 | msgstr "" 61 | 62 | #: my_app/templates/products.html:18 63 | msgid "No Previous Page" 64 | msgstr "" 65 | 66 | #: my_app/templates/products.html:20 67 | #, python-format 68 | msgid "%(num)d page" 69 | msgid_plural "%(num)d pages" 70 | msgstr[0] "" 71 | msgstr[1] "" 72 | 73 | #: my_app/templates/products.html:23 74 | msgid "Next page >>" 75 | msgstr "" 76 | 77 | #: my_app/templates/products.html:26 78 | msgid "No Next page" 79 | msgstr "" 80 | 81 | -------------------------------------------------------------------------------- /Chapter10/my_app/messages.pot: -------------------------------------------------------------------------------- 1 | # Translations template for PROJECT. 2 | # Copyright (C) 2014 ORGANIZATION 3 | # This file is distributed under the same license as the PROJECT project. 4 | # FIRST AUTHOR , 2014. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PROJECT VERSION\n" 10 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 11 | "POT-Creation-Date: 2014-10-22 12:19+0530\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=utf-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Generated-By: Babel 1.3\n" 19 | 20 | #: my_app/catalog/models.py:42 my_app/catalog/models.py:105 21 | msgid "Name" 22 | msgstr "" 23 | 24 | #: my_app/catalog/models.py:74 25 | msgid "Not a valid choice" 26 | msgstr "" 27 | 28 | #: my_app/catalog/models.py:78 29 | msgid "Price" 30 | msgstr "" 31 | 32 | #: my_app/catalog/models.py:82 33 | msgid "Category" 34 | msgstr "" 35 | 36 | #: my_app/catalog/models.py:84 37 | msgid "Product Image" 38 | msgstr "" 39 | 40 | #: my_app/catalog/views.py:86 41 | #, python-format 42 | msgid "The product %(name)s has been created" 43 | msgstr "" 44 | 45 | #: my_app/catalog/views.py:128 46 | #, python-format 47 | msgid "The category %(name)s has been created" 48 | msgstr "" 49 | 50 | #: my_app/templates/home.html:4 51 | msgid "Welcome to the Catalog Home" 52 | msgstr "" 53 | 54 | #: my_app/templates/home.html:6 55 | msgid "Click here to see the catalog " 56 | msgstr "" 57 | 58 | #: my_app/templates/products.html:15 59 | msgid "<< Previous Page" 60 | msgstr "" 61 | 62 | #: my_app/templates/products.html:18 63 | msgid "No Previous Page" 64 | msgstr "" 65 | 66 | #: my_app/templates/products.html:20 67 | #, python-format 68 | msgid "%(num)d page" 69 | msgid_plural "%(num)d pages" 70 | msgstr[0] "" 71 | msgstr[1] "" 72 | 73 | #: my_app/templates/products.html:23 74 | msgid "Next page >>" 75 | msgstr "" 76 | 77 | #: my_app/templates/products.html:26 78 | msgid "No Next page" 79 | msgstr "" 80 | 81 | -------------------------------------------------------------------------------- /Chapter11/my_app/messages.pot: -------------------------------------------------------------------------------- 1 | # Translations template for PROJECT. 2 | # Copyright (C) 2014 ORGANIZATION 3 | # This file is distributed under the same license as the PROJECT project. 4 | # FIRST AUTHOR , 2014. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PROJECT VERSION\n" 10 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 11 | "POT-Creation-Date: 2014-10-22 12:19+0530\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=utf-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Generated-By: Babel 1.3\n" 19 | 20 | #: my_app/catalog/models.py:42 my_app/catalog/models.py:105 21 | msgid "Name" 22 | msgstr "" 23 | 24 | #: my_app/catalog/models.py:74 25 | msgid "Not a valid choice" 26 | msgstr "" 27 | 28 | #: my_app/catalog/models.py:78 29 | msgid "Price" 30 | msgstr "" 31 | 32 | #: my_app/catalog/models.py:82 33 | msgid "Category" 34 | msgstr "" 35 | 36 | #: my_app/catalog/models.py:84 37 | msgid "Product Image" 38 | msgstr "" 39 | 40 | #: my_app/catalog/views.py:86 41 | #, python-format 42 | msgid "The product %(name)s has been created" 43 | msgstr "" 44 | 45 | #: my_app/catalog/views.py:128 46 | #, python-format 47 | msgid "The category %(name)s has been created" 48 | msgstr "" 49 | 50 | #: my_app/templates/home.html:4 51 | msgid "Welcome to the Catalog Home" 52 | msgstr "" 53 | 54 | #: my_app/templates/home.html:6 55 | msgid "Click here to see the catalog " 56 | msgstr "" 57 | 58 | #: my_app/templates/products.html:15 59 | msgid "<< Previous Page" 60 | msgstr "" 61 | 62 | #: my_app/templates/products.html:18 63 | msgid "No Previous Page" 64 | msgstr "" 65 | 66 | #: my_app/templates/products.html:20 67 | #, python-format 68 | msgid "%(num)d page" 69 | msgid_plural "%(num)d pages" 70 | msgstr[0] "" 71 | msgstr[1] "" 72 | 73 | #: my_app/templates/products.html:23 74 | msgid "Next page >>" 75 | msgstr "" 76 | 77 | #: my_app/templates/products.html:26 78 | msgid "No Next page" 79 | msgstr "" 80 | 81 | -------------------------------------------------------------------------------- /Chapter13/Docker/my_app/messages.pot: -------------------------------------------------------------------------------- 1 | # Translations template for PROJECT. 2 | # Copyright (C) 2014 ORGANIZATION 3 | # This file is distributed under the same license as the PROJECT project. 4 | # FIRST AUTHOR , 2014. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PROJECT VERSION\n" 10 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 11 | "POT-Creation-Date: 2014-10-22 12:19+0530\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=utf-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Generated-By: Babel 1.3\n" 19 | 20 | #: my_app/catalog/models.py:42 my_app/catalog/models.py:105 21 | msgid "Name" 22 | msgstr "" 23 | 24 | #: my_app/catalog/models.py:74 25 | msgid "Not a valid choice" 26 | msgstr "" 27 | 28 | #: my_app/catalog/models.py:78 29 | msgid "Price" 30 | msgstr "" 31 | 32 | #: my_app/catalog/models.py:82 33 | msgid "Category" 34 | msgstr "" 35 | 36 | #: my_app/catalog/models.py:84 37 | msgid "Product Image" 38 | msgstr "" 39 | 40 | #: my_app/catalog/views.py:86 41 | #, python-format 42 | msgid "The product %(name)s has been created" 43 | msgstr "" 44 | 45 | #: my_app/catalog/views.py:128 46 | #, python-format 47 | msgid "The category %(name)s has been created" 48 | msgstr "" 49 | 50 | #: my_app/templates/home.html:4 51 | msgid "Welcome to the Catalog Home" 52 | msgstr "" 53 | 54 | #: my_app/templates/home.html:6 55 | msgid "Click here to see the catalog " 56 | msgstr "" 57 | 58 | #: my_app/templates/products.html:15 59 | msgid "<< Previous Page" 60 | msgstr "" 61 | 62 | #: my_app/templates/products.html:18 63 | msgid "No Previous Page" 64 | msgstr "" 65 | 66 | #: my_app/templates/products.html:20 67 | #, python-format 68 | msgid "%(num)d page" 69 | msgid_plural "%(num)d pages" 70 | msgstr[0] "" 71 | msgstr[1] "" 72 | 73 | #: my_app/templates/products.html:23 74 | msgid "Next page >>" 75 | msgstr "" 76 | 77 | #: my_app/templates/products.html:26 78 | msgid "No Next page" 79 | msgstr "" 80 | 81 | -------------------------------------------------------------------------------- /Chapter11/my_app/__init__.py: -------------------------------------------------------------------------------- 1 | import newrelic.agent 2 | newrelic.agent.initialize('newrelic.ini') 3 | 4 | import os 5 | from flask import Flask 6 | from flask_sqlalchemy import SQLAlchemy 7 | from flask_babel import Babel 8 | import sentry_sdk 9 | from sentry_sdk.integrations.flask import FlaskIntegration 10 | 11 | 12 | ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif']) 13 | 14 | ALLOWED_LANGUAGES = { 15 | 'en': 'English', 16 | 'fr': 'French', 17 | } 18 | 19 | RECEPIENTS = ['some_receiver@gmail.com'] 20 | 21 | sentry_sdk.init( 22 | dsn="https://1234:5678@fake-sentry-server/1", 23 | integrations=[FlaskIntegration()] 24 | ) 25 | 26 | app = Flask(__name__) 27 | app.config['UPLOAD_FOLDER'] = '/Users/shalabhaggarwal/workspace/mydev/flask_test_uploads' 28 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db' 29 | app.config['WTF_CSRF_SECRET_KEY'] = 'random key for form' 30 | app.config['AWS_ACCESS_KEY'] = 'Amazon Access Key' 31 | app.config['AWS_SECRET_KEY'] = 'Amazon Secret Key' 32 | app.config['AWS_BUCKET'] = 'flask-cookbook' 33 | db = SQLAlchemy(app) 34 | 35 | app.config['LOG_FILE'] = '/tmp/application.log' 36 | 37 | 38 | if not app.debug: 39 | import logging 40 | logging.basicConfig(level=logging.INFO) 41 | from logging import FileHandler, Formatter 42 | from logging.handlers import SMTPHandler 43 | file_handler = FileHandler(app.config['LOG_FILE']) 44 | app.logger.addHandler(file_handler) 45 | mail_handler = SMTPHandler( 46 | ("smtp.gmail.com", 587), 'sender@gmail.com', RECEPIENTS, 47 | 'Error occurred in your application', 48 | ('some_email@gmail.com', 'some_gmail_password'), secure=()) 49 | mail_handler.setLevel(logging.ERROR) 50 | # app.logger.addHandler(mail_handler) 51 | for handler in [file_handler, mail_handler]: 52 | handler.setFormatter(Formatter( 53 | '%(asctime)s %(levelname)s: %(message)s ' 54 | '[in %(pathname)s:%(lineno)d]' 55 | )) 56 | 57 | 58 | babel = Babel(app) 59 | 60 | app.secret_key = 'some_random_key' 61 | 62 | from my_app.catalog.views import catalog 63 | app.register_blueprint(catalog) 64 | 65 | db.create_all() 66 | -------------------------------------------------------------------------------- /Chapter13/Docker/my_app/__init__.py: -------------------------------------------------------------------------------- 1 | import newrelic.agent 2 | newrelic.agent.initialize('newrelic.ini') 3 | 4 | import os 5 | from flask import Flask 6 | from flask_sqlalchemy import SQLAlchemy 7 | from flask_babel import Babel 8 | import sentry_sdk 9 | from sentry_sdk.integrations.flask import FlaskIntegration 10 | 11 | 12 | ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif']) 13 | 14 | ALLOWED_LANGUAGES = { 15 | 'en': 'English', 16 | 'fr': 'French', 17 | } 18 | 19 | RECEPIENTS = ['some_receiver@gmail.com'] 20 | 21 | sentry_sdk.init( 22 | dsn="https://1234:5678@fake-sentry-server/1", 23 | integrations=[FlaskIntegration()] 24 | ) 25 | 26 | app = Flask(__name__) 27 | app.config['UPLOAD_FOLDER'] = '/Users/shalabhaggarwal/workspace/mydev/flask_test_uploads' 28 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db' 29 | app.config['WTF_CSRF_SECRET_KEY'] = 'random key for form' 30 | app.config['AWS_ACCESS_KEY'] = 'Amazon Access Key' 31 | app.config['AWS_SECRET_KEY'] = 'Amazon Secret Key' 32 | app.config['AWS_BUCKET'] = 'flask-cookbook' 33 | db = SQLAlchemy(app) 34 | 35 | app.config['LOG_FILE'] = '/tmp/application.log' 36 | 37 | 38 | if not app.debug: 39 | import logging 40 | logging.basicConfig(level=logging.INFO) 41 | from logging import FileHandler, Formatter 42 | from logging.handlers import SMTPHandler 43 | file_handler = FileHandler(app.config['LOG_FILE']) 44 | app.logger.addHandler(file_handler) 45 | mail_handler = SMTPHandler( 46 | ("smtp.gmail.com", 587), 'sender@gmail.com', RECEPIENTS, 47 | 'Error occurred in your application', 48 | ('some_email@gmail.com', 'some_gmail_password'), secure=()) 49 | mail_handler.setLevel(logging.ERROR) 50 | # app.logger.addHandler(mail_handler) 51 | for handler in [file_handler, mail_handler]: 52 | handler.setFormatter(Formatter( 53 | '%(asctime)s %(levelname)s: %(message)s ' 54 | '[in %(pathname)s:%(lineno)d]' 55 | )) 56 | 57 | 58 | babel = Babel(app) 59 | 60 | app.secret_key = 'some_random_key' 61 | 62 | from my_app.catalog.views import catalog 63 | app.register_blueprint(catalog) 64 | 65 | db.create_all() 66 | -------------------------------------------------------------------------------- /Chapter09/my_app/translations/fr/LC_MESSAGES/messages.po: -------------------------------------------------------------------------------- 1 | # French translations for PROJECT. 2 | # Copyright (C) 2014 ORGANIZATION 3 | # This file is distributed under the same license as the PROJECT project. 4 | # FIRST AUTHOR , 2014. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PROJECT VERSION\n" 10 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 11 | "POT-Creation-Date: 2014-10-22 12:19+0530\n" 12 | "PO-Revision-Date: 2014-10-22 11:48+0530\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language: fr\n" 15 | "Language-Team: fr \n" 16 | "Plural-Forms: nplurals=2; plural=(n > 1)\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=utf-8\n" 19 | "Content-Transfer-Encoding: 8bit\n" 20 | "Generated-By: Babel 2.6.0\n" 21 | 22 | #: my_app/catalog/models.py:42 my_app/catalog/models.py:105 23 | msgid "Name" 24 | msgstr "Nom" 25 | 26 | #: my_app/catalog/models.py:74 27 | msgid "Not a valid choice" 28 | msgstr "" 29 | 30 | #: my_app/catalog/models.py:78 31 | msgid "Price" 32 | msgstr "Prix" 33 | 34 | #: my_app/catalog/models.py:82 35 | msgid "Category" 36 | msgstr "Catégorie" 37 | 38 | #: my_app/catalog/models.py:84 39 | msgid "Product Image" 40 | msgstr "Image du produit" 41 | 42 | #: my_app/catalog/views.py:86 43 | #, python-format 44 | msgid "The product %(name)s has been created" 45 | msgstr "Le produit %(name)s a été créé." 46 | 47 | #: my_app/catalog/views.py:128 48 | #, python-format 49 | msgid "The category %(name)s has been created" 50 | msgstr "" 51 | 52 | #: my_app/templates/home.html:4 53 | msgid "Welcome to the Catalog Home" 54 | msgstr "Bienvenue sur le catalogue Accueil" 55 | 56 | #: my_app/templates/home.html:6 57 | msgid "Click here to see the catalog " 58 | msgstr "Cliquez ici pour voir le catalogue " 59 | 60 | #: my_app/templates/products.html:15 61 | msgid "<< Previous Page" 62 | msgstr "" 63 | 64 | #: my_app/templates/products.html:18 65 | msgid "No Previous Page" 66 | msgstr "" 67 | 68 | #: my_app/templates/products.html:20 69 | #, python-format 70 | msgid "%(num)d page" 71 | msgid_plural "%(num)d pages" 72 | msgstr[0] "%(num)d page" 73 | msgstr[1] "%(num)d pages" 74 | 75 | #: my_app/templates/products.html:23 76 | msgid "Next page >>" 77 | msgstr "" 78 | 79 | #: my_app/templates/products.html:26 80 | msgid "No Next page" 81 | msgstr "" 82 | 83 | -------------------------------------------------------------------------------- /Chapter10/my_app/translations/fr/LC_MESSAGES/messages.po: -------------------------------------------------------------------------------- 1 | # French translations for PROJECT. 2 | # Copyright (C) 2014 ORGANIZATION 3 | # This file is distributed under the same license as the PROJECT project. 4 | # FIRST AUTHOR , 2014. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PROJECT VERSION\n" 10 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 11 | "POT-Creation-Date: 2014-10-22 12:19+0530\n" 12 | "PO-Revision-Date: 2014-10-22 11:48+0530\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language: fr\n" 15 | "Language-Team: fr \n" 16 | "Plural-Forms: nplurals=2; plural=(n > 1)\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=utf-8\n" 19 | "Content-Transfer-Encoding: 8bit\n" 20 | "Generated-By: Babel 2.6.0\n" 21 | 22 | #: my_app/catalog/models.py:42 my_app/catalog/models.py:105 23 | msgid "Name" 24 | msgstr "Nom" 25 | 26 | #: my_app/catalog/models.py:74 27 | msgid "Not a valid choice" 28 | msgstr "" 29 | 30 | #: my_app/catalog/models.py:78 31 | msgid "Price" 32 | msgstr "Prix" 33 | 34 | #: my_app/catalog/models.py:82 35 | msgid "Category" 36 | msgstr "Catégorie" 37 | 38 | #: my_app/catalog/models.py:84 39 | msgid "Product Image" 40 | msgstr "Image du produit" 41 | 42 | #: my_app/catalog/views.py:86 43 | #, python-format 44 | msgid "The product %(name)s has been created" 45 | msgstr "Le produit %(name)s a été créé." 46 | 47 | #: my_app/catalog/views.py:128 48 | #, python-format 49 | msgid "The category %(name)s has been created" 50 | msgstr "" 51 | 52 | #: my_app/templates/home.html:4 53 | msgid "Welcome to the Catalog Home" 54 | msgstr "Bienvenue sur le catalogue Accueil" 55 | 56 | #: my_app/templates/home.html:6 57 | msgid "Click here to see the catalog " 58 | msgstr "Cliquez ici pour voir le catalogue " 59 | 60 | #: my_app/templates/products.html:15 61 | msgid "<< Previous Page" 62 | msgstr "" 63 | 64 | #: my_app/templates/products.html:18 65 | msgid "No Previous Page" 66 | msgstr "" 67 | 68 | #: my_app/templates/products.html:20 69 | #, python-format 70 | msgid "%(num)d page" 71 | msgid_plural "%(num)d pages" 72 | msgstr[0] "%(num)d page" 73 | msgstr[1] "%(num)d pages" 74 | 75 | #: my_app/templates/products.html:23 76 | msgid "Next page >>" 77 | msgstr "" 78 | 79 | #: my_app/templates/products.html:26 80 | msgid "No Next page" 81 | msgstr "" 82 | 83 | -------------------------------------------------------------------------------- /Chapter11/my_app/translations/fr/LC_MESSAGES/messages.po: -------------------------------------------------------------------------------- 1 | # French translations for PROJECT. 2 | # Copyright (C) 2014 ORGANIZATION 3 | # This file is distributed under the same license as the PROJECT project. 4 | # FIRST AUTHOR , 2014. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PROJECT VERSION\n" 10 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 11 | "POT-Creation-Date: 2014-10-22 12:19+0530\n" 12 | "PO-Revision-Date: 2014-10-22 11:48+0530\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language: fr\n" 15 | "Language-Team: fr \n" 16 | "Plural-Forms: nplurals=2; plural=(n > 1)\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=utf-8\n" 19 | "Content-Transfer-Encoding: 8bit\n" 20 | "Generated-By: Babel 2.6.0\n" 21 | 22 | #: my_app/catalog/models.py:42 my_app/catalog/models.py:105 23 | msgid "Name" 24 | msgstr "Nom" 25 | 26 | #: my_app/catalog/models.py:74 27 | msgid "Not a valid choice" 28 | msgstr "" 29 | 30 | #: my_app/catalog/models.py:78 31 | msgid "Price" 32 | msgstr "Prix" 33 | 34 | #: my_app/catalog/models.py:82 35 | msgid "Category" 36 | msgstr "Catégorie" 37 | 38 | #: my_app/catalog/models.py:84 39 | msgid "Product Image" 40 | msgstr "Image du produit" 41 | 42 | #: my_app/catalog/views.py:86 43 | #, python-format 44 | msgid "The product %(name)s has been created" 45 | msgstr "Le produit %(name)s a été créé." 46 | 47 | #: my_app/catalog/views.py:128 48 | #, python-format 49 | msgid "The category %(name)s has been created" 50 | msgstr "" 51 | 52 | #: my_app/templates/home.html:4 53 | msgid "Welcome to the Catalog Home" 54 | msgstr "Bienvenue sur le catalogue Accueil" 55 | 56 | #: my_app/templates/home.html:6 57 | msgid "Click here to see the catalog " 58 | msgstr "Cliquez ici pour voir le catalogue " 59 | 60 | #: my_app/templates/products.html:15 61 | msgid "<< Previous Page" 62 | msgstr "" 63 | 64 | #: my_app/templates/products.html:18 65 | msgid "No Previous Page" 66 | msgstr "" 67 | 68 | #: my_app/templates/products.html:20 69 | #, python-format 70 | msgid "%(num)d page" 71 | msgid_plural "%(num)d pages" 72 | msgstr[0] "%(num)d page" 73 | msgstr[1] "%(num)d pages" 74 | 75 | #: my_app/templates/products.html:23 76 | msgid "Next page >>" 77 | msgstr "" 78 | 79 | #: my_app/templates/products.html:26 80 | msgid "No Next page" 81 | msgstr "" 82 | 83 | -------------------------------------------------------------------------------- /Chapter13/Docker/my_app/translations/fr/LC_MESSAGES/messages.po: -------------------------------------------------------------------------------- 1 | # French translations for PROJECT. 2 | # Copyright (C) 2014 ORGANIZATION 3 | # This file is distributed under the same license as the PROJECT project. 4 | # FIRST AUTHOR , 2014. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PROJECT VERSION\n" 10 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 11 | "POT-Creation-Date: 2014-10-22 12:19+0530\n" 12 | "PO-Revision-Date: 2014-10-22 11:48+0530\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language: fr\n" 15 | "Language-Team: fr \n" 16 | "Plural-Forms: nplurals=2; plural=(n > 1)\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=utf-8\n" 19 | "Content-Transfer-Encoding: 8bit\n" 20 | "Generated-By: Babel 2.6.0\n" 21 | 22 | #: my_app/catalog/models.py:42 my_app/catalog/models.py:105 23 | msgid "Name" 24 | msgstr "Nom" 25 | 26 | #: my_app/catalog/models.py:74 27 | msgid "Not a valid choice" 28 | msgstr "" 29 | 30 | #: my_app/catalog/models.py:78 31 | msgid "Price" 32 | msgstr "Prix" 33 | 34 | #: my_app/catalog/models.py:82 35 | msgid "Category" 36 | msgstr "Catégorie" 37 | 38 | #: my_app/catalog/models.py:84 39 | msgid "Product Image" 40 | msgstr "Image du produit" 41 | 42 | #: my_app/catalog/views.py:86 43 | #, python-format 44 | msgid "The product %(name)s has been created" 45 | msgstr "Le produit %(name)s a été créé." 46 | 47 | #: my_app/catalog/views.py:128 48 | #, python-format 49 | msgid "The category %(name)s has been created" 50 | msgstr "" 51 | 52 | #: my_app/templates/home.html:4 53 | msgid "Welcome to the Catalog Home" 54 | msgstr "Bienvenue sur le catalogue Accueil" 55 | 56 | #: my_app/templates/home.html:6 57 | msgid "Click here to see the catalog " 58 | msgstr "Cliquez ici pour voir le catalogue " 59 | 60 | #: my_app/templates/products.html:15 61 | msgid "<< Previous Page" 62 | msgstr "" 63 | 64 | #: my_app/templates/products.html:18 65 | msgid "No Previous Page" 66 | msgstr "" 67 | 68 | #: my_app/templates/products.html:20 69 | #, python-format 70 | msgid "%(num)d page" 71 | msgid_plural "%(num)d pages" 72 | msgstr[0] "%(num)d page" 73 | msgstr[1] "%(num)d pages" 74 | 75 | #: my_app/templates/products.html:23 76 | msgid "Next page >>" 77 | msgstr "" 78 | 79 | #: my_app/templates/products.html:26 80 | msgid "No Next page" 81 | msgstr "" 82 | 83 | -------------------------------------------------------------------------------- /Chapter08/my_app/auth/models.py: -------------------------------------------------------------------------------- 1 | from werkzeug.security import generate_password_hash, check_password_hash 2 | from flask_wtf import FlaskForm 3 | from wtforms import TextField, PasswordField, BooleanField, widgets, \ 4 | TextAreaField 5 | from wtforms.validators import InputRequired, EqualTo 6 | from my_app import db 7 | 8 | 9 | class User(db.Model): 10 | id = db.Column(db.Integer, primary_key=True) 11 | username = db.Column(db.String(100)) 12 | pwdhash = db.Column(db.String()) 13 | admin = db.Column(db.Boolean()) 14 | notes = db.Column(db.UnicodeText) 15 | roles = db.Column(db.String(4)) 16 | 17 | def __init__(self, username, password, admin=False, notes='', roles='R'): 18 | self.username = username 19 | self.pwdhash = generate_password_hash(password) 20 | self.admin = admin 21 | self.notes = notes 22 | self.roles = self.admin and roles or '' 23 | 24 | def is_admin(self): 25 | return self.admin 26 | 27 | def check_password(self, password): 28 | return check_password_hash(self.pwdhash, password) 29 | 30 | @property 31 | def is_authenticated(self): 32 | return True 33 | 34 | @property 35 | def is_active(self): 36 | return True 37 | 38 | @property 39 | def is_anonymous(self): 40 | return False 41 | 42 | def get_id(self): 43 | return str(self.id) 44 | 45 | 46 | class RegistrationForm(FlaskForm): 47 | username = TextField('Username', [InputRequired()]) 48 | password = PasswordField( 49 | 'Password', [ 50 | InputRequired(), EqualTo('confirm', message='Passwords must match') 51 | ] 52 | ) 53 | confirm = PasswordField('Confirm Password', [InputRequired()]) 54 | 55 | 56 | class LoginForm(FlaskForm): 57 | username = TextField('Username', [InputRequired()]) 58 | password = PasswordField('Password', [InputRequired()]) 59 | 60 | 61 | class AdminUserCreateForm(FlaskForm): 62 | username = TextField('Username', [InputRequired()]) 63 | password = PasswordField('Password', [InputRequired()]) 64 | admin = BooleanField('Is Admin ?') 65 | 66 | 67 | class AdminUserUpdateForm(FlaskForm): 68 | username = TextField('Username', [InputRequired()]) 69 | admin = BooleanField('Is Admin ?') 70 | 71 | 72 | class CKTextAreaWidget(widgets.TextArea): 73 | def __call__(self, field, **kwargs): 74 | kwargs.setdefault('class_', 'ckeditor') 75 | return super(CKTextAreaWidget, self).__call__(field, **kwargs) 76 | 77 | 78 | class CKTextAreaField(TextAreaField): 79 | widget = CKTextAreaWidget() 80 | -------------------------------------------------------------------------------- /Chapter05/my_app/catalog/models.py: -------------------------------------------------------------------------------- 1 | from decimal import Decimal 2 | from flask_wtf import FlaskForm 3 | from flask_wtf.file import FileField, FileRequired 4 | from wtforms import TextField, DecimalField, SelectField 5 | from wtforms.validators import InputRequired, NumberRange, ValidationError 6 | from wtforms.widgets import html_params, Select, HTMLString 7 | from my_app import db 8 | 9 | class Product(db.Model): 10 | id = db.Column(db.Integer, primary_key=True) 11 | name = db.Column(db.String(255)) 12 | price = db.Column(db.Float) 13 | category_id = db.Column(db.Integer, db.ForeignKey('category.id')) 14 | category = db.relationship( 15 | 'Category', backref=db.backref('products', lazy='dynamic') 16 | ) 17 | image_path = db.Column(db.String(255)) 18 | 19 | def __init__(self, name, price, category, image_path): 20 | self.name = name 21 | self.price = price 22 | self.category = category 23 | self.image_path = image_path 24 | 25 | def __repr__(self): 26 | return '' % self.id 27 | 28 | 29 | class Category(db.Model): 30 | id = db.Column(db.Integer, primary_key=True) 31 | name = db.Column(db.String(100)) 32 | 33 | def __init__(self, name): 34 | self.name = name 35 | 36 | def __repr__(self): 37 | return '' % self.id 38 | 39 | 40 | class NameForm(FlaskForm): 41 | name = TextField('Name', validators=[InputRequired()]) 42 | 43 | 44 | class CustomCategoryInput(Select): 45 | 46 | def __call__(self, field, **kwargs): 47 | kwargs.setdefault('id', field.id) 48 | html = [] 49 | for val, label, selected in field.iter_choices(): 50 | html.append( 51 | ' %s' % ( 52 | html_params( 53 | name=field.name, value=val, checked=selected, **kwargs 54 | ), label 55 | ) 56 | ) 57 | return HTMLString(' '.join(html)) 58 | 59 | 60 | class CategoryField(SelectField): 61 | widget = CustomCategoryInput() 62 | 63 | def iter_choices(self): 64 | categories = [(c.id, c.name) for c in Category.query.all()] 65 | for value, label in categories: 66 | yield (value, label, self.coerce(value) == self.data) 67 | 68 | def pre_validate(self, form): 69 | for v, _ in [(c.id, c.name) for c in Category.query.all()]: 70 | if self.data == v: 71 | break 72 | else: 73 | raise ValueError(self.gettext('Not a valid choice')) 74 | 75 | 76 | class ProductForm(NameForm): 77 | price = DecimalField('Price', validators=[ 78 | InputRequired(), NumberRange(min=Decimal('0.0')) 79 | ]) 80 | category = CategoryField( 81 | 'Category', validators=[InputRequired()], coerce=int 82 | ) 83 | image = FileField('Product Image', validators=[FileRequired()]) 84 | 85 | 86 | def check_duplicate_category(case_sensitive=True): 87 | def _check_duplicate(form, field): 88 | if case_sensitive: 89 | res = Category.query.filter( 90 | Category.name.like('%' + field.data + '%') 91 | ).first() 92 | else: 93 | res = Category.query.filter( 94 | Category.name.ilike('%' + field.data + '%') 95 | ).first() 96 | if res: 97 | raise ValidationError( 98 | 'Category named %s already exists' % field.data 99 | ) 100 | return _check_duplicate 101 | 102 | 103 | class CategoryForm(NameForm): 104 | name = TextField('Name', validators=[ 105 | InputRequired(), check_duplicate_category() 106 | ]) 107 | -------------------------------------------------------------------------------- /Chapter09/my_app/catalog/models.py: -------------------------------------------------------------------------------- 1 | from decimal import Decimal 2 | from flask_wtf import FlaskForm 3 | from flask_wtf.file import FileField, FileRequired 4 | from wtforms import TextField, DecimalField, SelectField 5 | from wtforms.validators import InputRequired, NumberRange, ValidationError 6 | from wtforms.widgets import html_params, Select, HTMLString 7 | from flask_babel import lazy_gettext as _ 8 | from my_app import db 9 | 10 | 11 | class Product(db.Model): 12 | id = db.Column(db.Integer, primary_key=True) 13 | name = db.Column(db.String(255)) 14 | price = db.Column(db.Float) 15 | category_id = db.Column(db.Integer, db.ForeignKey('category.id')) 16 | category = db.relationship( 17 | 'Category', backref=db.backref('products', lazy='dynamic') 18 | ) 19 | image_path = db.Column(db.String(255)) 20 | 21 | def __init__(self, name, price, category, image_path): 22 | self.name = name 23 | self.price = price 24 | self.category = category 25 | self.image_path = image_path 26 | 27 | def __repr__(self): 28 | return '' % self.id 29 | 30 | 31 | class Category(db.Model): 32 | id = db.Column(db.Integer, primary_key=True) 33 | name = db.Column(db.String(100)) 34 | 35 | def __init__(self, name): 36 | self.name = name 37 | 38 | def __repr__(self): 39 | return '' % self.id 40 | 41 | 42 | class NameForm(FlaskForm): 43 | name = TextField(_('Name'), validators=[InputRequired()]) 44 | 45 | 46 | class CustomCategoryInput(Select): 47 | 48 | def __call__(self, field, **kwargs): 49 | kwargs.setdefault('id', field.id) 50 | html = [] 51 | for val, label, selected in field.iter_choices(): 52 | html.append( 53 | ' %s' % ( 54 | html_params( 55 | name=field.name, value=val, checked=selected, **kwargs 56 | ), label 57 | ) 58 | ) 59 | return HTMLString(' '.join(html)) 60 | 61 | 62 | class CategoryField(SelectField): 63 | widget = CustomCategoryInput() 64 | 65 | def iter_choices(self): 66 | categories = [(c.id, c.name) for c in Category.query.all()] 67 | for value, label in categories: 68 | yield (value, label, self.coerce(value) == self.data) 69 | 70 | def pre_validate(self, form): 71 | for v, _ in [(c.id, c.name) for c in Category.query.all()]: 72 | if self.data == v: 73 | break 74 | else: 75 | raise ValueError(self.gettext('Not a valid choice')) 76 | 77 | 78 | class ProductForm(NameForm): 79 | price = DecimalField(_('Price'), validators=[ 80 | InputRequired(), NumberRange(min=Decimal('0.0')) 81 | ]) 82 | category = CategoryField( 83 | _('Category'), validators=[InputRequired()], coerce=int 84 | ) 85 | image = FileField(_('Product Image'), validators=[FileRequired()]) 86 | 87 | 88 | def check_duplicate_category(case_sensitive=True): 89 | def _check_duplicate(form, field): 90 | if case_sensitive: 91 | res = Category.query.filter( 92 | Category.name.like('%' + field.data + '%') 93 | ).first() 94 | else: 95 | res = Category.query.filter( 96 | Category.name.ilike('%' + field.data + '%') 97 | ).first() 98 | if res: 99 | raise ValidationError( 100 | 'Category named %s already exists' % field.data 101 | ) 102 | return _check_duplicate 103 | 104 | 105 | class CategoryForm(NameForm): 106 | name = TextField(_('Name'), validators=[ 107 | InputRequired(), check_duplicate_category() 108 | ]) 109 | -------------------------------------------------------------------------------- /Chapter10/my_app/catalog/models.py: -------------------------------------------------------------------------------- 1 | from decimal import Decimal 2 | from flask_wtf import FlaskForm 3 | from flask_wtf.file import FileField, FileRequired 4 | from wtforms import TextField, DecimalField, SelectField 5 | from wtforms.validators import InputRequired, NumberRange, ValidationError 6 | from wtforms.widgets import html_params, Select, HTMLString 7 | from flask_wtf import Form 8 | from flask_babel import lazy_gettext as _ 9 | from my_app import db 10 | 11 | 12 | class Product(db.Model): 13 | id = db.Column(db.Integer, primary_key=True) 14 | name = db.Column(db.String(255)) 15 | price = db.Column(db.Float) 16 | category_id = db.Column(db.Integer, db.ForeignKey('category.id')) 17 | category = db.relationship( 18 | 'Category', backref=db.backref('products', lazy='dynamic') 19 | ) 20 | image_path = db.Column(db.String(255)) 21 | user_timezone = db.Column(db.String(255)) 22 | 23 | def __init__(self, name, price, category, image_path, user_timezone=''): 24 | self.name = name 25 | self.price = price 26 | self.category = category 27 | self.image_path = image_path 28 | self.user_timezone = user_timezone 29 | 30 | def __repr__(self): 31 | return '' % self.id 32 | 33 | 34 | class Category(db.Model): 35 | id = db.Column(db.Integer, primary_key=True) 36 | name = db.Column(db.String(100)) 37 | 38 | def __init__(self, name): 39 | self.name = name 40 | 41 | def __repr__(self): 42 | return '' % self.id 43 | 44 | 45 | class NameForm(FlaskForm): 46 | name = TextField(_('Name'), validators=[InputRequired()]) 47 | 48 | 49 | class CustomCategoryInput(Select): 50 | 51 | def __call__(self, field, **kwargs): 52 | kwargs.setdefault('id', field.id) 53 | html = [] 54 | for val, label, selected in field.iter_choices(): 55 | html.append( 56 | ' %s' % ( 57 | html_params( 58 | name=field.name, value=val, checked=selected, **kwargs 59 | ), label 60 | ) 61 | ) 62 | return HTMLString(' '.join(html)) 63 | 64 | 65 | class CategoryField(SelectField): 66 | widget = CustomCategoryInput() 67 | 68 | def iter_choices(self): 69 | categories = [(c.id, c.name) for c in Category.query.all()] 70 | for value, label in categories: 71 | yield (value, label, self.coerce(value) == self.data) 72 | 73 | def pre_validate(self, form): 74 | for v, _ in [(c.id, c.name) for c in Category.query.all()]: 75 | if self.data == v: 76 | break 77 | else: 78 | raise ValueError(self.gettext('Not a valid choice')) 79 | 80 | 81 | class ProductForm(NameForm): 82 | price = DecimalField(_('Price'), validators=[ 83 | InputRequired(), NumberRange(min=Decimal('0.0')) 84 | ]) 85 | category = CategoryField( 86 | _('Category'), validators=[InputRequired()], coerce=int 87 | ) 88 | image = FileField(_('Product Image'), validators=[FileRequired()]) 89 | 90 | 91 | def check_duplicate_category(case_sensitive=True): 92 | def _check_duplicate(form, field): 93 | if case_sensitive: 94 | res = Category.query.filter( 95 | Category.name.like('%' + field.data + '%') 96 | ).first() 97 | else: 98 | res = Category.query.filter( 99 | Category.name.ilike('%' + field.data + '%') 100 | ).first() 101 | if res: 102 | raise ValidationError( 103 | 'Category named %s already exists' % field.data 104 | ) 105 | return _check_duplicate 106 | 107 | 108 | class CategoryForm(NameForm): 109 | name = TextField(_('Name'), validators=[ 110 | InputRequired(), check_duplicate_category() 111 | ]) 112 | -------------------------------------------------------------------------------- /Chapter11/my_app/catalog/models.py: -------------------------------------------------------------------------------- 1 | from decimal import Decimal 2 | from flask_wtf import FlaskForm 3 | from flask_wtf.file import FileField, FileRequired 4 | from wtforms import TextField, DecimalField, SelectField 5 | from wtforms.validators import InputRequired, NumberRange, ValidationError 6 | from wtforms.widgets import html_params, Select, HTMLString 7 | from flask_wtf import Form 8 | from flask_babel import lazy_gettext as _ 9 | from my_app import db 10 | 11 | 12 | class Product(db.Model): 13 | id = db.Column(db.Integer, primary_key=True) 14 | name = db.Column(db.String(255)) 15 | price = db.Column(db.Float) 16 | category_id = db.Column(db.Integer, db.ForeignKey('category.id')) 17 | category = db.relationship( 18 | 'Category', backref=db.backref('products', lazy='dynamic') 19 | ) 20 | image_path = db.Column(db.String(255)) 21 | user_timezone = db.Column(db.String(255)) 22 | 23 | def __init__(self, name, price, category, image_path, user_timezone=''): 24 | self.name = name 25 | self.price = price 26 | self.category = category 27 | self.image_path = image_path 28 | self.user_timezone = user_timezone 29 | 30 | def __repr__(self): 31 | return '' % self.id 32 | 33 | 34 | class Category(db.Model): 35 | id = db.Column(db.Integer, primary_key=True) 36 | name = db.Column(db.String(100)) 37 | 38 | def __init__(self, name): 39 | self.name = name 40 | 41 | def __repr__(self): 42 | return '' % self.id 43 | 44 | 45 | class NameForm(FlaskForm): 46 | name = TextField(_('Name'), validators=[InputRequired()]) 47 | 48 | 49 | class CustomCategoryInput(Select): 50 | 51 | def __call__(self, field, **kwargs): 52 | kwargs.setdefault('id', field.id) 53 | html = [] 54 | for val, label, selected in field.iter_choices(): 55 | html.append( 56 | ' %s' % ( 57 | html_params( 58 | name=field.name, value=val, checked=selected, **kwargs 59 | ), label 60 | ) 61 | ) 62 | return HTMLString(' '.join(html)) 63 | 64 | 65 | class CategoryField(SelectField): 66 | widget = CustomCategoryInput() 67 | 68 | def iter_choices(self): 69 | categories = [(c.id, c.name) for c in Category.query.all()] 70 | for value, label in categories: 71 | yield (value, label, self.coerce(value) == self.data) 72 | 73 | def pre_validate(self, form): 74 | for v, _ in [(c.id, c.name) for c in Category.query.all()]: 75 | if self.data == v: 76 | break 77 | else: 78 | raise ValueError(self.gettext('Not a valid choice')) 79 | 80 | 81 | class ProductForm(NameForm): 82 | price = DecimalField(_('Price'), validators=[ 83 | InputRequired(), NumberRange(min=Decimal('0.0')) 84 | ]) 85 | category = CategoryField( 86 | _('Category'), validators=[InputRequired()], coerce=int 87 | ) 88 | image = FileField(_('Product Image'), validators=[FileRequired()]) 89 | 90 | 91 | def check_duplicate_category(case_sensitive=True): 92 | def _check_duplicate(form, field): 93 | if case_sensitive: 94 | res = Category.query.filter( 95 | Category.name.like('%' + field.data + '%') 96 | ).first() 97 | else: 98 | res = Category.query.filter( 99 | Category.name.ilike('%' + field.data + '%') 100 | ).first() 101 | if res: 102 | raise ValidationError( 103 | 'Category named %s already exists' % field.data 104 | ) 105 | return _check_duplicate 106 | 107 | 108 | class CategoryForm(NameForm): 109 | name = TextField(_('Name'), validators=[ 110 | InputRequired(), check_duplicate_category() 111 | ]) 112 | -------------------------------------------------------------------------------- /Chapter13/Docker/my_app/catalog/models.py: -------------------------------------------------------------------------------- 1 | from decimal import Decimal 2 | from flask_wtf import FlaskForm 3 | from flask_wtf.file import FileField, FileRequired 4 | from wtforms import TextField, DecimalField, SelectField 5 | from wtforms.validators import InputRequired, NumberRange, ValidationError 6 | from wtforms.widgets import html_params, Select, HTMLString 7 | from flask_wtf import Form 8 | from flask_babel import lazy_gettext as _ 9 | from my_app import db 10 | 11 | 12 | class Product(db.Model): 13 | id = db.Column(db.Integer, primary_key=True) 14 | name = db.Column(db.String(255)) 15 | price = db.Column(db.Float) 16 | category_id = db.Column(db.Integer, db.ForeignKey('category.id')) 17 | category = db.relationship( 18 | 'Category', backref=db.backref('products', lazy='dynamic') 19 | ) 20 | image_path = db.Column(db.String(255)) 21 | user_timezone = db.Column(db.String(255)) 22 | 23 | def __init__(self, name, price, category, image_path, user_timezone=''): 24 | self.name = name 25 | self.price = price 26 | self.category = category 27 | self.image_path = image_path 28 | self.user_timezone = user_timezone 29 | 30 | def __repr__(self): 31 | return '' % self.id 32 | 33 | 34 | class Category(db.Model): 35 | id = db.Column(db.Integer, primary_key=True) 36 | name = db.Column(db.String(100)) 37 | 38 | def __init__(self, name): 39 | self.name = name 40 | 41 | def __repr__(self): 42 | return '' % self.id 43 | 44 | 45 | class NameForm(FlaskForm): 46 | name = TextField(_('Name'), validators=[InputRequired()]) 47 | 48 | 49 | class CustomCategoryInput(Select): 50 | 51 | def __call__(self, field, **kwargs): 52 | kwargs.setdefault('id', field.id) 53 | html = [] 54 | for val, label, selected in field.iter_choices(): 55 | html.append( 56 | ' %s' % ( 57 | html_params( 58 | name=field.name, value=val, checked=selected, **kwargs 59 | ), label 60 | ) 61 | ) 62 | return HTMLString(' '.join(html)) 63 | 64 | 65 | class CategoryField(SelectField): 66 | widget = CustomCategoryInput() 67 | 68 | def iter_choices(self): 69 | categories = [(c.id, c.name) for c in Category.query.all()] 70 | for value, label in categories: 71 | yield (value, label, self.coerce(value) == self.data) 72 | 73 | def pre_validate(self, form): 74 | for v, _ in [(c.id, c.name) for c in Category.query.all()]: 75 | if self.data == v: 76 | break 77 | else: 78 | raise ValueError(self.gettext('Not a valid choice')) 79 | 80 | 81 | class ProductForm(NameForm): 82 | price = DecimalField(_('Price'), validators=[ 83 | InputRequired(), NumberRange(min=Decimal('0.0')) 84 | ]) 85 | category = CategoryField( 86 | _('Category'), validators=[InputRequired()], coerce=int 87 | ) 88 | image = FileField(_('Product Image'), validators=[FileRequired()]) 89 | 90 | 91 | def check_duplicate_category(case_sensitive=True): 92 | def _check_duplicate(form, field): 93 | if case_sensitive: 94 | res = Category.query.filter( 95 | Category.name.like('%' + field.data + '%') 96 | ).first() 97 | else: 98 | res = Category.query.filter( 99 | Category.name.ilike('%' + field.data + '%') 100 | ).first() 101 | if res: 102 | raise ValidationError( 103 | 'Category named %s already exists' % field.data 104 | ) 105 | return _check_duplicate 106 | 107 | 108 | class CategoryForm(NameForm): 109 | name = TextField(_('Name'), validators=[ 110 | InputRequired(), check_duplicate_category() 111 | ]) 112 | -------------------------------------------------------------------------------- /Chapter04/my_app/catalog/views.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | from flask import request, Blueprint, render_template, jsonify, flash, \ 3 | redirect, url_for 4 | from my_app import db, app 5 | from my_app.catalog.models import Product, Category 6 | from sqlalchemy.orm.util import join 7 | 8 | catalog = Blueprint('catalog', __name__) 9 | 10 | 11 | def template_or_json(template=None): 12 | """"Return a dict from your view and this will either 13 | pass it to a template or render json. Use like: 14 | 15 | @template_or_json('template.html') 16 | """ 17 | def decorated(f): 18 | @wraps(f) 19 | def decorated_fn(*args, **kwargs): 20 | ctx = f(*args, **kwargs) 21 | if request.is_xhr or not template: 22 | return jsonify(ctx) 23 | else: 24 | return render_template(template, **ctx) 25 | return decorated_fn 26 | return decorated 27 | 28 | 29 | @app.errorhandler(404) 30 | def page_not_found(e): 31 | return render_template('404.html'), 404 32 | 33 | 34 | @catalog.route('/') 35 | @catalog.route('/home') 36 | @template_or_json('home.html') 37 | def home(): 38 | products = Product.query.all() 39 | return {'count': len(products)} 40 | 41 | 42 | @catalog.route('/product/') 43 | def product(id): 44 | product = Product.query.get_or_404(id) 45 | return render_template('product.html', product=product) 46 | 47 | 48 | @catalog.route('/products') 49 | @catalog.route('/products/') 50 | def products(page=1): 51 | products = Product.query.paginate(page, 10) 52 | return render_template('products.html', products=products) 53 | 54 | 55 | @catalog.route('/product-create', methods=['GET', 'POST']) 56 | def create_product(): 57 | if request.method == 'POST': 58 | name = request.form.get('name') 59 | price = request.form.get('price') 60 | categ_name = request.form.get('category') 61 | category = Category.query.filter_by(name=categ_name).first() 62 | if not category: 63 | category = Category(categ_name) 64 | product = Product(name, price, category) 65 | db.session.add(product) 66 | db.session.commit() 67 | flash('The product %s has been created' % name, 'success') 68 | return redirect(url_for('catalog.product', id=product.id)) 69 | return render_template('product-create.html') 70 | 71 | 72 | @catalog.route('/product-search') 73 | @catalog.route('/product-search/') 74 | def product_search(page=1): 75 | name = request.args.get('name') 76 | price = request.args.get('price') 77 | company = request.args.get('company') 78 | category = request.args.get('category') 79 | products = Product.query 80 | if name: 81 | products = products.filter(Product.name.like('%' + name + '%')) 82 | if price: 83 | products = products.filter(Product.price == price) 84 | if company: 85 | products = products.filter(Product.company.like('%' + company + '%')) 86 | if category: 87 | products = products.select_from(join(Product, Category)).filter( 88 | Category.name.like('%' + category + '%') 89 | ) 90 | return render_template( 91 | 'products.html', products=products.paginate(page, 10) 92 | ) 93 | 94 | 95 | @catalog.route('/category-create', methods=['POST',]) 96 | def create_category(): 97 | name = request.form.get('name') 98 | category = Category(name) 99 | db.session.add(category) 100 | db.session.commit() 101 | return render_template('category.html', category=category) 102 | 103 | 104 | @catalog.route('/category/') 105 | def category(id): 106 | category = Category.query.get_or_404(id) 107 | return render_template('category.html', category=category) 108 | 109 | 110 | @catalog.route('/categories') 111 | def categories(): 112 | categories = Category.query.all() 113 | return render_template('categories.html', categories=categories) 114 | -------------------------------------------------------------------------------- /Chapter05/my_app/catalog/views.py: -------------------------------------------------------------------------------- 1 | import os 2 | from functools import wraps 3 | from werkzeug import secure_filename 4 | from flask import request, Blueprint, render_template, jsonify, flash, \ 5 | redirect, url_for 6 | from my_app import db, app, ALLOWED_EXTENSIONS 7 | from my_app.catalog.models import Product, Category, ProductForm, CategoryForm 8 | from sqlalchemy.orm.util import join 9 | 10 | catalog = Blueprint('catalog', __name__) 11 | 12 | 13 | def template_or_json(template=None): 14 | """"Return a dict from your view and this will either 15 | pass it to a template or render json. Use like: 16 | 17 | @template_or_json('template.html') 18 | """ 19 | def decorated(f): 20 | @wraps(f) 21 | def decorated_fn(*args, **kwargs): 22 | ctx = f(*args, **kwargs) 23 | if request.is_xhr or not template: 24 | return jsonify(ctx) 25 | else: 26 | return render_template(template, **ctx) 27 | return decorated_fn 28 | return decorated 29 | 30 | 31 | def allowed_file(filename): 32 | return '.' in filename and \ 33 | filename.lower().rsplit('.', 1)[1] in ALLOWED_EXTENSIONS 34 | 35 | 36 | @app.errorhandler(404) 37 | def page_not_found(e): 38 | return render_template('404.html'), 404 39 | 40 | 41 | @catalog.route('/') 42 | @catalog.route('/home') 43 | @template_or_json('home.html') 44 | def home(): 45 | products = Product.query.all() 46 | return {'count': len(products)} 47 | 48 | 49 | @catalog.route('/product/') 50 | def product(id): 51 | product = Product.query.get_or_404(id) 52 | return render_template('product.html', product=product) 53 | 54 | 55 | @catalog.route('/products') 56 | @catalog.route('/products/') 57 | def products(page=1): 58 | products = Product.query.paginate(page, 10) 59 | return render_template('products.html', products=products) 60 | 61 | 62 | @catalog.route('/product-create', methods=['GET', 'POST']) 63 | def create_product(): 64 | form = ProductForm() 65 | 66 | if form.validate_on_submit(): 67 | name = form.name.data 68 | price = form.price.data 69 | category = Category.query.get_or_404( 70 | form.category.data 71 | ) 72 | image = form.image.data 73 | if allowed_file(image.filename): 74 | filename = secure_filename(image.filename) 75 | image.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) 76 | product = Product(name, price, category, filename) 77 | db.session.add(product) 78 | db.session.commit() 79 | flash('The product %s has been created' % name, 'success') 80 | return redirect(url_for('catalog.product', id=product.id)) 81 | 82 | if form.errors: 83 | flash(form.errors, 'danger') 84 | 85 | return render_template('product-create.html', form=form) 86 | 87 | 88 | @catalog.route('/product-search') 89 | @catalog.route('/product-search/') 90 | def product_search(page=1): 91 | name = request.args.get('name') 92 | price = request.args.get('price') 93 | company = request.args.get('company') 94 | category = request.args.get('category') 95 | products = Product.query 96 | if name: 97 | products = products.filter(Product.name.like('%' + name + '%')) 98 | if price: 99 | products = products.filter(Product.price == price) 100 | if company: 101 | products = products.filter(Product.company.like('%' + company + '%')) 102 | if category: 103 | products = products.select_from(join(Product, Category)).filter( 104 | Category.name.like('%' + category + '%') 105 | ) 106 | return render_template( 107 | 'products.html', products=products.paginate(page, 10) 108 | ) 109 | 110 | 111 | @catalog.route('/category-create', methods=['GET', 'POST']) 112 | def create_category(): 113 | form = CategoryForm() 114 | 115 | if form.validate_on_submit(): 116 | name = form.name.data 117 | category = Category(name) 118 | db.session.add(category) 119 | db.session.commit() 120 | flash( 121 | 'The category %s has been created' % name, 'success' 122 | ) 123 | return redirect( 124 | url_for('catalog.category', id=category.id) 125 | ) 126 | 127 | if form.errors: 128 | flash(form.errors, 'danger') 129 | 130 | return render_template('category-create.html', form=form) 131 | 132 | 133 | @catalog.route('/category/') 134 | def category(id): 135 | category = Category.query.get_or_404(id) 136 | return render_template('category.html', category=category) 137 | 138 | 139 | @catalog.route('/categories') 140 | def categories(): 141 | categories = Category.query.all() 142 | return render_template('categories.html', categories=categories) 143 | -------------------------------------------------------------------------------- /Chapter09/my_app/catalog/views.py: -------------------------------------------------------------------------------- 1 | import os 2 | from functools import wraps 3 | from werkzeug import secure_filename 4 | from flask import request, Blueprint, render_template, jsonify, flash, \ 5 | redirect, url_for as flask_url_for, g 6 | from my_app import db, app, ALLOWED_EXTENSIONS, babel 7 | from my_app.catalog.models import Product, Category, ProductForm, CategoryForm 8 | from sqlalchemy.orm.util import join 9 | from flask_babel import lazy_gettext as _ 10 | 11 | catalog = Blueprint('catalog', __name__) 12 | 13 | 14 | @app.before_request 15 | def before(): 16 | if request.view_args and 'lang' in request.view_args: 17 | g.current_lang = request.view_args['lang'] 18 | request.view_args.pop('lang') 19 | 20 | 21 | @app.context_processor 22 | def inject_url_for(): 23 | return { 24 | 'url_for': lambda endpoint, **kwargs: flask_url_for( 25 | endpoint, lang=g.get('current_lang', 'en'), **kwargs 26 | ) 27 | } 28 | 29 | 30 | url_for = inject_url_for()['url_for'] 31 | 32 | 33 | @babel.localeselector 34 | def get_locale(): 35 | return g.get('current_lang', 'en') 36 | 37 | 38 | def template_or_json(template=None): 39 | """"Return a dict from your view and this will either 40 | pass it to a template or render json. Use like: 41 | 42 | @template_or_json('template.html') 43 | """ 44 | def decorated(f): 45 | @wraps(f) 46 | def decorated_fn(*args, **kwargs): 47 | ctx = f(*args, **kwargs) 48 | if request.is_xhr or not template: 49 | return jsonify(ctx) 50 | else: 51 | return render_template(template, **ctx) 52 | return decorated_fn 53 | return decorated 54 | 55 | 56 | def allowed_file(filename): 57 | return '.' in filename and \ 58 | filename.lower().rsplit('.', 1)[1] in ALLOWED_EXTENSIONS 59 | 60 | 61 | @app.errorhandler(404) 62 | def page_not_found(e): 63 | return render_template('404.html'), 404 64 | 65 | 66 | @catalog.route('/') 67 | @catalog.route('//') 68 | @catalog.route('//home') 69 | @template_or_json('home.html') 70 | def home(): 71 | products = Product.query.all() 72 | return {'count': len(products)} 73 | 74 | 75 | @catalog.route('//product/') 76 | def product(id): 77 | product = Product.query.get_or_404(id) 78 | return render_template('product.html', product=product) 79 | 80 | 81 | @catalog.route('//products') 82 | @catalog.route('//products/') 83 | def products(page=1): 84 | products = Product.query.paginate(page, 10) 85 | return render_template('products.html', products=products) 86 | 87 | 88 | @catalog.route('//product-create', methods=['GET', 'POST']) 89 | def create_product(): 90 | form = ProductForm() 91 | 92 | if form.validate_on_submit(): 93 | name = form.name.data 94 | price = form.price.data 95 | category = Category.query.get_or_404( 96 | form.category.data 97 | ) 98 | image = form.image.data 99 | if allowed_file(image.filename): 100 | filename = secure_filename(image.filename) 101 | image.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) 102 | product = Product(name, price, category, filename) 103 | db.session.add(product) 104 | db.session.commit() 105 | flash(_('The product %(name)s has been created', name=name), 'success') 106 | return redirect(url_for('catalog.product', id=product.id)) 107 | 108 | if form.errors: 109 | flash(form.errors, 'danger') 110 | 111 | return render_template('product-create.html', form=form) 112 | 113 | 114 | @catalog.route('//product-search') 115 | @catalog.route('//product-search/') 116 | def product_search(page=1): 117 | name = request.args.get('name') 118 | price = request.args.get('price') 119 | company = request.args.get('company') 120 | category = request.args.get('category') 121 | products = Product.query 122 | if name: 123 | products = products.filter(Product.name.like('%' + name + '%')) 124 | if price: 125 | products = products.filter(Product.price == price) 126 | if company: 127 | products = products.filter(Product.company.like('%' + company + '%')) 128 | if category: 129 | products = products.select_from(join(Product, Category)).filter( 130 | Category.name.like('%' + category + '%') 131 | ) 132 | return render_template( 133 | 'products.html', products=products.paginate(page, 10) 134 | ) 135 | 136 | 137 | @catalog.route('//category-create', methods=['GET', 'POST']) 138 | def create_category(): 139 | form = CategoryForm() 140 | 141 | if form.validate_on_submit(): 142 | name = form.name.data 143 | category = Category(name) 144 | db.session.add(category) 145 | db.session.commit() 146 | flash( 147 | _('The category %(name)s has been created', name=name), 'success' 148 | ) 149 | return redirect( 150 | url_for('catalog.category', id=category.id) 151 | ) 152 | 153 | if form.errors: 154 | flash(form.errors, 'danger') 155 | 156 | return render_template('category-create.html', form=form) 157 | 158 | 159 | @catalog.route('//category/') 160 | def category(id): 161 | category = Category.query.get_or_404(id) 162 | return render_template('category.html', category=category) 163 | 164 | 165 | @catalog.route('//categories') 166 | def categories(): 167 | categories = Category.query.all() 168 | return render_template('categories.html', categories=categories) 169 | -------------------------------------------------------------------------------- /Chapter10/app_tests.py: -------------------------------------------------------------------------------- 1 | import os 2 | from my_app import app, db 3 | import unittest 4 | from unittest import mock 5 | import tempfile 6 | import geoip2.records 7 | import coverage 8 | 9 | cov = coverage.coverage( 10 | omit = [ 11 | '/Users/shalabh.aggarwal/workspace/cookbook10/lib/python3.6/site-packages/*', 12 | 'app_tests.py' 13 | ] 14 | ) 15 | cov.start() 16 | 17 | 18 | class CatalogTestCase(unittest.TestCase): 19 | 20 | def setUp(self): 21 | self.test_db_file = tempfile.mkstemp()[1] 22 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + self.test_db_file 23 | app.config['TESTING'] = True 24 | app.config['WTF_CSRF_ENABLED'] = False 25 | self.app = app.test_client() 26 | self.geoip_city_patcher = mock.patch('geoip2.models.City', 27 | location=geoip2.records.Location(time_zone = 'America/Los_Angeles') 28 | ) 29 | PatchedGeoipCity = self.geoip_city_patcher.start() 30 | self.geoip_reader_patcher = mock.patch('geoip2.database.Reader') 31 | PatchedGeoipReader = self.geoip_reader_patcher.start() 32 | PatchedGeoipReader().city.return_value = PatchedGeoipCity 33 | db.create_all() 34 | 35 | def tearDown(self): 36 | self.geoip_city_patcher.stop() 37 | self.geoip_reader_patcher.stop() 38 | os.remove(self.test_db_file) 39 | 40 | def test_home(self): 41 | rv = self.app.get('/') 42 | self.assertEqual(rv.status_code, 200) 43 | 44 | def test_products(self): 45 | "Test Products list page" 46 | rv = self.app.get('/en/products') 47 | self.assertEqual(rv.status_code, 200) 48 | self.assertTrue('No Previous Page' in rv.data.decode("utf-8")) 49 | self.assertTrue('No Next Page' in rv.data.decode("utf-8")) 50 | 51 | def test_create_category(self): 52 | "Test creation of new category" 53 | rv = self.app.get('/en/category-create') 54 | self.assertEqual(rv.status_code, 200) 55 | 56 | rv = self.app.post('/en/category-create') 57 | self.assertEqual(rv.status_code, 200) 58 | self.assertTrue('This field is required.' in rv.data.decode("utf-8")) 59 | 60 | rv = self.app.get('/en/categories') 61 | self.assertEqual(rv.status_code, 200) 62 | self.assertFalse('Phones' in rv.data.decode("utf-8")) 63 | 64 | rv = self.app.post('/en/category-create', data={ 65 | 'name': 'Phones', 66 | }) 67 | self.assertEqual(rv.status_code, 302) 68 | 69 | rv = self.app.get('/en/categories') 70 | self.assertEqual(rv.status_code, 200) 71 | self.assertTrue('Phones' in rv.data.decode("utf-8")) 72 | 73 | rv = self.app.get('/en/category/1') 74 | self.assertEqual(rv.status_code, 200) 75 | self.assertTrue('Phones' in rv.data.decode("utf-8")) 76 | 77 | def test_create_product(self): 78 | "Test creation of new product" 79 | rv = self.app.get('/en/product-create') 80 | self.assertEqual(rv.status_code, 200) 81 | 82 | rv = self.app.post('/en/product-create') 83 | self.assertEqual(rv.status_code, 200) 84 | self.assertTrue('This field is required.' in rv.data.decode("utf-8")) 85 | 86 | # Create a category to be used in product creation 87 | rv = self.app.post('/en/category-create', data={ 88 | 'name': 'Phones', 89 | }) 90 | self.assertEqual(rv.status_code, 302) 91 | 92 | rv = self.app.post('/en/product-create', data={ 93 | 'name': 'iPhone 5', 94 | 'price': 549.49, 95 | 'company': 'Apple', 96 | 'category': 1, 97 | 'image': tempfile.NamedTemporaryFile() 98 | }) 99 | self.assertEqual(rv.status_code, 302) 100 | 101 | rv = self.app.get('/en/product/1') 102 | self.assertEqual(rv.status_code, 200) 103 | self.assertTrue('iPhone 5' in rv.data.decode("utf-8")) 104 | self.assertTrue('America/Los_Angeles' in rv.data.decode("utf-8")) 105 | 106 | def test_search_product(self): 107 | "Test searching product" 108 | # Create a category to be used in product creation 109 | rv = self.app.post('/en/category-create', data={ 110 | 'name': 'Phones', 111 | }) 112 | self.assertEqual(rv.status_code, 302) 113 | 114 | # Create a product 115 | rv = self.app.post('/en/product-create', data={ 116 | 'name': 'iPhone 5', 117 | 'price': 549.49, 118 | 'company': 'Apple', 119 | 'category': 1, 120 | 'image': tempfile.NamedTemporaryFile() 121 | }) 122 | self.assertEqual(rv.status_code, 302) 123 | 124 | # Create another product 125 | rv = self.app.post('/en/product-create', data={ 126 | 'name': 'Galaxy S5', 127 | 'price': 549.49, 128 | 'company': 'Samsung', 129 | 'category': 1, 130 | 'image': tempfile.NamedTemporaryFile() 131 | }) 132 | self.assertEqual(rv.status_code, 302) 133 | 134 | self.app.get('/') 135 | 136 | rv = self.app.get('/en/product-search?name=iPhone') 137 | self.assertEqual(rv.status_code, 200) 138 | self.assertTrue('iPhone 5' in rv.data.decode("utf-8")) 139 | self.assertFalse('Galaxy S5' in rv.data.decode("utf-8")) 140 | 141 | rv = self.app.get('/en/product-search?name=iPhone 6') 142 | self.assertEqual(rv.status_code, 200) 143 | self.assertFalse('iPhone 6' in rv.data.decode("utf-8")) 144 | 145 | 146 | if __name__ == '__main__': 147 | try: 148 | unittest.main() 149 | finally: 150 | cov.stop() 151 | cov.save() 152 | cov.report() 153 | cov.html_report(directory = 'coverage') 154 | cov.erase() 155 | -------------------------------------------------------------------------------- /Chapter11/app_tests.py: -------------------------------------------------------------------------------- 1 | import os 2 | from my_app import app, db 3 | import unittest 4 | from unittest import mock 5 | import tempfile 6 | import geoip2.records 7 | import coverage 8 | 9 | cov = coverage.coverage( 10 | omit = [ 11 | '/Users/shalabh.aggarwal/workspace/cookbook10/lib/python3.6/site-packages/*', 12 | 'app_tests.py' 13 | ] 14 | ) 15 | cov.start() 16 | 17 | 18 | class CatalogTestCase(unittest.TestCase): 19 | 20 | def setUp(self): 21 | self.test_db_file = tempfile.mkstemp()[1] 22 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + self.test_db_file 23 | app.config['TESTING'] = True 24 | app.config['WTF_CSRF_ENABLED'] = False 25 | self.app = app.test_client() 26 | self.geoip_city_patcher = mock.patch('geoip2.models.City', 27 | location=geoip2.records.Location(time_zone = 'America/Los_Angeles') 28 | ) 29 | PatchedGeoipCity = self.geoip_city_patcher.start() 30 | self.geoip_reader_patcher = mock.patch('geoip2.database.Reader') 31 | PatchedGeoipReader = self.geoip_reader_patcher.start() 32 | PatchedGeoipReader().city.return_value = PatchedGeoipCity 33 | db.create_all() 34 | 35 | def tearDown(self): 36 | self.geoip_city_patcher.stop() 37 | self.geoip_reader_patcher.stop() 38 | os.remove(self.test_db_file) 39 | 40 | def test_home(self): 41 | rv = self.app.get('/') 42 | self.assertEqual(rv.status_code, 200) 43 | 44 | def test_products(self): 45 | "Test Products list page" 46 | rv = self.app.get('/en/products') 47 | self.assertEqual(rv.status_code, 200) 48 | self.assertTrue('No Previous Page' in rv.data.decode("utf-8")) 49 | self.assertTrue('No Next Page' in rv.data.decode("utf-8")) 50 | 51 | def test_create_category(self): 52 | "Test creation of new category" 53 | rv = self.app.get('/en/category-create') 54 | self.assertEqual(rv.status_code, 200) 55 | 56 | rv = self.app.post('/en/category-create') 57 | self.assertEqual(rv.status_code, 200) 58 | self.assertTrue('This field is required.' in rv.data.decode("utf-8")) 59 | 60 | rv = self.app.get('/en/categories') 61 | self.assertEqual(rv.status_code, 200) 62 | self.assertFalse('Phones' in rv.data.decode("utf-8")) 63 | 64 | rv = self.app.post('/en/category-create', data={ 65 | 'name': 'Phones', 66 | }) 67 | self.assertEqual(rv.status_code, 302) 68 | 69 | rv = self.app.get('/en/categories') 70 | self.assertEqual(rv.status_code, 200) 71 | self.assertTrue('Phones' in rv.data.decode("utf-8")) 72 | 73 | rv = self.app.get('/en/category/1') 74 | self.assertEqual(rv.status_code, 200) 75 | self.assertTrue('Phones' in rv.data.decode("utf-8")) 76 | 77 | def test_create_product(self): 78 | "Test creation of new product" 79 | rv = self.app.get('/en/product-create') 80 | self.assertEqual(rv.status_code, 200) 81 | 82 | rv = self.app.post('/en/product-create') 83 | self.assertEqual(rv.status_code, 200) 84 | self.assertTrue('This field is required.' in rv.data.decode("utf-8")) 85 | 86 | # Create a category to be used in product creation 87 | rv = self.app.post('/en/category-create', data={ 88 | 'name': 'Phones', 89 | }) 90 | self.assertEqual(rv.status_code, 302) 91 | 92 | rv = self.app.post('/en/product-create', data={ 93 | 'name': 'iPhone 5', 94 | 'price': 549.49, 95 | 'company': 'Apple', 96 | 'category': 1, 97 | 'image': tempfile.NamedTemporaryFile() 98 | }) 99 | self.assertEqual(rv.status_code, 302) 100 | 101 | rv = self.app.get('/en/product/1') 102 | self.assertEqual(rv.status_code, 200) 103 | self.assertTrue('iPhone 5' in rv.data.decode("utf-8")) 104 | self.assertTrue('America/Los_Angeles' in rv.data.decode("utf-8")) 105 | 106 | def test_search_product(self): 107 | "Test searching product" 108 | # Create a category to be used in product creation 109 | rv = self.app.post('/en/category-create', data={ 110 | 'name': 'Phones', 111 | }) 112 | self.assertEqual(rv.status_code, 302) 113 | 114 | # Create a product 115 | rv = self.app.post('/en/product-create', data={ 116 | 'name': 'iPhone 5', 117 | 'price': 549.49, 118 | 'company': 'Apple', 119 | 'category': 1, 120 | 'image': tempfile.NamedTemporaryFile() 121 | }) 122 | self.assertEqual(rv.status_code, 302) 123 | 124 | # Create another product 125 | rv = self.app.post('/en/product-create', data={ 126 | 'name': 'Galaxy S5', 127 | 'price': 549.49, 128 | 'company': 'Samsung', 129 | 'category': 1, 130 | 'image': tempfile.NamedTemporaryFile() 131 | }) 132 | self.assertEqual(rv.status_code, 302) 133 | 134 | self.app.get('/') 135 | 136 | rv = self.app.get('/en/product-search?name=iPhone') 137 | self.assertEqual(rv.status_code, 200) 138 | self.assertTrue('iPhone 5' in rv.data.decode("utf-8")) 139 | self.assertFalse('Galaxy S5' in rv.data.decode("utf-8")) 140 | 141 | rv = self.app.get('/en/product-search?name=iPhone 6') 142 | self.assertEqual(rv.status_code, 200) 143 | self.assertFalse('iPhone 6' in rv.data.decode("utf-8")) 144 | 145 | 146 | if __name__ == '__main__': 147 | try: 148 | unittest.main() 149 | finally: 150 | cov.stop() 151 | cov.save() 152 | cov.report() 153 | cov.html_report(directory = 'coverage') 154 | cov.erase() 155 | -------------------------------------------------------------------------------- /Chapter13/Docker/app_tests.py: -------------------------------------------------------------------------------- 1 | import os 2 | from my_app import app, db 3 | import unittest 4 | from unittest import mock 5 | import tempfile 6 | import geoip2.records 7 | import coverage 8 | 9 | cov = coverage.coverage( 10 | omit = [ 11 | '/Users/shalabh.aggarwal/workspace/cookbook10/lib/python3.6/site-packages/*', 12 | 'app_tests.py' 13 | ] 14 | ) 15 | cov.start() 16 | 17 | 18 | class CatalogTestCase(unittest.TestCase): 19 | 20 | def setUp(self): 21 | self.test_db_file = tempfile.mkstemp()[1] 22 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + self.test_db_file 23 | app.config['TESTING'] = True 24 | app.config['WTF_CSRF_ENABLED'] = False 25 | self.app = app.test_client() 26 | self.geoip_city_patcher = mock.patch('geoip2.models.City', 27 | location=geoip2.records.Location(time_zone = 'America/Los_Angeles') 28 | ) 29 | PatchedGeoipCity = self.geoip_city_patcher.start() 30 | self.geoip_reader_patcher = mock.patch('geoip2.database.Reader') 31 | PatchedGeoipReader = self.geoip_reader_patcher.start() 32 | PatchedGeoipReader().city.return_value = PatchedGeoipCity 33 | db.create_all() 34 | 35 | def tearDown(self): 36 | self.geoip_city_patcher.stop() 37 | self.geoip_reader_patcher.stop() 38 | os.remove(self.test_db_file) 39 | 40 | def test_home(self): 41 | rv = self.app.get('/') 42 | self.assertEqual(rv.status_code, 200) 43 | 44 | def test_products(self): 45 | "Test Products list page" 46 | rv = self.app.get('/en/products') 47 | self.assertEqual(rv.status_code, 200) 48 | self.assertTrue('No Previous Page' in rv.data.decode("utf-8")) 49 | self.assertTrue('No Next Page' in rv.data.decode("utf-8")) 50 | 51 | def test_create_category(self): 52 | "Test creation of new category" 53 | rv = self.app.get('/en/category-create') 54 | self.assertEqual(rv.status_code, 200) 55 | 56 | rv = self.app.post('/en/category-create') 57 | self.assertEqual(rv.status_code, 200) 58 | self.assertTrue('This field is required.' in rv.data.decode("utf-8")) 59 | 60 | rv = self.app.get('/en/categories') 61 | self.assertEqual(rv.status_code, 200) 62 | self.assertFalse('Phones' in rv.data.decode("utf-8")) 63 | 64 | rv = self.app.post('/en/category-create', data={ 65 | 'name': 'Phones', 66 | }) 67 | self.assertEqual(rv.status_code, 302) 68 | 69 | rv = self.app.get('/en/categories') 70 | self.assertEqual(rv.status_code, 200) 71 | self.assertTrue('Phones' in rv.data.decode("utf-8")) 72 | 73 | rv = self.app.get('/en/category/1') 74 | self.assertEqual(rv.status_code, 200) 75 | self.assertTrue('Phones' in rv.data.decode("utf-8")) 76 | 77 | def test_create_product(self): 78 | "Test creation of new product" 79 | rv = self.app.get('/en/product-create') 80 | self.assertEqual(rv.status_code, 200) 81 | 82 | rv = self.app.post('/en/product-create') 83 | self.assertEqual(rv.status_code, 200) 84 | self.assertTrue('This field is required.' in rv.data.decode("utf-8")) 85 | 86 | # Create a category to be used in product creation 87 | rv = self.app.post('/en/category-create', data={ 88 | 'name': 'Phones', 89 | }) 90 | self.assertEqual(rv.status_code, 302) 91 | 92 | rv = self.app.post('/en/product-create', data={ 93 | 'name': 'iPhone 5', 94 | 'price': 549.49, 95 | 'company': 'Apple', 96 | 'category': 1, 97 | 'image': tempfile.NamedTemporaryFile() 98 | }) 99 | self.assertEqual(rv.status_code, 302) 100 | 101 | rv = self.app.get('/en/product/1') 102 | self.assertEqual(rv.status_code, 200) 103 | self.assertTrue('iPhone 5' in rv.data.decode("utf-8")) 104 | self.assertTrue('America/Los_Angeles' in rv.data.decode("utf-8")) 105 | 106 | def test_search_product(self): 107 | "Test searching product" 108 | # Create a category to be used in product creation 109 | rv = self.app.post('/en/category-create', data={ 110 | 'name': 'Phones', 111 | }) 112 | self.assertEqual(rv.status_code, 302) 113 | 114 | # Create a product 115 | rv = self.app.post('/en/product-create', data={ 116 | 'name': 'iPhone 5', 117 | 'price': 549.49, 118 | 'company': 'Apple', 119 | 'category': 1, 120 | 'image': tempfile.NamedTemporaryFile() 121 | }) 122 | self.assertEqual(rv.status_code, 302) 123 | 124 | # Create another product 125 | rv = self.app.post('/en/product-create', data={ 126 | 'name': 'Galaxy S5', 127 | 'price': 549.49, 128 | 'company': 'Samsung', 129 | 'category': 1, 130 | 'image': tempfile.NamedTemporaryFile() 131 | }) 132 | self.assertEqual(rv.status_code, 302) 133 | 134 | self.app.get('/') 135 | 136 | rv = self.app.get('/en/product-search?name=iPhone') 137 | self.assertEqual(rv.status_code, 200) 138 | self.assertTrue('iPhone 5' in rv.data.decode("utf-8")) 139 | self.assertFalse('Galaxy S5' in rv.data.decode("utf-8")) 140 | 141 | rv = self.app.get('/en/product-search?name=iPhone 6') 142 | self.assertEqual(rv.status_code, 200) 143 | self.assertFalse('iPhone 6' in rv.data.decode("utf-8")) 144 | 145 | 146 | if __name__ == '__main__': 147 | try: 148 | unittest.main() 149 | finally: 150 | cov.stop() 151 | cov.save() 152 | cov.report() 153 | cov.html_report(directory = 'coverage') 154 | cov.erase() 155 | -------------------------------------------------------------------------------- /Chapter12/my_app/catalog/views.py: -------------------------------------------------------------------------------- 1 | import json 2 | from threading import Thread 3 | from functools import wraps 4 | from flask import request, Blueprint, render_template, jsonify, flash, \ 5 | redirect, url_for 6 | from my_app import db, app, es, cache, mail, celery 7 | from my_app.catalog.models import Product, Category, product_created, \ 8 | category_created 9 | from sqlalchemy.orm.util import join 10 | from flask_mail import Message 11 | 12 | catalog = Blueprint('catalog', __name__) 13 | 14 | 15 | def template_or_json(template=None): 16 | """"Return a dict from your view and this will either 17 | pass it to a template or render json. Use like: 18 | 19 | @template_or_json('template.html') 20 | """ 21 | def decorated(f): 22 | @wraps(f) 23 | def decorated_fn(*args, **kwargs): 24 | ctx = f(*args, **kwargs) 25 | if request.is_xhr or not template: 26 | return jsonify(ctx) 27 | else: 28 | return render_template(template, **ctx) 29 | return decorated_fn 30 | return decorated 31 | 32 | 33 | @celery.task() 34 | def send_mail(category_id, category_name): 35 | with app.app_context(): 36 | category = Category(category_name) 37 | category.id = category_id 38 | message = Message( 39 | "New category added", 40 | recipients=['some-receiver@domain.com'] 41 | ) 42 | message.body = render_template( 43 | "category-create-email-text.html", 44 | category=category 45 | ) 46 | message.html = render_template( 47 | "category-create-email-html.html", 48 | category=category 49 | ) 50 | mail.send(message) 51 | 52 | 53 | @app.errorhandler(404) 54 | def page_not_found(e): 55 | return render_template('404.html'), 404 56 | 57 | 58 | @catalog.route('/') 59 | @catalog.route('/home') 60 | @template_or_json('home.html') 61 | def home(): 62 | products = Product.query.all() 63 | return {'count': len(products)} 64 | 65 | 66 | @catalog.route('/product/') 67 | @cache.memoize(120) 68 | def product(id): 69 | product = Product.query.get_or_404(id) 70 | return render_template('product.html', product=product) 71 | 72 | 73 | @catalog.route('/products') 74 | @catalog.route('/products/') 75 | def products(page=1): 76 | products = Product.query.paginate(page, 10) 77 | return render_template('products.html', products=products) 78 | 79 | 80 | @catalog.route('/product-create', methods=['GET', 'POST']) 81 | def create_product(): 82 | if request.method == 'POST': 83 | name = request.form.get('name') 84 | price = request.form.get('price') 85 | company = request.form.get('company') 86 | categ_name = request.form.get('category') 87 | category = Category.query.filter_by(name=categ_name).first() 88 | if not category: 89 | category = Category(categ_name) 90 | product = Product(name, price, category, company) 91 | db.session.add(product) 92 | db.session.commit() 93 | product_created.send(app, product=product) 94 | #product.add_index_to_es() 95 | flash('The product %s has been created' % name, 'success') 96 | return redirect(url_for('catalog.product', id=product.id)) 97 | return render_template('product-create.html') 98 | 99 | 100 | @catalog.route('/product-search') 101 | @catalog.route('/product-search/') 102 | def product_search(page=1): 103 | name = request.args.get('name') 104 | price = request.args.get('price') 105 | company = request.args.get('company') 106 | category = request.args.get('category') 107 | products = Product.query 108 | if name: 109 | products = products.filter(Product.name.like('%' + name + '%')) 110 | if price: 111 | products = products.filter(Product.price == price) 112 | if company: 113 | products = products.filter(Product.company.like('%' + company + '%')) 114 | if category: 115 | products = products.select_from(join(Product, Category)).filter( 116 | Category.name.like('%' + category + '%') 117 | ) 118 | return render_template( 119 | 'products.html', products=products.paginate(page, 10) 120 | ) 121 | 122 | 123 | @catalog.route('/product-search-whoosh') 124 | @catalog.route('/product-search-whoosh/') 125 | def product_search_whoosh(page=1): 126 | q = request.args.get('q') 127 | products = Product.query.whoosh_search(q) 128 | return render_template( 129 | 'products.html', products=products.paginate(page, 10) 130 | ) 131 | 132 | 133 | @catalog.route('/product-search-es') 134 | @catalog.route('/product-search-es/') 135 | def product_search_es(page=1): 136 | q = request.args.get('q') 137 | products = es.search(index="catalog", body={ 138 | "query": { 139 | "query_string": { 140 | "query": q 141 | } 142 | } 143 | }) 144 | return json.dumps(products) 145 | 146 | 147 | @catalog.route('/category-create', methods=['POST',]) 148 | def create_category(): 149 | name = request.form.get('name') 150 | category = Category(name) 151 | db.session.add(category) 152 | db.session.commit() 153 | category_created.send(app, category=category) 154 | #category.add_index_to_es() 155 | #mail.send(message) 156 | send_mail.apply_async(args=[category.id, category.name]) 157 | return render_template('category.html', category=category) 158 | 159 | 160 | @catalog.route('/category/') 161 | def category(id): 162 | category = Category.query.get_or_404(id) 163 | return render_template('category.html', category=category) 164 | 165 | 166 | @catalog.route('/categories') 167 | @cache.cached(timeout=120) 168 | def categories(): 169 | categories = Category.query.all() 170 | return render_template('categories.html', categories=categories) 171 | -------------------------------------------------------------------------------- /Chapter10/my_app/catalog/views.py: -------------------------------------------------------------------------------- 1 | import os 2 | from functools import wraps 3 | from werkzeug import secure_filename 4 | from flask import request, Blueprint, render_template, jsonify, flash, \ 5 | redirect, url_for as flask_url_for, g, abort 6 | from my_app import db, app, ALLOWED_EXTENSIONS, babel 7 | from my_app.catalog.models import Product, Category, ProductForm, CategoryForm 8 | from sqlalchemy.orm.util import join 9 | from flask_babel import lazy_gettext as _ 10 | import geoip2.database 11 | from geoip2.errors import AddressNotFoundError 12 | 13 | catalog = Blueprint('catalog', __name__) 14 | 15 | 16 | @app.before_request 17 | def before(): 18 | if request.view_args and 'lang' in request.view_args: 19 | g.current_lang = request.view_args['lang'] 20 | request.view_args.pop('lang') 21 | 22 | 23 | @app.context_processor 24 | def inject_url_for(): 25 | return { 26 | 'url_for': lambda endpoint, **kwargs: flask_url_for( 27 | endpoint, lang=g.get('current_lang', 'en'), **kwargs 28 | ) 29 | } 30 | 31 | 32 | url_for = inject_url_for()['url_for'] 33 | 34 | 35 | @babel.localeselector 36 | def get_locale(): 37 | return g.get('current_lang', 'en') 38 | 39 | 40 | def template_or_json(template=None): 41 | """"Return a dict from your view and this will either 42 | pass it to a template or render json. Use like: 43 | 44 | @template_or_json('template.html') 45 | """ 46 | def decorated(f): 47 | @wraps(f) 48 | def decorated_fn(*args, **kwargs): 49 | ctx = f(*args, **kwargs) 50 | if request.is_xhr or not template: 51 | return jsonify(ctx) 52 | else: 53 | return render_template(template, **ctx) 54 | return decorated_fn 55 | return decorated 56 | 57 | 58 | def allowed_file(filename): 59 | return '.' in filename and \ 60 | filename.lower().rsplit('.', 1)[1] in ALLOWED_EXTENSIONS 61 | 62 | 63 | @app.errorhandler(404) 64 | def page_not_found(e): 65 | app.logger.error(e) 66 | return render_template('404.html'), 404 67 | 68 | 69 | @catalog.route('/') 70 | @catalog.route('//') 71 | @catalog.route('//home') 72 | @template_or_json('home.html') 73 | def home(): 74 | products = Product.query.all() 75 | app.logger.info( 76 | 'Home page with total of %d products' % len(products) 77 | ) 78 | return {'count': len(products)} 79 | 80 | 81 | @catalog.route('//product/') 82 | def product(id): 83 | product = Product.query.filter_by(id=id).first() 84 | if not product: 85 | app.logger.warning('Requested product not found.') 86 | abort(404) 87 | return render_template('product.html', product=product) 88 | 89 | 90 | @catalog.route('//products') 91 | @catalog.route('//products/') 92 | def products(page=1): 93 | products = Product.query.paginate(page, 10) 94 | return render_template('products.html', products=products) 95 | 96 | 97 | @catalog.route('//product-create', methods=['GET', 'POST']) 98 | def create_product(): 99 | form = ProductForm() 100 | 101 | if form.validate_on_submit(): 102 | name = form.name.data 103 | price = form.price.data 104 | category = Category.query.get_or_404( 105 | form.category.data 106 | ) 107 | image = request.files and request.files['image'] 108 | filename = '' 109 | if image and allowed_file(image.filename): 110 | filename = secure_filename(image.filename) 111 | image.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) 112 | reader = geoip2.database.Reader('GeoLite2-City_20190416/GeoLite2-City.mmdb') 113 | try: 114 | match = reader.city(request.remote_addr) 115 | except AddressNotFoundError: 116 | match = None 117 | product = Product( 118 | name, price, category, filename, 119 | match and match.location.time_zone or 'Localhost' 120 | ) 121 | db.session.add(product) 122 | db.session.commit() 123 | flash( 124 | _('The product %(name)s has been created', name=name), 125 | 'success' 126 | ) 127 | return redirect(url_for('catalog.product', id=product.id)) 128 | 129 | if form.errors: 130 | flash(form.errors, 'danger') 131 | 132 | return render_template('product-create.html', form=form) 133 | 134 | 135 | @catalog.route('//product-search') 136 | @catalog.route('//product-search/') 137 | def product_search(page=1): 138 | name = request.args.get('name') 139 | price = request.args.get('price') 140 | company = request.args.get('company') 141 | category = request.args.get('category') 142 | products = Product.query 143 | if name: 144 | products = products.filter(Product.name.like('%' + name + '%')) 145 | if price: 146 | products = products.filter(Product.price == price) 147 | if company: 148 | products = products.filter(Product.company.like('%' + company + '%')) 149 | if category: 150 | products = products.select_from(join(Product, Category)).filter( 151 | Category.name.like('%' + category + '%') 152 | ) 153 | return render_template( 154 | 'products.html', products=products.paginate(page, 10) 155 | ) 156 | 157 | 158 | @catalog.route('//category-create', methods=['GET', 'POST']) 159 | def create_category(): 160 | form = CategoryForm() 161 | 162 | if form.validate_on_submit(): 163 | name = form.name.data 164 | category = Category(name) 165 | db.session.add(category) 166 | db.session.commit() 167 | flash( 168 | _('The category %(name)s has been created', name=name), 169 | 'success' 170 | ) 171 | return redirect( 172 | url_for('catalog.category', id=category.id) 173 | ) 174 | 175 | if form.errors: 176 | flash(form.errors, 'danger') 177 | 178 | return render_template('category-create.html', form=form) 179 | 180 | 181 | @catalog.route('//category/') 182 | def category(id): 183 | category = Category.query.get_or_404(id) 184 | return render_template('category.html', category=category) 185 | 186 | 187 | @catalog.route('//categories') 188 | def categories(): 189 | categories = Category.query.all() 190 | return render_template('categories.html', categories=categories) 191 | -------------------------------------------------------------------------------- /Chapter07/my_app/catalog/views.py: -------------------------------------------------------------------------------- 1 | import json 2 | from functools import wraps 3 | from flask import request, Blueprint, render_template, jsonify, flash, \ 4 | redirect, url_for, abort 5 | from flask_restful import Resource, reqparse 6 | from sqlalchemy.orm.util import join 7 | 8 | from my_app import db, app, api 9 | from my_app.catalog.models import Product, Category 10 | 11 | 12 | catalog = Blueprint('catalog', __name__) 13 | 14 | 15 | parser = reqparse.RequestParser() 16 | parser.add_argument('name', type=str) 17 | parser.add_argument('price', type=float) 18 | parser.add_argument('category', type=dict) 19 | 20 | 21 | def template_or_json(template=None): 22 | """"Return a dict from your view and this will either 23 | pass it to a template or render json. Use like: 24 | 25 | @template_or_json('template.html') 26 | """ 27 | def decorated(f): 28 | @wraps(f) 29 | def decorated_fn(*args, **kwargs): 30 | ctx = f(*args, **kwargs) 31 | if request.is_xhr or not template: 32 | return jsonify(ctx) 33 | else: 34 | return render_template(template, **ctx) 35 | return decorated_fn 36 | return decorated 37 | 38 | 39 | @app.errorhandler(404) 40 | def page_not_found(e): 41 | return render_template('404.html'), 404 42 | 43 | 44 | @catalog.route('/') 45 | @catalog.route('/home') 46 | @template_or_json('home.html') 47 | def home(): 48 | products = Product.query.all() 49 | return {'count': len(products)} 50 | 51 | 52 | @catalog.route('/product/') 53 | def product(id): 54 | product = Product.query.get_or_404(id) 55 | return render_template('product.html', product=product) 56 | 57 | 58 | @catalog.route('/products') 59 | @catalog.route('/products/') 60 | def products(page=1): 61 | products = Product.query.paginate(page, 10) 62 | return render_template('products.html', products=products) 63 | 64 | 65 | @catalog.route('/product-create', methods=['GET', 'POST']) 66 | def create_product(): 67 | if request.method == 'POST': 68 | name = request.form.get('name') 69 | price = request.form.get('price') 70 | categ_name = request.form.get('category') 71 | category = Category.query.filter_by(name=categ_name).first() 72 | if not category: 73 | category = Category(categ_name) 74 | product = Product(name, price, category) 75 | db.session.add(product) 76 | db.session.commit() 77 | flash('The product %s has been created' % name, 'success') 78 | return redirect(url_for('catalog.product', id=product.id)) 79 | return render_template('product-create.html') 80 | 81 | 82 | @catalog.route('/product-search') 83 | @catalog.route('/product-search/') 84 | def product_search(page=1): 85 | name = request.args.get('name') 86 | price = request.args.get('price') 87 | company = request.args.get('company') 88 | category = request.args.get('category') 89 | products = Product.query 90 | if name: 91 | products = products.filter(Product.name.like('%' + name + '%')) 92 | if price: 93 | products = products.filter(Product.price == price) 94 | if company: 95 | products = products.filter(Product.company.like('%' + company + '%')) 96 | if category: 97 | products = products.select_from(join(Product, Category)).filter( 98 | Category.name.like('%' + category + '%') 99 | ) 100 | return render_template( 101 | 'products.html', products=products.paginate(page, 10) 102 | ) 103 | 104 | 105 | @catalog.route('/category-create', methods=['POST',]) 106 | def create_category(): 107 | name = request.form.get('name') 108 | category = Category(name) 109 | db.session.add(category) 110 | db.session.commit() 111 | return render_template('category.html', category=category) 112 | 113 | 114 | @catalog.route('/category/') 115 | def category(id): 116 | category = Category.query.get_or_404(id) 117 | return render_template('category.html', category=category) 118 | 119 | 120 | @catalog.route('/categories') 121 | def categories(): 122 | categories = Category.query.all() 123 | return render_template('categories.html', categories=categories) 124 | 125 | 126 | class ProductApi(Resource): 127 | 128 | def get(self, id=None, page=1): 129 | if not id: 130 | products = Product.query.paginate(page, 10).items 131 | else: 132 | products = [Product.query.get(id)] 133 | if not products: 134 | abort(404) 135 | res = {} 136 | for product in products: 137 | res[product.id] = { 138 | 'name': product.name, 139 | 'price': product.price, 140 | 'category': product.category.name 141 | } 142 | return json.dumps(res) 143 | 144 | def post(self): 145 | args = parser.parse_args() 146 | name = args['name'] 147 | price = args['price'] 148 | categ_name = args['category']['name'] 149 | category = Category.query.filter_by(name=categ_name).first() 150 | if not category: 151 | category = Category(categ_name) 152 | product = Product(name, price, category) 153 | db.session.add(product) 154 | db.session.commit() 155 | res = {} 156 | res[product.id] = { 157 | 'name': product.name, 158 | 'price': product.price, 159 | 'category': product.category.name, 160 | } 161 | return json.dumps(res) 162 | 163 | def put(self, id): 164 | args = parser.parse_args() 165 | name = args['name'] 166 | price = args['price'] 167 | categ_name = args['category']['name'] 168 | category = Category.query.filter_by(name=categ_name).first() 169 | Product.query.filter_by(id=id).update({ 170 | 'name': name, 171 | 'price': price, 172 | 'category_id': category.id, 173 | }) 174 | db.session.commit() 175 | product = Product.query.get_or_404(id) 176 | res = {} 177 | res[product.id] = { 178 | 'name': product.name, 179 | 'price': product.price, 180 | 'category': product.category.name, 181 | } 182 | return json.dumps(res) 183 | 184 | def delete(self, id): 185 | product = Product.query.filter_by(id=id) 186 | product.delete() 187 | db.session.commit() 188 | return json.dumps({'response': 'Success'}) 189 | 190 | 191 | api.add_resource( 192 | ProductApi, 193 | '/api/product', 194 | '/api/product/', 195 | '/api/product//' 196 | ) 197 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Flask Framework Cookbook 5 | 6 | Flask Framework Cookbook 7 | 8 | This is the code repository for [Flask Framework Cookbook](https://www.packtpub.com/web-development/flask-framework-cookbook-second-edition?utm_source=github&utm_medium=repository&utm_campaign=9781789951295), published by Packt. 9 | 10 | **Over 80 proven recipes and techniques for Python web development with Flask** 11 | 12 | ## What is this book about? 13 | Flask, the lightweight Python web framework, is popular due to its powerful modular design that lets you build scalable web apps. With this recipe-based guide, you’ll explore modern solutions and best practices for Flask web development. 14 | 15 | This book covers the following exciting features: 16 | * Explore web application development in Flask, right from installation to post-deployment stages 17 | * Make use of advanced templating and data modeling techniques 18 | * Discover effective debugging, logging, and error handling techniques in Flask 19 | * Integrate Flask with different technologies such as Redis, Sentry, and MongoDB 20 | * Deploy and package Flask applications with Docker and Kubernetes 21 | 22 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1789951291) today! 23 | 24 | https://www.packtpub.com/ 26 | 27 | 28 | ## Instructions and Navigations 29 | All of the code is organized into folders. For example, Chapter02. 30 | 31 | The code will look like the following: 32 | ``` 33 | from flask import Flask 34 | app = Flask(__name__) 35 | 36 | @app.route('/') 37 | def hello_world(): 38 | return 'Hello to the World of Flask!' 39 | 40 | if __name__ == '__main__': 41 | app.run() 42 | ``` 43 | 44 | **Following is what you need for this book:** 45 | 46 | If you are a web developer who wants to learn more about developing scalable and production-ready applications in Flask, this is the book for you. You’ll also find this book useful if you are already aware of Flask's major extensions and want to use them for better application development. Basic Python programming experience along with basic understanding of Flask is assumed. 47 | 48 | With the following software and hardware list you can run all code files present in the book (Chapter 1-13). 49 | 50 | ### Software and Hardware List 51 | 52 | | Chapter | Software required | OS required | 53 | | -------- | ------------------------------------ | -----------------------------------| 54 | | 1 | Flask, Virtualenv, virtualenvwrapper | Windows, Mac OS X, and Linux (Any) | 55 | | 2 | Twitter Bootstrap, ccy, Moment.js | Windows, Mac OS X, and Linux (Any) | 56 | | 3 | Flask-sqlalchemy, requests, | Windows, Mac OS X, and Linux (Any) | 57 | | | Redis, Flask-Migrate, Flask-mongoengine, | | 58 | | 4 | - | - | 59 | | 5 | Wtforms, Flask-WTF | Windows, Mac OS X, and Linux (Any) | 60 | | 6 | Flask-Login, pyOpenSSL, Flask-Dance, | Windows, Mac OS X, and Linux (Any) | 61 | | | python-ldap | | 62 | | 7 | Flask-Restful, requests | Windows, Mac OS X, and Linux (Any) | 63 | | 8 | Flask-Admin, CKEditor.js | Windows, Mac OS X, and Linux (Any) | 64 | | 9 | Flask-Babel | Windows, Mac OS X, and Linux (Any) | 65 | | 10 | sentry-sdk, Nose, geoip2, Coverage | Windows, Mac OS X, and Linux (Any) | 66 | | 11 | mod_wsgi, Nginx, uwsgi, gunicorn, | Windows, Mac OS X, and Linux (Any) | 67 | | | supervisor, Tornado, boto3, newrelic, | | 68 | | | Heroku CLI, AWS Elastic Beanstalk setup | | 69 | | 12 | Docker, Minikube, zappa | Windows, Mac OS X, and Linux (Any) | 70 | | 13 | Flask-WhooshAlchemy, elasticsearch python | Windows, Mac OS X, and Linux (Any) | 71 | | | library, Elasticsearch, Flask-Caching, | | 72 | | | Flask-Mail, Celery | | 73 | 74 | We also provide a PDF file that has color images of the screenshots/diagrams used in this book. [Click here to download it](https://static.packt-cdn.com/downloads/9781789951295_ColorImages.pdf). 75 | 76 | 77 | ### Related products 78 | * Hands-On RESTful Python Web Services - Second Edition [[Packt]](https://www.packtpub.com/application-development/hands-restful-python-web-services-second-edition?utm_source=github&utm_medium=repository&utm_campaign=9781789532227) [[Amazon]](https://www.amazon.com/dp/1789532221) 79 | 80 | * Django 2 by Example [[Packt]](https://www.packtpub.com/application-development/django-2-example?utm_source=github&utm_medium=repository&utm_campaign=9781788472487) [[Amazon]](https://www.amazon.com/dp/1788472489) 81 | 82 | ## Get to Know the Author 83 | **Shalabh Aggarwal** 84 | has more than 10 years' experience in developing and managing enterprise systems, as well as web and mobile applications for small-to large-scale industries. He started his career working on Python, and although he now works on multiple technologies, he remains a Python developer at heart. He is passionate about open source technologies and writes highly readable and quality code. 85 | He is also active in voluntary training for engineering students on non-conventional and open source topics. When not working with full-time assignments, he consults for start-ups on leveraging different technologies. When not writing code, he writes technical and nontechnical literature, which is published across multiple blogs. 86 | 87 | ## Other books by the authors 88 | * [Flask Framework Cookbook](https://www.packtpub.com/web-development/flask-framework-cookbook?utm_source=github&utm_medium=repository&utm_campaign=9781783983407) 89 | 90 | ### Suggestions and Feedback 91 | [Click here](https://docs.google.com/forms/d/e/1FAIpQLSdy7dATC6QmEL81FIUuymZ0Wy9vH1jHkvpY57OiMeKGqib_Ow/viewform) if you have any feedback or suggestions. 92 | --------------------------------------------------------------------------------