In order to use an existing database in Django, you need to have a model for each table. Creating models for existing tables manually is just too much work. However, there's no need to do that, since Django has a builtin tool to solve this exact problem.

Note: this guide assumes you start with a fresh Django project. If you already have a Django project with its own database, you might want to configure database routing for each database as described in this guide.

Configure Django to use the database

Basically, edit settings.py in your project and set the DATABASES option to point to your existing database. Here's a sample config from Django docs:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydatabase',
        'USER': 'mydatabaseuser',
        'PASSWORD': 'mypassword',
        'HOST': '127.0.0.1',
        'PORT': '5432',
    }
}

You can test the connectivity by launching a database shell with this command:

python manage.py dbshell

If you end up in the database shell that means that the configuration is correct.

How to generate models for existing tables?

There's a django-admin command that inspects your database and outputs Python code for each model. Since it outputs the code to the standard output, you might want to save the results in a models.py file:

python manage.py inspectdb > models.py

The output of this command might be not 100% accurate, so you need to have a look at the generated models and tweak them, so that the models represent the tables correctly. Here're the instructions that come with the models code:

# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
#   * Rearrange models' order
#   * Make sure each model has one field with primary_key=True
#   * Make sure each ForeignKey has `on_delete` set to the desired behavior.
#   * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.

Letting Django handle the migrations

While you can access the data in the database via models, Django still won't apply the migrations for existing tables. This means that if you want to edit the schema of a table, you'd need to tweak it manually via SQL. If you'd like Django to manage your tables schema automatically, you need to remove the managed attribute from each generated model.

class Example(models.Model):
  ...
    
  class Meta:
    managed = False # remove this line
    db_table = 'example_table_name'

How to create initial migrations for existing tables?

Let's generate the initial migrations for the existing tables and for the tables required by Django to function properly:

python manage.py makemigrations

Usually, you'd just run the migrate command to apply the migrations, however in our case it would result in an error since some of the tables already exist. Luckily, there's a --fake-initial option that applies the migrations where it's possible and skips the migrations where the tables are already there:

python manage.py migrate --fake-initial

At this point, any new changes to the model structure and subsequent migrations would work as if Django managed the database since its inception.  Your job now is to use the app and see whether there are any bugs with the generated models left. Tune the model fields definitions until all bugs are fixed.