惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
爱范儿
爱范儿
罗磊的独立博客
V
V2EX
量子位
Last Week in AI
Last Week in AI
Hugging Face - Blog
Hugging Face - Blog
博客园 - 司徒正美
Jina AI
Jina AI
博客园 - 叶小钗
小众软件
小众软件
博客园 - 【当耐特】
Y
Y Combinator Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Tailwind CSS Blog
博客园 - 聂微东
Microsoft Security Blog
Microsoft Security Blog
美团技术团队
P
Proofpoint News Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
有赞技术团队
有赞技术团队
MongoDB | Blog
MongoDB | Blog
Recent Announcements
Recent Announcements
酷 壳 – CoolShell
酷 壳 – CoolShell

DigitalOcean Community Questions

Droplet stuck: "Power on" and "Snapshot" both in Executing state for hours - what are my options? | DigitalOcean Referral Link Not Working anymore | DigitalOcean Recent console change has destroyed capacity to connect to legacy FreeBSD installs | DigitalOcean issue after moving app from aws to digitalocean | DigitalOcean issue after moving app from aws to digitalocean | DigitalOcean Container registry auomated garbage cillection sign in | DigitalOcean Billings And Free Tier Offer | DigitalOcean Switch hostinger to digitalocean? | DigitalOcean Unable to connect to Droplet via Console – “Failed to get droplet info” error | DigitalOcean billing problem | DigitalOcean How do I migrate an old Xen DomU VM, backed by a DRBD volume, to Digital Ocean? | DigitalOcean How to fix website not loading issue on VPS server (Nginx + WordPress) | DigitalOcean Account Locked After Payment Method Attempt (Hatch Program) - No Clear Reason | DigitalOcean Ghost Blog Marketplace Droplet is Ubuntu 22.04, not Ubuntu 24.04 | DigitalOcean How to setup browser with openclaw | DigitalOcean unable to load CA private key | DigitalOcean unable to load CA private key.. | DigitalOcean Add additional billing contact | DigitalOcean Digital Ocean Cloud Firewall? | DigitalOcean Mongodb auditing and compliance | DigitalOcean How much you're spending on AI tools? | DigitalOcean I can't create a managed MySQL cluster | DigitalOcean Please unblock SMTP (ports 25/465/587) for my droplet | DigitalOcean Reported promotional profile still accessible on DigitalOcean | DigitalOcean Change DNS server on Ubuntu 24 | DigitalOcean cannot add promotion of github student pack | DigitalOcean Do I need to use a Load Balancer on DigitalOcean for HTTPS or can I handle it on the Droplet? | DigitalOcean Best way to deploy a small Docker app on DigitalOcean without overengineering? | DigitalOcean Why is my DigitalOcean Droplet bandwidth usage so high all of a sudden? | DigitalOcean smtpout.secureserver.net 587 is blocked | DigitalOcean
How to Deploy a Django + Next.js Application on DigitalOc...
By KFSysSystem Administrator · 2026-03-15 · via DigitalOcean Community Questions

Step 1 — Setting Up the Django Backend

First, create a project directory and set up the Django backend.

Create the project structure:

mkdir django-nextjs-app && cd django-nextjs-app
mkdir backend && cd backend

Create and activate a Python virtual environment:

python3 -m venv venv
source venv/bin/activate

Install Django and the required packages:

pip install django djangorestframework django-cors-headers gunicorn psycopg2-binary dj-database-url python-decouple whitenoise

Create a new Django project and app:

django-admin startproject config .
python manage.py startapp events

Your backend/ directory should now look like this:

backend/
├── config/
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── events/
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── views.py
│   └── ...
├── manage.py
└── venv/

Step 2 — Configuring Django for Production

Open backend/config/settings.py and update it to support environment variables and production deployment:

# backend/config/settings.py

import os
from pathlib import Path
from decouple import config, Csv
import dj_database_url

BASE_DIR = Path(__file__).resolve().parent.parent

SECRET_KEY = config('DJANGO_SECRET_KEY', default='change-me-in-production')

DEBUG = config('DJANGO_DEBUG', default=False, cast=bool)

ALLOWED_HOSTS = config('DJANGO_ALLOWED_HOSTS', default='localhost,127.0.0.1', cast=Csv())

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Third party
    'rest_framework',
    'corsheaders',
    # Local
    'events',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'config.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'config.wsgi.application'

# Database
DATABASES = {
    'default': dj_database_url.config(
        default=config('DATABASE_URL', default=f'sqlite:///{BASE_DIR / "db.sqlite3"}'),
        conn_max_age=600,
    )
}

# Static files
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STORAGES = {
    "staticfiles": {
        "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
    },
}

# CORS
CORS_ALLOWED_ORIGINS = config(
    'CORS_ALLOWED_ORIGINS',
    default='http://localhost:3000',
    cast=Csv()
)

# REST Framework
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',
    ],
}

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

This configuration uses python-decouple to read environment variables, dj-database-url to parse the database connection string, and whitenoise to serve static files efficiently in production.

Step 3 — Creating the Events API

Define the Event model in backend/events/models.py:

# backend/events/models.py

from django.db import models


class Event(models.Model):
    title = models.CharField(max_length=200)
    description = models.TextField()
    date = models.DateTimeField()
    location = models.CharField(max_length=200)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['date']

    def __str__(self):
        return self.title

Create a serializer in backend/events/serializers.py:

# backend/events/serializers.py

from rest_framework import serializers
from .models import Event


class EventSerializer(serializers.ModelSerializer):
    class Meta:
        model = Event
        fields = ['id', 'title', 'description', 'date', 'location', 'created_at']

Create the API view in backend/events/views.py:

# backend/events/views.py

from rest_framework import generics
from .models import Event
from .serializers import EventSerializer


class EventListView(generics.ListAPIView):
    queryset = Event.objects.all()
    serializer_class = EventSerializer

Wire up the URLs in backend/events/urls.py:

# backend/events/urls.py

from django.urls import path
from .views import EventListView

urlpatterns = [
    path('events/', EventListView.as_view(), name='event-list'),
]

Include the events URLs in backend/config/urls.py:

# backend/config/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('events.urls')),
]

Run the migrations and create some sample data:

python manage.py makemigrations events
python manage.py migrate
python manage.py createsuperuser

Start the development server and add a few events through the admin at http://localhost:8000/admin/:

python manage.py runserver

Verify the API works by visiting http://localhost:8000/api/events/ in your browser.

Step 4 — Preparing the Django Backend for Deployment

Create a backend/requirements.txt file:

pip freeze > requirements.txt

Create a backend/runtime.txt to specify the Python version:

python-3.11.7

Create a backend/.env.example for reference (do not commit your actual .env file):

DJANGO_SECRET_KEY=your-secret-key
DJANGO_DEBUG=False
DJANGO_ALLOWED_HOSTS=.ondigitalocean.app
DATABASE_URL=postgresql://user:password@host:port/dbname
CORS_ALLOWED_ORIGINS=https://your-frontend.ondigitalocean.app

Step 5 — Building the Next.js Frontend

Navigate back to the root directory and create the Next.js app:

cd ..
npx create-next-app@latest frontend --typescript --tailwind --app --eslint --src-dir --no-import-alias
cd frontend

Create an API utility to fetch data from the Django backend. Create frontend/src/lib/api.ts:

// frontend/src/lib/api.ts

const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';

export interface Event {
  id: number;
  title: string;
  description: string;
  date: string;
  location: string;
  created_at: string;
}

export async function getEvents(): Promise<Event[]> {
  const res = await fetch(`${API_BASE_URL}/api/events/`, {
    next: { revalidate: 60 },
  });

  if (!res.ok) {
    throw new Error('Failed to fetch events');
  }

  return res.json();
}

Replace the contents of frontend/src/app/page.tsx with the events listing page:

// frontend/src/app/page.tsx

import { getEvents } from '@/lib/api';

export default async function Home() {
  const events = await getEvents();

  return (
    <main className="min-h-screen bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
      <div className="max-w-4xl mx-auto">
        <h1 className="text-4xl font-bold text-gray-900 mb-2">
          Upcoming Events
        </h1>
        <p className="text-gray-600 mb-8">
          Browse the latest events happening near you.
        </p>

        {events.length === 0 ? (
          <p className="text-gray-500 text-center py-12">
            No events found. Check back later!
          </p>
        ) : (
          <div className="grid gap-6">
            {events.map((event) => (
              <article
                key={event.id}
                className="bg-white rounded-lg shadow-sm border border-gray-200 p-6 hover:shadow-md transition-shadow"
              >
                <div className="flex justify-between items-start">
                  <div>
                    <h2 className="text-xl font-semibold text-gray-900">
                      {event.title}
                    </h2>
                    <p className="text-gray-600 mt-2">{event.description}</p>
                  </div>
                </div>
                <div className="mt-4 flex flex-wrap gap-4 text-sm text-gray-500">
                  <span className="flex items-center gap-1">
                    📅{' '}
                    {new Date(event.date).toLocaleDateString('en-US', {
                      weekday: 'long',
                      year: 'numeric',
                      month: 'long',
                      day: 'numeric',
                    })}
                  </span>
                  <span className="flex items-center gap-1">
                    📍 {event.location}
                  </span>
                </div>
              </article>
            ))}
          </div>
        )}
      </div>
    </main>
  );
}

Update frontend/src/app/layout.tsx:

// frontend/src/app/layout.tsx

import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';

const inter = Inter({ subsets: ['latin'] });

export const metadata: Metadata = {
  title: 'Event Listings — Django + Next.js on DigitalOcean',
  description: 'A demo event listing app built with Django and Next.js, deployed on DigitalOcean App Platform.',
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={inter.className}>{children}</body>
    </html>
  );
}

Test the frontend locally (make sure your Django server is running on port 8000):

npm run dev

Visit http://localhost:3000 to confirm events render correctly.

Step 6 — Preparing the Next.js Frontend for Deployment

DigitalOcean App Platform needs Next.js to run in standalone mode to minimize the deployed bundle size. Update frontend/next.config.ts:

// frontend/next.config.ts

import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  output: 'standalone',
};

export default nextConfig;

Step 7 — Setting Up the GitHub Repository

Initialize a Git repository from the project root:

cd ..  # back to django-nextjs-app/
git init

Create a .gitignore in the root:

# Python
backend/venv/
backend/__pycache__/
backend/**/__pycache__/
backend/db.sqlite3
backend/.env
*.pyc

# Node
frontend/node_modules/
frontend/.next/
frontend/.env.local

# OS
.DS_Store

Commit and push:

git add .
git commit -m "Initial commit: Django + Next.js event app"
git remote add origin https://github.com/your-username/django-nextjs-app.git
git branch -M main
git push -u origin main

Replace your-username with your actual GitHub username.

Step 8 — Creating a Managed PostgreSQL Database on DigitalOcean

Before deploying the app, set up a managed database:

  1. Log in to your DigitalOcean Cloud Panel
  2. Click Databases in the left sidebar
  3. Click Create Database Cluster
  4. Choose PostgreSQL (version 16)
  5. Select a datacenter region close to your users
  6. Choose the Basic Plan ($15/month — suitable for development and small production workloads)
  7. Name it events-db
  8. Click Create Database Cluster

Once provisioning completes (typically 3–5 minutes):

  1. Click into your database cluster
  2. Under Connection Details, note the values for host, port, username, password, and database
  3. Copy the full Connection String — you’ll need it shortly

Step 9 — Deploying to DigitalOcean App Platform

Now deploy both the Django backend and Next.js frontend as components of a single App Platform application.

Create the App

  1. Navigate to App Platform in the DigitalOcean dashboard
  2. Click Create App
  3. Select GitHub as the source and authorize DigitalOcean to access your repository
  4. Select the django-nextjs-app repository and the main branch

Configure the Backend Component

  1. Click Edit on the detected component, or Add ResourceService

  2. Set the following:

    • Name: backend
    • Source Directory: /backend
    • Build Command: pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate
    • Run Command: gunicorn config.wsgi --bind 0.0.0.0:8080
    • HTTP Port: 8080
    • Instance Size: Basic ($5/month)
  3. Under Environment Variables, add:

Variable Value
DJANGO_SECRET_KEY (click “Generate” or paste a strong random string)
DJANGO_DEBUG False
DJANGO_ALLOWED_HOSTS .ondigitalocean.app
DATABASE_URL (paste the connection string from Step 8)
CORS_ALLOWED_ORIGINS (leave blank for now — you’ll update this after the frontend deploys)

Tip: Mark DJANGO_SECRET_KEY and DATABASE_URL as Encrypted to keep them secure.

Configure the Frontend Component

  1. Click Add ResourceService

  2. Set the following:

    • Name: frontend
    • Source Directory: /frontend
    • Build Command: npm ci && npm run build
    • Run Command: node .next/standalone/server.js
    • HTTP Port: 3000
    • Instance Size: Basic ($5/month)
  3. Under Environment Variables, add:

Variable Value
NEXT_PUBLIC_API_URL https://backend-xxxxx.ondigitalocean.app (use the backend’s URL from App Platform)
PORT 3000

Attach the Database

  1. Click Add ResourceDatabase
  2. Select Previously Created Database and choose the events-db cluster from Step 8
  3. App Platform will automatically inject the DATABASE_URL environment variable

Deploy

Click Create Resources. DigitalOcean will now:

  • Pull your code from GitHub
  • Build both components
  • Run database migrations
  • Start serving your application

The first deployment takes approximately 5–10 minutes.

Step 10 — Updating CORS After Deployment

Once both components are live, you need to update the backend’s CORS configuration with the frontend’s actual URL.

  1. Go to your app in the App Platform dashboard
  2. Click on the backend component → SettingsEnvironment Variables
  3. Update CORS_ALLOWED_ORIGINS to your frontend URL, for example:
    https://frontend-xxxxx.ondigitalocean.app
    
  4. Click Save — this triggers a redeployment of the backend

After the redeployment finishes, visit your frontend URL. You should see your events listed, rendered server-side by Next.js and served from your Django API.

Step 11 — Setting Up Automatic Deployments

DigitalOcean App Platform supports automatic deployments out of the box. Every time you push to the main branch, both components rebuild and redeploy automatically.

To verify this:

# Make a small change, then push
git add .
git commit -m "Update event listing styles"
git push origin main

Within minutes, your live application reflects the changes — no manual intervention required.

Step 12 — Adding a Custom Domain (Optional)

To serve your app on a custom domain:

  1. In App Platform, go to SettingsDomains
  2. Click Add Domain
  3. Enter your domain name
  4. Add the provided CNAME record in your domain’s DNS settings
  5. DigitalOcean automatically provisions a free Let’s Encrypt SSL certificate

Remember to update DJANGO_ALLOWED_HOSTS and CORS_ALLOWED_ORIGINS with your custom domain as well.

This approach of pairing Django with Next.js on a managed platform is well-suited for content-driven and event-driven applications — it’s a similar architecture behind production sites like extratime.world, which serves sports event data to users worldwide.

Conclusion

In this tutorial, you built a full-stack application with a Django REST Framework backend and a Next.js frontend, then deployed both to DigitalOcean App Platform with a managed PostgreSQL database.

Your deployed application now has:

  • Server-side rendering via Next.js for fast initial page loads and SEO
  • Automatic HTTPS via DigitalOcean’s managed SSL
  • CI/CD with automatic deployments on every Git push
  • Managed database with automated backups and failover
  • Independent scaling — backend and frontend can be scaled separately

From here, you can extend the application by adding user authentication with Django’s auth system and Next.js middleware, implementing real-time updates with WebSockets via Django Channels, or adding an admin interface to manage events through the Django admin panel.