All posts by marmik

How to integrate Celery into Django project

What is Celery?

Celery is a distributed task queue that allows us to execute jobs in background. This article explains how to set up Celery with Django to perform a background task.

Advantages:

  • Large or small, Celery makes scheduling such periodic tasks easy.
  • You never want end users to have to wait unnecessarily for pages to load or actions to complete. If a long process is part of your application’s workflow, you can use Celery to execute that process in the background, as resources become available, so that your application can continue to respond to client requests.

Celery uses brokers to pass messages between a Django Project and the Celery workers. We will use Redis as the message broker.

Installation

Before diving into Celery, follow the below setup points

Create a new virtualenv ‘venv’ using following command:

$ virtualenv venv

To activate the environment use command:

$ source ./venv/bin/activate

Install django and create a django project ‘myproject’. Make sure to activate a virtualenv, create a requirements.txt file and run the migrations. Then fire up the server and navigate to http://localhost:8000/ in your browser. You should see the familiar “Congratulations on your first Django-powered page” text. When done, kill the sever.

Let’s install Celery:

$ pip install celery

$ pip freeze > requirements.txt

Now we will integrate Celery into our Django project with the following steps:

Step 1:

Inside the myproject directory i.e beside your settings.py create a new file called celery.py and add the following code in that:

from __future__ import absolute_import
import os
from celery import Celery
from django.conf import settings

# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
app = Celery('myproject')

# Using a string here means the worker will not have to
# pickle the object when using Windows.
app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

@app.task(bind=True)
def debug_task(self):
    print('Request: {0!r}'.format(self.request))

Let’s break down what happens in the first module, first we import absolute imports from the future, so that our celery.py module will not clash with the library:

from __future__ import absolute_import

Then we set the default DJANGO_SETTINGS_MODULE for the celery command-line program:

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')

Specifying the settings here means the celery command line program will know where your Django project is. This statement must always appear before the app instance is created, which is what we do next:

app = Celery('myproject')

This is your instance of the library, you can have many instances but there’s probably no reason for that when using Django.

We also add the Django settings module as a configuration source for Celery. This means that you don’t have to use multiple configuration files, and instead configure Celery directly from the Django settings.

You can pass the object directly here, but using a string is better since then the worker doesn’t have to serialize the object when using Windows or execv:

app.config_from_object('django.conf:settings')

Next, a common practice for reusable apps is to define all tasks in a separate tasks.py module, and Celery does have a way to autodiscover these modules:

app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

Step 2:

To ensure that the Celery app is loaded when Django starts, add the following code into the __init__.py file that sits next to your settings.py file:

from __future__ import absolute_import
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.

from .celery import app as celery_app

Project layout should look like:

├── manage.py
├── myproject
│   ├── __init__.py
│   ├── celery.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── requirements.txt

Step 3:

Celery uses “brokers” to pass messages between a Django Project and the Celery workers. In this article, we will use Redis as the message broker.

First, install Redis from the official download page and then turn to your terminal, in a new terminal window, fire up the server:

$ redis-server

You can test that Redis is working properly by typing this into your terminal:

$ redis-cli ping

Redis should reply with PONG – try it!

Once Redis is up, add the following code to your settings.py file:

# CELERY STUFF
BROKER_URL = 'redis://localhost:6379'
CELERY_RESULT_BACKEND = 'redis://localhost:6379'
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE = 'Africa/Nairobi'

You also need to add Redis as a dependency in the Django Project:

$ pip install redis==2.10.3
$ pip freeze > requirements.txt

Test that the Celery worker is ready to receive tasks:

$ celery -A myproject worker -l info

Kill the process with CTRL-C. Now, test that the Celery task scheduler is ready for action:

$ celery -A myproject beat -l info

That’s it! You can now use Celery with Django. For more information on setting up Celery with Django, please check out the official Celery documentation.

How to implement PayPal payment gateway

The PayPal REST APIs are supported in two environments. Use the Sandbox environment for testing purposes, then move to the live environment for production processing.

The following endpoints address are two environments:

Sandbox (for testing) : https://api.sandbox.paypal.com 
Live (production) : https://api.paypal.com

A complete REST operation is formed by combining an HTTP method with the full URI to the resource you’re addressing. For example, here is the operation to create a new payment:

POST https://api.paypal.com/v1/payments/payment

The PayPal REST sdk can be obtained through pip

pip install paypalrestsdk

OAuth Request / Response

import paypalrestsdk
api = paypalrestsdk.set_config(
       mode="sandbox", # sandbox or live
       client_id="CLIENT_ID",
       client_secret="CLIENT_SECRET")
api.get_access_token()

Client Id and Secret Id can be obtained from the application created in the paypal account.

For the following each API call, you’ll need to set request headers, including the access token.

Creating a WebProfile:

web_profile = WebProfile({
        "name": Web_Profile_Name,
        "presentation": {
            "brand_name": "BusinessName",
            "logo_image": URL to logo image,
            "locale_code": "US"
            },
        "input_fields": {
            "allow_note": 1,
            "no_shipping": 1,
            "address_override": 1
            },
        "flow_config": {
            "landing_page_type": "Login"
            }
    })
web_profile.create(): # Will return True or False
name:

The name of the web experience profile which should be unique among the profiles for a given merchant.

presentation:

It contains the parameters for style and presentation.

input_fields:

Parameters for input fields customization:

  1. allow_note : It enables the buyer to enter a note to the merchant on the paypal page during checkout.

  2. no_shipping : Determines whether or not PayPal displays shipping address fields on the experience pages.

    • 0 – PayPal displays the shipping address on the PayPal pages.
    • 1 – PayPal does not display shipping address fields whatsoever.
    • 2 – If you do not pass the shipping address, PayPal obtains it from the buyer’s account profile.
  3. address_override : Determines if the PayPal pages should display the shipping address supplied in this call, rather than the shipping address on file with PayPal for this buyer.

    • 0 – PayPal pages should display the address on file
    • 1 – PayPal pages should display the addresses supplied in this call instead of the address from the buyer’s PayPal account.
flow_config:

Parameters for flow configuration

  1. landing_page_type : Type of PayPal page to be displayed when a user lands on the PayPal site for checkout.

    • Billing – The Non-PayPal account landing page is used
    • Login – The paypal account login page is used.

Creating a Payment:

payment = Payment({
    "intent": "sale",
    "experience_profile_id": web_profile.id,
    "payer": {
        "payment_method": "paypal",
        "status": "VERIFIED" },
    "redirect_urls": {
        "return_url": RETURN_URL,
        "cancel_url": CANCEL_URL },
    "transactions": [ {
    "amount": {
        "total": amount,
        "currency": "USD" },
        "description": 'description' } ] } )
payment.create(): # Returns True or False

 

intent:

Payment intent. Allowed values are:

  • “sale” – For immediate payment
  • “authorize” – To authorize a payment for capture later
  • “order” – To create an order
experience_profile_id:

Id that will be obtained from the response of web profile request

payer:

Source of the funds for this payment represented by a PayPal account or a credit card.

  • payment_method : Payment method used. Must be either credit_card or paypal.
  • funding_instruments : A list of funding instruments for the current payment
  • payer_info : Information related to the payer
  • status : Status of the payer’s PayPal account. VERIFIED or UNVERIFIED
transactions:

Transactional details including the amount and item details.

redirect_urls:

Set of redirect URLs you provide only for PayPal-based payments.

  • return_url : The payer is redirected to this URL after approving the payment.
  • cancel_url : The payer is redirected to this URL after canceling the payment.

Execute an approved PayPal payment:

Use this call to complete a PayPal payment that has been approved by the payer.

payment = Payment.find("PAY-23456676765ASCASFE45")
payment.execute({ "payer_id": "7D4FDSFWEF5" })

The payment_id and payer_id are passed in the return_url. Once the payment is executed,it returns an array of payment object.

In the response state of the payment is obtained as : created approved failed, canceled, expired or pending.

The transaction details in the response contains state of the sale which is obtained as: pending, completed, refunded or partially_refunded

If the payment state is approved and the sale state is completed, the payment is successfully executed.