diff --git a/snooty.toml b/snooty.toml index 125cb037..c888bf9c 100644 --- a/snooty.toml +++ b/snooty.toml @@ -12,7 +12,8 @@ toc_landing_pages = [ "/crud/query", "/crud/update", "/monitoring-and-logging", - "/reference" + "/reference", + "/integrations" ] intersphinx = [ diff --git a/source/integrations.txt b/source/integrations.txt index 41eb6a35..d0b87295 100644 --- a/source/integrations.txt +++ b/source/integrations.txt @@ -18,6 +18,10 @@ Third-Party Integrations and Tools .. meta:: :keywords: pypi, package, web, module, pip +.. toctree:: + + Tutorial: Flask and Celery Integration + Overview -------- diff --git a/source/integrations/flask-celery-integration.txt b/source/integrations/flask-celery-integration.txt new file mode 100644 index 00000000..cde335e4 --- /dev/null +++ b/source/integrations/flask-celery-integration.txt @@ -0,0 +1,566 @@ +.. _pymongo-flask-celery: +.. original URL: https://www.mongodb.com/developer/products/mongodb/python-flask-celery-newsletter/ + +====================================== +Tutorial: Celery and Flask Integration +====================================== + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +.. facet:: + :name: genre + :values: tutorial + +.. meta:: + :keywords: code example, batch, framework + +Overview +-------- + +In this tutorial, you can learn how to use MongoDB, Celery, and Flask +to build a newsletter platform. This application allows users to subscribe to +newsletters, and administrators to manage and send batch emails asynchronously. + +Celery +~~~~~~ + +Celery is an open-source distributed task queue that handles large +volumes of messages efficiently. It supports asynchronous processing and task +scheduling. For more information, see the `Celery webpage +`__. + +Flask +~~~~~ + +Flask is a lightweight web application framework with built-in configuration and +convention defaults that provide consistency to developers across projects. For +more information, see the `Flask webpage +`__. + +Tutorial +-------- + +This tutorial creates a modified version of the sample application in the +:github:`Newsletter Platform with JavaScript, Flask, and MongoDB sample project +` GitHub repository. + +Prerequisites +~~~~~~~~~~~~~ + +Ensure that you have the following components installed and set up before you start +this tutorial: + +- A MongoDB cluster. We recommend that you use Atlas. To learn how + to create an Atlas cluster, see the + :atlas:`Get Started with Atlas ` page + in the Atlas documentation. +- A database named ``newsletter`` in your cluster. For more information, see + the :atlas:`Create a Database ` page + in the Atlas guide. +- `RabbitMQ `__ to use as a message broker for Celery. +- `Gmail `__ to use as an SMTP server. For more information about + SMTP servers, see the :wikipedia:`Simple Mail Transfer Protocol + ` Wikipedia page. +- `Python 3.8 or later `__ + +Setup +~~~~~ + +.. procedure:: + :style: connected + + .. step:: Create your project directory and structure + + The name of your project directory is ``newsletter``. Create your directory and navigate to it by running the following commands in terminal: + + .. code-block:: bash + + mkdir newsletter + cd newsletter + + The following files will hold the code for your application: + + - ``app.py``: The main entry point for your Flask application + - ``config.py``: Configuration settings for your application, including + MongoDB connection details, mail server configuration, Celery broker + connection, and any other environment-specific variables + - ``tasks.py``: Defines background tasks to send emails asynchronously + - ``routes.py``: Defines the routes (URLs) that your application responds to + + We recommend structuring your application to separate concerns, which can + make the application modular and more maintainable. + + In your project directory, create the following structure: + + .. code-block:: none + + newsletter/ + ├── app.py + ├── config.py + ├── routes.py + ├── tasks.py + ├── templates/ + │ ├── admin.html + │ └── subscribe.html + └── static/ + └── styles.css + + .. step:: Install the required Python packages + + Your application depends on the following libraries: + + - `Flask `__ for handling the web server and routing + - `Flask Mail `__ for sending emails from your application + - :ref:`{+driver-short+} ` + - `Celery `__ to manage tasks, such + as sending batch emails + + .. tip:: Use a Virtual Environment + + Python `virtual environments + `__ allow you to install + different versions of libraries for different projects. Before running + any ``pip`` commands, ensure that your ``virtualenv`` is active. + + Run the following ``pip`` command in your terminal to install the dependencies: + + .. code-block:: bash + + pip install Flask Flask-Mail pymongo celery + +Configure Your Application +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``config.py`` file contains the settings and credentials to perform the +following actions: + +- Connect Celery to RabbitMQ as its message broker +- Configure Flask-Mail to use Gmail as its SMTP server +- Connect your application to your MongoDB server + +Define the necessary configurations by adding the following code to your +``config.py`` file: + +.. code-block:: python + + import os + + class Config: + MAIL_SERVER = 'smtp.gmail.com' + MAIL_PORT = 587 + MAIL_USE_TLS = True + MAIL_USERNAME = '' # Your email address without '@gmail.com' + MAIL_PASSWORD = '' + ALLOWED_IPS = ['127.0.0.1'] + MONGO_URI = '' + CELERY_BROKER_URL = 'amqp://guest:guest@localhost//' + RESULT_BACKEND = MONGO_URI + '/celery_results' + +You must provide your Gmail credentials (``MAIL_USERNAME`` and ``MAIL_PASSWORD``) to +enable your application to send emails. For security purposes, we recommend that +you generate an app password to use, rather than using your primary password. +For more information, see the `App Password settings +`__ in your Google Account. + +You must also create a connection string to set into the ``MONGO_URI`` +environment variable. For more information see the :ref:`Create a Connection +String ` section of this guide. + +The provided Celery broker URL (``CELERY_BROKER_URL``) specifies RabbitMQ as its broker, +but you can customize this URL to support other implementations. For more +information, see the `Broker Settings +`__ +section of the Celery documentation. + +Initialize Flask, MongoDB, and Celery +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Initialize Flask, MongoDB, and Celery by adding the following code to your +``app.py`` file: + +.. code-block:: python + + from flask import Flask + from flask_mail import Mail + from pymongo import MongoClient + from celery import Celery + + app = Flask(__name__) + app.config.from_object('config.Config') + + mail = Mail(app) + client = MongoClient(app.config['MONGO_URI']) + db = client.get_database(name="newsletter") + + celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL']) + celery.conf.update(app.config) + + from routes import * + from tasks import * + + if __name__ == '__main__': + app.run(debug=True) + +This opens a connection to the ``newsletter`` database in your MongoDB cluster +and configures your Celery task queue. + +Define Your Routes +~~~~~~~~~~~~~~~~~~ + +Define the ``root``, ``admin``, ``subscribe``, and ``send-newsletter`` routes by adding the following code to your ``routes.py`` file: + +.. code-block:: python + + from flask import render_template, request, abort, jsonify + from app import app, db + from tasks import send_emails + + @app.before_request + def limit_remote_addr(): + if 'X-Forwarded-For' in request.headers: + remote_addr = request.headers['X-Forwarded-For'].split(',')[0] + else: + remote_addr = request.remote_addr + + if request.endpoint == 'admin' and remote_addr not in app.config['ALLOWED_IPS']: + abort(403) + + @app.route('/') + def home(): + return render_template('subscribe.html') + + @app.route('/admin') + def admin(): + return render_template('admin.html') + + @app.route('/subscribe', methods=['POST']) + def subscribe(): + first_name = request.form['firstname'] + last_name = request.form['lastname'] + email = request.form['email'] + + if db.users.find_one({'email': email}): + return """ +
+ This email is already subscribed! +
+ """, 409 + + db.users.insert_one({'firstname': first_name, 'lastname': last_name, 'email': email, 'subscribed': True}) + return """ +
+ Subscribed successfully! +
+ """, 200 + + @app.route('/send-newsletters', methods=['POST']) + def send_newsletters(): + title = request.form['title'] + body = request.form['body'] + subscribers = list(db.users.find({'subscribed': True})) + + for subscriber in subscribers: + subscriber['_id'] = str(subscriber['_id']) + + send_emails.apply_async(args=[subscribers, title, body]) + return jsonify({'message': 'Emails are being sent!'}), 202 + +You can add more security protections or customize user-facing alerts for your +application in this file. + +Create Your Pages +~~~~~~~~~~~~~~~~~ + +You can build your user interface in the ``templates`` directory. + +Because this application uses asynchronous messages, the scripts in the +following files use :wikipedia:`Fetch API calls `. They also +handle timeouts and errors. + +Copy the following code into your ``subscribe.html`` file to create your +:guilabel:`Subscribe to Newsletter` page. + +.. code-block:: html + + + + + + + Subscribe to Newsletter + + + +

Subscribe to our Newsletter

+
+ + +
+ + +
+ + +
+ +
+
+ + + + +The script for the admin page displays an alert to the user that depends on the +success of the ``send_newsletter`` call. + +Copy the following code into your ``admin.html`` file to create your +:guilabel:`Send Newsletter` page: + +.. code-block:: html + + + + + + + Admin - Send Newsletter + + + +

Send Newsletter

+
+ + +
+ + +
+ +
+
+ + + + +Format Your Pages +~~~~~~~~~~~~~~~~~ + +You can apply a style sheet to your templates by adding the following code to +the ``styles.css`` file: + +.. code-block:: css + + body { + font-family: system-ui; + font-optical-sizing: auto; + font-weight: 300; + font-style: normal; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100vh; + background-color: #040100; + } + + h1 { + color: white; + } + + form { + background: #023430; + padding: 30px 40px; + border-radius: 8px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + width: 100%; + max-width: 400px; + margin: 20px 0; + } + + label { + display: block; + margin-bottom: 8px; + font-weight: bold; + color: white; + } + + input[type="text"], + input[type="email"], + textarea { + width: 100%; + padding: 10px; + margin-bottom: 10px; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 16px; + } + + button { + background: #00ED64; + color: white; + padding: 10px 20px; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 16px; + font-family: "Nunito", sans-serif; + } + + button:hover { + background: #00684A; + } + + #response { + margin-top: 20px; + font-size: 16px; + color: #28a745; + } + + footer { + text-align: center; + padding: 20px; + margin-top: 20px; + font-size: 16px; + color: #666; + } + +You can modify this style sheet or create your own to customize your +application. + +Testing the Platform +~~~~~~~~~~~~~~~~~~~~ + +After you complete the previous steps, you have a working application that +uses MongoDB, Flask, and Celery to manage a newsletter platform. + +You can use the following steps to test your application: + +.. procedure:: + :style: connected + + .. step:: Start your background services + + Start your RabbitMQ node. For instructions, see the `RabbitMQ + documentation `__ for your + operating system. + + .. step:: Start your application + + Use the following code to start your application: + + .. code-block:: bash + + flask --app app run + + In another terminal, start the Celery worker: + + .. code-block:: bash + + celery -A app.celery worker --loglevel=info + + .. step:: Create a subscriber + + Navigate to ``localhost:5000`` in your browser to open the + :guilabel:`Subscribe to our Newsletter` page. + + Enter the subscriber information and click :guilabel:`Subscribe`. + + To confirm that you created a new subscriber, open `Atlas + `__ and navigate to the + ``users`` collection in your ``newletter`` database. + + .. step:: Dispatch a newsletter + + Navigate to ``localhost:5000/admin`` in your browser to open the + :guilabel:`Send Newsletter` page. Enter the newsletter details and click + :guilabel:`Send`. + + Your Celery worker log will display an ``Email sent`` log entry similar to + the following image: + + .. code-block:: bash + + [2025-05-27 09:54:43,873: INFO/ForkPoolWorker-7] Task tasks.send_emails[7d7f9616-7b9b-4508-a889-95c35f54fe43] succeeded in 3.93334774998948s: {'result': 'All emails sent'} + [2025-05-27 10:04:52,043: INFO/MainProcess] Task tasks.send_emails[ac2ec70f-2d3e-444a-95bb-185ac659f460] received + [2025-05-27 10:04:52,046: WARNING/ForkPoolWorker-7] Sending email to + [2025-05-27 10:04:53,474: WARNING/ForkPoolWorker-7] Email sent + + You can also confirm that you sent an email by navigating to the + ``deliveries`` collection in your ``newsletter`` database. + +Next Steps +~~~~~~~~~~ + +This application demonstrates how to integrate with the Celery task queue to +manage subscriber data, and send batch emails. You can further enhance this +platform by integrating analytics, customizing email templates, and implementing +automated responses. + +More Resources +-------------- + +For more information about the components used in this tutorial, see the following +resources: + +- `Flask `__ +- `Flask Mail `__ +- `Celery `__ +- `RabbitMQ `__ + +To find support or to contribute to the MongoDB community, see the `MongoDB Developer Community `__ page. \ No newline at end of file