I am trying to create a Django app that creates a new database for every user when he/she signs up. I am going with this approach due to some reason. I have tried many ways using management commands and even Celery. But I am still getting the same error.
2022-12-23 07:16:07.410 UTC [49] STATEMENT: CREATE DATABASE tenant_asdadsad
[2022-12-23 07:16:07,415: ERROR/ForkPoolWorker-4] Task user.utils.create_database[089b0bc0-0b5f-4199-8cf3-bc336acc7624] raised unexpected: ActiveSqlTransaction('CREATE DATABASE cannot run inside a transaction block\n')
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/celery/app/trace.py", line 451, in trace_task
R = retval = fun(*args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/celery/app/trace.py", line 734, in __protected_call__
return self.run(*args, **kwargs)
File "/app/user/utils.py", line 45, in create_database
cursor.execute(f'CREATE DATABASE tenant_{tenant_id}')
psycopg2.errors.ActiveSqlTransaction: CREATE DATABASE cannot run inside a transaction block
This is my task
@shared_task
def create_database(tenant_id):
conn = psycopg2.connect(database="mydb", user="dbuser", password="mypass", host="db")
cursor = conn.cursor()
transaction.set_autocommit(True)
cursor.execute(f'CREATE DATABASE tenant_{tenant_id}')
cursor.execute(f'GRANT ALL PRIVILEGES ON DATABASE tenant_{tenant_id} TO dbuser')
cursor.close()
conn.close()
I have tried several ways but I always get the same error
This is my API call
def create(self, request, *args, **kwargs):
serializer_class = mySerializer(data=request.data)
if serializer_class.is_valid():
validated_data = serializer_class.validated_data
or = validated_data["org"]
or = Org.objects.create(**org)
create_database.delay(str(or.id))
return Response(create_user(validated_data))
Use the autocommit
property of the connection:
from psycopg2 import sql
def create_database(tenant_id):
conn = psycopg2.connect(database="mydb", user="dbuser", password="mypass", host="db")
cursor = conn.cursor()
conn.autocommit = True #!
# transaction.set_autocommit(True) #?
dbname = sql.Identifier(f'tenant_{tenant_id}')
create_cmd = sql.SQL('CREATE DATABASE {}').format(dbname)
grant_cmd = sql.SQL('GRANT ALL PRIVILEGES ON DATABASE {} TO dbuser').format(dbname)
cursor.execute(create_cmd)
cursor.execute(grant_cmd)
cursor.close()
conn.close()
Read in the docs about connection.autocommit.
Note also the use of the SQL string composition to avoid SQL injection.