tags:
- postgresql
- langchain/langmem
- ai
date created: Wednesday, April 2nd 2025, 1:40:09 pm
date modified: Tuesday, May 6th 2025, 6:52:57 pm
title: How to create a Django app for LangMem
permalink: how-to-create-django-langmem
Create an admin
user and database named langmem
. (See PostgreSQL.)
Log into the langmem
database with the admin
user.
psql -U admin -d langmem
Create a migrations database user. This user will CRUD tables for database migrations.
-- Create the user with a password
CREATE USER langmem_migrations WITH ENCRYPTED PASSWORD 'secure_password';
-- Grant database connection and schema access
GRANT CONNECT ON DATABASE langmem TO langmem_migrations;
GRANT USAGE, CREATE ON SCHEMA public TO langmem_migrations;
-- Grant CRUD on existing tables (optional, for data manipulation during migrations)
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO langmem_migrations;
-- Automatically grant CRUD on future tables
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO langmem_migrations;
Create a service account database user. This user will access the database as the application runs, updating records.
-- Create the user with a password
CREATE USER langmem_app WITH ENCRYPTED PASSWORD 'secure_password';
-- Grant database connection and schema access
GRANT CONNECT ON DATABASE langmem TO langmem_app;
GRANT USAGE ON SCHEMA public TO langmem_app;
-- Grant CRUD on existing tables
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO langmem_app;
-- Automatically grant CRUD on future tables
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO langmem_app;
Add LangMem and PostgreSQL dependencies.
poetry add pgconnstr langmem langgraph-checkpoint-postgres
Create the migration script.
from decouple import config
from langgraph.store.postgres import PostgresStore
from pgconnstr import ConnectionString
DB_HOST = config("DB_HOST", default="localhost")
DB_NAME = config("DB_NAME")
DB_PORT = config("DB_PORT", default=5432, cast=int)
DB_USER = config("DB_USER")
DB_PASSWORD = config("DB_PASSWORD")
CONN = ConnectionString(
host=DB_HOST,
dbname=DB_NAME,
port=DB_PORT,
user=DB_USER,
password=DB_PASSWORD,
)
def migrate_db():
"""Run migrations to set up the memory table."""
with PostgresStore.from_conn_string(str(CONN)) as store:
store.setup()
def main():
migrate_db()
if __name__ == "__main__":
main()
After running, LangMem will create these tables in the database:
store
store_migrations
Navigate to the current package (within a mono-repo).
cd packages/sai_langmem
Add Django.
poetry add django
Create the web app.
poetry run django-admin startproject langmem_django src
Change the database connection settings in settings.py
.
code packages/sai_langmem/src/langmem_django/settings.py
DB_HOST = config("HOST", default="localhost")
DB_NAME = config("DB_NAME")
DB_PORT = config("DB_PORT", default=5432, cast=int)
DB_USER = config("DB_USER")
DB_PASSWORD = config("DB_PASSWORD")
POSTGRES = {
"ENGINE": "django.db.backends.postgresql",
"NAME": DB_NAME,
"DB_USER": DB_USER,
"DB_PASSWORD": DB_PASSWORD,
"HOST": DB_HOST,
"DB_PORT": DB_PORT,
}
DATABASES = {
"default": POSTGRES,
"sqlite": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
},
}
Migrate the database. Django will create many tables, including auth_group
, auth_user
, and others.
poetry run src/manage.py migrate
Run the web app server.
poetry run src/manage.py runserver
Enable the Poetry shell to type commands without poetry run
...
poetry shell
Navigate the web app source directory.
cd src
Create an admin user.
python manage.py createsuperuser
A Django webapp is the whole project. Django apps are installable components of a web app.
python manage.py startapp langmem
Replace the singular models.py
file with models/__init__.py
. Put your models here.
store.py
from django.db import models
from django.contrib import admin
class Store(models.Model):
prefix = models.TextField()
key = models.TextField()
value = models.JSONField()
created_at = models.DateTimeField()
updated_at = models.DateTimeField()
expires_at = models.DateTimeField(null=True, blank=True)
ttl_minutes = models.IntegerField(null=True, blank=True)
class Meta:
# Map to the existing 'store' table
db_table = 'store'
# Disable migrations for this model
managed = False
def __str__(self):
return f"{self.prefix} - {self.key}"
# Register the model in the Django admin app
@admin.register(Store)
class StoreAdmin(admin.ModelAdmin):
list_display = ('prefix', 'key', 'created_at', 'updated_at', 'expires_at', 'ttl_minutes')
search_fields = ('prefix', 'key')
store_migrations.py
from django.db import models
class StoreMigration(models.Model):
v = models.IntegerField(primary_key=True)
class Meta:
db_table = "store_migrations"
managed = False
verbose_name = "store migration"
verbose_name_plural = "store migrations"
def __str__(self):
return f"Migration version {self.v}"
Update the admin.py file.
admin.py
from django.contrib import admin
from .models import Store, StoreMigration
@admin.register(Store)
class StoreAdmin(admin.ModelAdmin):
list_display = (
"prefix",
"key",
"created_at",
"updated_at",
"expires_at",
"ttl_minutes",
)
search_fields = ("prefix", "key")
# Make all fields read-only. Only LangMem will update the database.
def has_add_permission(self, request):
return False
def has_change_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
@admin.register(StoreMigration)
class StoreMigrationAdmin(admin.ModelAdmin):
list_display = ("v",)
# Make all fields read-only
def has_add_permission(self, request):
return False
def has_change_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
Even though the models are not managed by Django (managed = False
), Django migrations still sees them.
Make the database migrations.
python manage.py makemigrations
Migrate the tables.
python manage.py migrate