Skip to content

Instantly share code, notes, and snippets.

@afmicc
Last active September 21, 2020 16:28
Show Gist options
  • Save afmicc/0450ee1f3b9c9fa915847126a76869c9 to your computer and use it in GitHub Desktop.
Save afmicc/0450ee1f3b9c9fa915847126a76869c9 to your computer and use it in GitHub Desktop.

How starting a new django project with Cookiecutter

Create enviroment

$ pyenv virtualenv cookiecutter-starter

Activate enviroment

$ pyenv activate cookiecutter-starter

Install Cookiecutter

$ pip install cookiecutter

Create project

$ cookiecutter gh:pydanny/cookiecutter-django

Fill generator options

NOTE:

  • Each option suggests a default value if you agree with it, just press enter.
  • Full list of options here.

Some interesting options:

  • project_name: Cookiecutter Starter
  • project_slug: cookiecutter_starter
  • description: This proyect is an example of how configure a Django proyect from the beginning
  • author_name: @afmicc
  • domain_name: example.com
  • email: afmicc@example.com
  • windows: n
    • will you develop at windows enviroment?
  • use_pycharm: n
    • pycharm is the Jetbrains' Python IDE
  • cloud_provider (we usually use AWS): 1
  • mail_service: 6
    • we usually use Sendgrid
  • use_drf: y
  • use_mailhog: y
    • to view emails locally
  • use_heroku: y
    • we usually deploy to Heroku our solutions
  • keep_local_envs_in_vcs: n
    • to add to the version control the .env files to be shared along with the team
    • usefull to set the heroku or docker variables
Full bash output:
project_name [My Awesome Project]: Cookiecutter Starter
project_slug [cookiecutter_starter]:
description [Behold My Awesome Project!]: This proyect is an example of how configure a Django proyect since beginning
author_name [Daniel Roy Greenfeld]: @afmicc
domain_name [example.com]: rootstrap.com
email [@afmicc@example.com]: dev@rootstrap.com
version [0.1.0]:
Select open_source_license:
1 - MIT
2 - BSD
3 - GPLv3
4 - Apache Software License 2.0
5 - Not open source
Choose from 1, 2, 3, 4, 5 [1]: 1
timezone [UTC]:
windows [n]:
use_pycharm [n]:
use_docker [n]:
Select postgresql_version:
1 - 12.3
2 - 11.8
3 - 10.8
4 - 9.6
5 - 9.5
Choose from 1, 2, 3, 4, 5 [1]: 1
Select js_task_runner:
1 - None
2 - Gulp
Choose from 1, 2 [1]:
Select cloud_provider:
1 - AWS
2 - GCP
3 - None
Choose from 1, 2, 3 [1]: 1
Select mail_service:
1 - Mailgun
2 - Amazon SES
3 - Mailjet
4 - Mandrill
5 - Postmark
6 - Sendgrid
7 - SendinBlue
8 - SparkPost
9 - Other SMTP
Choose from 1, 2, 3, 4, 5, 6, 7, 8, 9 [1]: 6
use_async [n]:
use_drf [n]: y
custom_bootstrap_compilation [n]: n
use_compressor [n]:
use_celery [n]:
use_mailhog [n]: y
use_sentry [n]:
use_whitenoise [n]:
use_heroku [n]: y
Select ci_tool:
1 - None
2 - Travis
3 - Gitlab
Choose from 1, 2, 3 [1]:
keep_local_envs_in_vcs [y]: n
debug [n]:

Install dependencies

$ pip install -r requirements/local.txt

Create psql database

$ createdb cookiecutter_starter-dev

Load .env file for local configurations

  1. At config/settings/base.py set DJANGO_READ_DOT_ENV_FILE to load the configurations from .env file.
#  config/settings/base.py

#  Before
READ_DOT_ENV_FILE = env.bool("DJANGO_READ_DOT_ENV_FILE", default=False)

#  After
READ_DOT_ENV_FILE = env.bool("DJANGO_READ_DOT_ENV_FILE", default=True)
  1. Create .env file at root folder and .env.example to track the local configuration needed.
#  .env.example

DJANGO_DEBUG=on
DATABASE_URL=postgres://<user>:<password>@localhost:5432/cookiecutter_starter-dev

Migrate

$ python manage.py migrate

Run test

$ pytest

Run test and get coverage percentage

$ coverage run -m pytest && coverage report -m

NOTE:

  • The command must run without exceptions because it mandatory for future configurations.
  • Don't matter if you write tests following the unittest or pytest approach since $ pytest command will be able to run both of them.

Install Mailhog

$ brew install mailhog

NOTE: If you prefer other options to install Mailhog, you can take a look at the Mailhog installation guideline.

Start the server running $ mailhog

Finally, if you go to http://localhost:8025/, your mail server is running.

Code Style

NOTE: By default this package is already installed.

  1. $ pip install flake8 flake8-isort
  2. Add dependencies to requirement files. Below the Code quality separator:
flake8==<version>
flake8-isort==<version>
  1. Add custom configuration at setup.cfg file according to the Rootstrap's standards.
    • extend-ignore: exclude flake8 warnings to avoid black conflicts. See more.
#  setup.cfg

[flake8]
max-line-length = 120
exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules,venv/*
extend-ignore = E203, W503
  1. Check running $ flake8.
    • This command list all the issues found.
    • A useful command could be $ isort . which resolves all the issues related to import styles. Also, you can add the flag --interactive at the end of the command to resolve or ignore one issue by one.

NOTE: By default this package is already installed.

  1. $ pip install black
  2. Add dependencies to requirement files. Below the Code quality separator:
black==<version>
  1. Add custom configuration at pyproject.toml file according to the Rootstrap's standards.
    • black does not support configurations at setup.cfg file and there isn't a plan to do it in the future. See more.
#  pyproject.toml

[tool.black]
skip_string_normalization = true
line-length = 120
exclude = '.*\/(migrations|settings)\/.*'
  1. Check running $ black . --check.
    • This command list all the issues found.
    • A useful command could be $ black . which resolves all the issues. Also, you can run $ black . --diff to watch the proposed changes.

Single-quotes

Here at Rootstrap we prefer to use single-quoted strings over double quotes. For docstrings we use double quotes since the chance of writing a ' is higher in the documentation string of a class or a method.

Rootstrap Guides/Python/String Quotes

To convert the existing double quotes to single ones, follow these steps:

  1. In your IDE, search by the regex /(?<!"")(?<=(?!""").)"(?!"")/.
  2. Replace the occurrences with the single quote '.
  3. Include only the python files: *.py.
  4. Exclude migrations files: *migrations*.
  5. Check that everything is well replaced.

The VS Code configuration:

  • Search: (?<!"")(?<=(?!""").)"(?!"")
  • Replace: '
  • files to include: *.py
  • files to exclude: *migrations*

CI

Code Climate

  1. Set up the project at CodeClimate. guide
  2. Find your Test Coverage ID at CodeClimate to complete the next configuration. guide
  3. Follow one of the following configurations Circle CI or GitHub Action.

Circle CI

  1. Set up the project at CircleCI. guide
  2. Create the folder /.circleci and inner it the file config.yml with the following content.
.circleci/config.yml
#  .circleci/config.yml

version: 2.1

jobs:
  build:
    docker:
      - image: circleci/python:latest
        environment:
          DATABASE_URL: postgresql://postgres:@localhost:5432/circle_test?sslmode=disable

      - image: circleci/postgres:alpine-postgis-ram

    steps:
      - checkout
      - run:
          name: Updating pip
          command: |
            python3 -m venv venv
            . venv/bin/activate
            pip install --upgrade pip==20.0.2

      - restore_cache:
          keys:
            - venv-requirements-{{ checksum "./requirements/base.txt" }}-{{ checksum "./requirements/local.txt" }}
            - venv-requirements-

      - run:
          name: Installing pip requirements
          command: |
            python3 -m venv venv
            . venv/bin/activate
            pip install -r ./requirements/local.txt

      - run:
          name: Checking PEP8 code style
          command: |
            . venv/bin/activate
            flake8 --count

      - run:
          name: Checking Black code formatter
          command: |
            . venv/bin/activate
            black . --check

      - run:
          name: Installing GDAL
          command: |
            sudo apt-get install libgdal-dev

      - run:
          name: Running tests
          command: |
            . venv/bin/activate
            coverage run -m pytest --ds=config.settings.test

      - save_cache:
          key: venv-requirements-{{ checksum "./requirements/base.txt" }}-{{ checksum "./requirements/local.txt" }}
          paths:
            - ".venv"

      - run:
          name: Checking coverage
          command: |
            . venv/bin/activate
            coverage report --fail-under=90 -m
            coverage xml

      - run:
          name: Setup Code Climate test-reporter
          command: |
            curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
            chmod +x ./cc-test-reporter
            ./cc-test-reporter before-build
            ./cc-test-reporter after-build --coverage-input-type coverage.py --exit-code $?
  1. Create a enviroment variable to know where send the CodeClimate report.guide
    • Name: CC_TEST_REPORTER_ID
    • Value: Test Coverage ID
  2. Add badge to readme file. guide
# Template:
.. image:: https://circleci.com/<VCS>/<ORG_NAME>/<PROJECT_NAME>.svg?style=svg
    :target: https://circleci.com/<VCS>/<ORG_NAME>/<PROJECT_NAME>

# Example:
.. image:: https://circleci.com/gh/afmicc/cookiecutter_starter.svg?style=shield
    :target: https://circleci.com/gh/afmicc/cookiecutter_starter
    :alt: Tests status

Extra: If you want, you can run CircleCI locally.

  1. Install the CircleCI CLI
  2. Run the command to generate the process.yml file: $ circleci config process .circleci/config.yml > process.yml.
  3. Finally execute it: $ circleci local execute -c process.yml --job build.
  4. (Optional) Add process.yml to .gitignore.
# .gitignore

### Continuous Integration

process.yml

GitHub Workflow

  1. Create the path /.github/workflows and inner it the file python-app.yml with the following content.
.github/workflows/python-app.yml
#  .github/workflows/python-app.yml

name: Python application

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build:
    runs-on: ubuntu-latest
    services:
      db:
        image: postgres:11.6-alpine
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_USER: postgres
        ports:
          - "5432:5432"
    env:
      DATABASE_URL: postgresql://postgres:postgres@localhost:5432/postgres
      CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}

    steps:
    - uses: actions/checkout@v2

    - name: Set up Python 3.8
      uses: actions/setup-python@v2
      with:
        python-version: 3.8

    - name: Updating pip
      run: |
        python3 -m venv venv
        . venv/bin/activate
        pip install --upgrade pip==20.0.2

    - name: Cache pipenv
      uses: actions/cache@v2
      id: cache-venv
      with:
        path: ./.venv
        key: ${{ runner.os }}-venv-${{ hashFiles('./requirements/base.txt','./requirements/local.txt') }}
        restore-keys: |
          ${{ runner.os }}-venv-

    - name: Installing requirements
      run: |
        python3 -m venv venv
        . venv/bin/activate
        pip install -r ./requirements/local.txt

    - name: Checking PEP8 code style
      run: |
        . venv/bin/activate
        flake8 --count

    - name: Checking Black code formatter
      run: |
        . venv/bin/activate
        black . --check

    - name: Running tests
      run: |
        . venv/bin/activate
        coverage run -m pytest --ds=config.settings.test

    - name: Checking coverage
      run: |
        . venv/bin/activate
        coverage report --fail-under=90 -m
        coverage xml

    - name: Setup Code Climate test-reporter
      run: |
          curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
          chmod +x ./cc-test-reporter
          ./cc-test-reporter before-build
          ./cc-test-reporter after-build --coverage-input-type coverage.py --exit-code $?
  1. Create a project secreat to use it at the config file. guide.
    • Name: CC_TEST_REPORTER_ID
    • Value: Test Coverage ID
  2. Add badge to readme file. guide
    • The workflow name is the value of the first configuration name, in the example above is Python application.
# Template:
.. image:: https://github.com/<OWNER>/<REPOSITORY>/workflows/<WORKFLOW_NAME>/badge.svg

# Example:
.. image:: https://github.com/afmicc/cookiecutter_starter/workflows/Python%20application/badge.svg
    :alt: Tests status

Final

Congratulations! Your project is fully configured 💪

Here the example project: afmicc/cookiecutter_starter

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment