Django Build Automation
Tags: django, python, webdev, programming
Published: 2023-11-29 17:14:40

Hello! If you are a person like try to automate django build for production, here is a small script for you. Building Django for production is not a tedious task compared to other frameworks but, there are many elements that can go wrong because we need to configure each of them manually , which can be removed by automating the process. Though some other approaches like using environment variables , docker secrets , or even splitting settings.py into production.py and development.py exist , i felt this approach suits my current project and hope some other person would find this helpful.

This is my current Folder structure
```
project_name
├── docs
├── .git
├── .gitignore
└── source
    ├── django
        ├── conf
        │   ├── asgi.py
        │   ├── __init__.py
        │   ├── settings.py
        │   ├── urls.py
        │   └── wsgi.py
        ├── manage.py
        └── requirements.txt

```

1 . Create a file named build.py inside project_name (along with source dir, .git and docs ) and add the following content

```python
import shutil
import os


class Builder:
    def __init__(self,source_dir,build_dir):
        
        self.source_dir = os.path.abspath(source_dir) if not os.path.isabs(source_dir) else source_dir
        self.build_dir = os.path.abspath(build_dir) if not os.path.isabs(build_dir) else build_dir
        self.ignore_file = os.path.join(os.path.dirname(os.path.abspath(__file__)),'.buildignore')
        self.prebuild_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)),'pre_build')
        print(self.source_dir)
        print(self.build_dir)

    def get_ignore_patterns(self):
        self.ignore_patterns = []
        if os.path.exists(self.ignore_file):
            with open(self.ignore_file, 'r') as buildignore_file:
                self.ignore_patterns = buildignore_file.read().splitlines()      

        print(self.ignore_patterns) 

    def copy_to_build_dir(self):
        shutil.copytree(self.source_dir,self.build_dir,ignore=shutil.ignore_patterns(*self.ignore_patterns))    
    
    def replace_prebuild(self):
        shutil.copytree(self.prebuild_dir, self.build_dir, dirs_exist_ok=True)
    
    def build(self):
        self.get_ignore_patterns()
        self.copy_to_build_dir()
        self.replace_prebuild()


builder =Builder('./source','./build') # change the name of dir if needed
builder.build()
```

You can change the name of the folders as you need in this file.

2 . Create a file name .buildignore. This will act similar to .gitignore , ie you can can mention here the files and dirs you dont need in production. For now you can add the following content and edit as you need.
```
*.log
*.pot
*.pyc
__pycache__/
.env
db.sqlite3
```
3 . Create a folder named pre_build and inside this create the files that you need to replace in production. Note you need to follow the same folder structure for the file you need to replace. For instance if you need to replace setting.py for production you should create settings.py in the following path: project_name/pre_build/django/conf/settings.py . Also you can add additional files that will be copied to the respective path in the build dir. In short, the script will copy all the contents from pre_build and replace any content that already exist from the source. You can utilise this to include other files like docker-compose,.env files or even nginx configs etc . Also you can configure your static files (change STATIC_ROOT to a dir inside pre_build).

Now my dir would look like

```
project_name
├── docs
├── .git
├── .gitignore
├── .buildignore
├── build.py
├── pre_build
│   └── django
│       └── conf
│           └── settings.py
└── source
    └── django
        ├── conf
        │   ├── asgi.py
        │   ├── __init__.py
        │   ├── settings.py
        │   ├── urls.py
        │   └── wsgi.py
        ├── manage.py
        └── requirements.txt
```

4 . and when you want to build the production run the following
```bash
python build.py
```