django-supabase 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. django_supabase-1.0.0/PKG-INFO +184 -0
  2. django_supabase-1.0.0/README.md +164 -0
  3. django_supabase-1.0.0/pyproject.toml +26 -0
  4. django_supabase-1.0.0/src/django_supabase/__init__.py +4 -0
  5. django_supabase-1.0.0/src/django_supabase/apps.py +13 -0
  6. django_supabase-1.0.0/src/django_supabase/auth/__init__.py +1 -0
  7. django_supabase-1.0.0/src/django_supabase/auth/apps.py +8 -0
  8. django_supabase-1.0.0/src/django_supabase/auth/backends.py +121 -0
  9. django_supabase-1.0.0/src/django_supabase/auth/middleware.py +33 -0
  10. django_supabase-1.0.0/src/django_supabase/auth/migrations/0001_initial.py +63 -0
  11. django_supabase-1.0.0/src/django_supabase/auth/migrations/__init__.py +0 -0
  12. django_supabase-1.0.0/src/django_supabase/auth/models.py +36 -0
  13. django_supabase-1.0.0/src/django_supabase/auth/sync.py +228 -0
  14. django_supabase-1.0.0/src/django_supabase/auth/tokens.py +93 -0
  15. django_supabase-1.0.0/src/django_supabase/conf.py +70 -0
  16. django_supabase-1.0.0/src/django_supabase/db/__init__.py +0 -0
  17. django_supabase-1.0.0/src/django_supabase/db/client.py +79 -0
  18. django_supabase-1.0.0/src/django_supabase/db/engine.py +66 -0
  19. django_supabase-1.0.0/src/django_supabase/defaults.py +79 -0
  20. django_supabase-1.0.0/src/django_supabase/env.py +46 -0
  21. django_supabase-1.0.0/src/django_supabase/management/__init__.py +0 -0
  22. django_supabase-1.0.0/src/django_supabase/management/commands/__init__.py +0 -0
  23. django_supabase-1.0.0/src/django_supabase/management/commands/create_storage_buckets.py +30 -0
  24. django_supabase-1.0.0/src/django_supabase/management/commands/sync_users.py +72 -0
  25. django_supabase-1.0.0/src/django_supabase/storage/__init__.py +0 -0
  26. django_supabase-1.0.0/src/django_supabase/storage/backends.py +252 -0
  27. django_supabase-1.0.0/src/django_supabase/storage/utils.py +53 -0
  28. django_supabase-1.0.0/src/django_supabase/utils.py +40 -0
@@ -0,0 +1,184 @@
1
+ Metadata-Version: 2.3
2
+ Name: django-supabase
3
+ Version: 1.0.0
4
+ Summary: A lightweight django package designed to connect Django applications to Supabase Auth, Database Access, and Storage
5
+ Author: Lakan
6
+ Author-email: Lakan <leydotpy.dev@gmail.com>
7
+ Requires-Dist: celery>=5.6.3
8
+ Requires-Dist: django>=6.0.6
9
+ Requires-Dist: django-celery-beat>=2.9.0
10
+ Requires-Dist: django-celery-results>=2.6.0
11
+ Requires-Dist: django-guardian>=3.3.2
12
+ Requires-Dist: django-redis>=7.0.0
13
+ Requires-Dist: djangorestframework>=3.17.1
14
+ Requires-Dist: gunicorn>=26.0.0
15
+ Requires-Dist: pyjwt[crypto]>=2.10.0
16
+ Requires-Dist: redis>=8.0.0
17
+ Requires-Dist: supabase>=2.31.0
18
+ Requires-Python: >=3.14
19
+ Description-Content-Type: text/markdown
20
+
21
+ # Django Supabase
22
+
23
+ Professional, production-ready Supabase integration for Django.
24
+
25
+ ## Features
26
+
27
+ - 🔐 **Authentication**: Seamless Supabase Auth + Django User model sync
28
+ - 💾 **Database**: Direct PostgreSQL connection with Django ORM
29
+ - 📦 **Storage**: Django storage backends for Supabase Storage
30
+ - 🚀 **Production-Ready**: Connection pooling, caching, error handling
31
+ - 🔧 **Django-Native**: Uses Django's ecosystem and patterns
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install django-supabase
37
+ ```
38
+
39
+ ## Quick Setup
40
+ ### Add to INSTALLED_APPS:
41
+
42
+ ```python
43
+ INSTALLED_APPS = [
44
+ # ...
45
+ 'django_supabase',
46
+ 'django_supabase.auth',
47
+ ]
48
+ ```
49
+
50
+ ## Configure Settings
51
+
52
+ ```python
53
+ SUPABASE_URL = 'https://your-project.supabase.co'
54
+ SUPABASE_KEY = 'your-publishable-or-anon-key'
55
+ SUPABASE_SECRET_KEY = 'your-secret-or-service-role-key' # server-side only
56
+
57
+ # Recommended default: ask Supabase Auth to validate bearer tokens.
58
+ SUPABASE_JWT_VERIFICATION = 'auth_server'
59
+
60
+ # Optional for projects using asymmetric JWT signing keys:
61
+ # SUPABASE_JWT_VERIFICATION = 'jwks'
62
+ # SUPABASE_JWT_ALGORITHMS = ['RS256', 'ES256']
63
+
64
+ # Legacy/local-only fallback for HS256 shared-secret verification:
65
+ # SUPABASE_JWT_VERIFICATION = 'jwt_secret'
66
+ # SUPABASE_JWT_SECRET = 'your-jwt-secret'
67
+
68
+ AUTH_USER_MODEL = 'supabase_auth.SupabaseUser'
69
+
70
+ AUTHENTICATION_BACKENDS = [
71
+ 'django_supabase.auth.backends.SupabaseAuthBackend',
72
+ 'django.contrib.auth.backends.ModelBackend',
73
+ ]
74
+
75
+ DATABASES = {
76
+ 'default': {
77
+ 'ENGINE': 'django_supabase.db.engine.SupabasePostgreSQLEngine',
78
+ 'NAME': 'postgres',
79
+ 'USER': 'postgres',
80
+ 'PASSWORD': 'your-password',
81
+ 'HOST': 'db.your-project.supabase.co',
82
+ 'PORT': 5432,
83
+ }
84
+ }
85
+
86
+ STORAGES = {
87
+ 'default': {
88
+ 'BACKEND': 'django_supabase.storage.backends.SupabaseMediaStorage',
89
+ },
90
+ 'staticfiles': {
91
+ 'BACKEND': 'django_supabase.storage.backends.SupabaseStaticStorage',
92
+ },
93
+ }
94
+ ```
95
+
96
+ ## Run Migrations
97
+
98
+ ```bash
99
+ python manage.py migrate
100
+ python manage.py create_storage_buckets
101
+ ```
102
+
103
+ # Usage Example
104
+ ## Authentication
105
+
106
+ ```python
107
+ from django.contrib.auth import authenticate
108
+
109
+ # Email/Password login
110
+ user = authenticate(email='user@example.com', password='password')
111
+
112
+ # JWT Token authentication (in views)
113
+ # Token automatically extracted from Authorization: Bearer <token>
114
+ ```
115
+
116
+ ## Frontend OAuth
117
+
118
+ Use Supabase Auth in the frontend with a publishable key. After OAuth login,
119
+ send the Supabase session access token to Django:
120
+
121
+ ```javascript
122
+ const { data, error } = await supabase.auth.signInWithOAuth({
123
+ provider: 'google',
124
+ options: { redirectTo: 'https://your-frontend.example.com/auth/callback' },
125
+ })
126
+
127
+ const { data: sessionData } = await supabase.auth.getSession()
128
+
129
+ await fetch('/api/me/', {
130
+ headers: {
131
+ Authorization: `Bearer ${sessionData.session.access_token}`,
132
+ },
133
+ })
134
+ ```
135
+
136
+ Django validates the bearer token through `SupabaseAuthMiddleware` and syncs the
137
+ local user by Supabase `sub` / `user.id`, not by email alone.
138
+
139
+ ## File Upload
140
+
141
+ ```python
142
+ from django_supabase.storage.utils import upload_file_to_supabase
143
+
144
+ file = ...
145
+
146
+ url = upload_file_to_supabase(file, 'media', path='uploads/')
147
+
148
+ ```
149
+
150
+ ## Direct Supabase Queries
151
+
152
+ ```python
153
+ from django_supabase.db.client import get_supabase_client
154
+
155
+ supabase = get_supabase_client()
156
+ response = supabase.table('posts').select('*').execute()
157
+ ```
158
+
159
+ ### Test
160
+
161
+ ```python
162
+ # supabase_integration/tests.py
163
+ from django.test import TestCase
164
+ from django.contrib.auth import get_user_model
165
+ from django_supabase.db.client import get_supabase_client
166
+
167
+ User = get_user_model()
168
+
169
+ class SupabaseIntegrationTestCase(TestCase):
170
+ """Base test case with Supabase utilities"""
171
+
172
+ @classmethod
173
+ def setUpClass(cls):
174
+ super().setUpClass()
175
+ cls.supabase = get_supabase_client()
176
+
177
+ def create_test_user(self, email='test@example.com'):
178
+ """Helper to create test users"""
179
+ return User.objects.create_user(
180
+ username=email,
181
+ email=email,
182
+ password='testpass123'
183
+ )
184
+ ```
@@ -0,0 +1,164 @@
1
+ # Django Supabase
2
+
3
+ Professional, production-ready Supabase integration for Django.
4
+
5
+ ## Features
6
+
7
+ - 🔐 **Authentication**: Seamless Supabase Auth + Django User model sync
8
+ - 💾 **Database**: Direct PostgreSQL connection with Django ORM
9
+ - 📦 **Storage**: Django storage backends for Supabase Storage
10
+ - 🚀 **Production-Ready**: Connection pooling, caching, error handling
11
+ - 🔧 **Django-Native**: Uses Django's ecosystem and patterns
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ pip install django-supabase
17
+ ```
18
+
19
+ ## Quick Setup
20
+ ### Add to INSTALLED_APPS:
21
+
22
+ ```python
23
+ INSTALLED_APPS = [
24
+ # ...
25
+ 'django_supabase',
26
+ 'django_supabase.auth',
27
+ ]
28
+ ```
29
+
30
+ ## Configure Settings
31
+
32
+ ```python
33
+ SUPABASE_URL = 'https://your-project.supabase.co'
34
+ SUPABASE_KEY = 'your-publishable-or-anon-key'
35
+ SUPABASE_SECRET_KEY = 'your-secret-or-service-role-key' # server-side only
36
+
37
+ # Recommended default: ask Supabase Auth to validate bearer tokens.
38
+ SUPABASE_JWT_VERIFICATION = 'auth_server'
39
+
40
+ # Optional for projects using asymmetric JWT signing keys:
41
+ # SUPABASE_JWT_VERIFICATION = 'jwks'
42
+ # SUPABASE_JWT_ALGORITHMS = ['RS256', 'ES256']
43
+
44
+ # Legacy/local-only fallback for HS256 shared-secret verification:
45
+ # SUPABASE_JWT_VERIFICATION = 'jwt_secret'
46
+ # SUPABASE_JWT_SECRET = 'your-jwt-secret'
47
+
48
+ AUTH_USER_MODEL = 'supabase_auth.SupabaseUser'
49
+
50
+ AUTHENTICATION_BACKENDS = [
51
+ 'django_supabase.auth.backends.SupabaseAuthBackend',
52
+ 'django.contrib.auth.backends.ModelBackend',
53
+ ]
54
+
55
+ DATABASES = {
56
+ 'default': {
57
+ 'ENGINE': 'django_supabase.db.engine.SupabasePostgreSQLEngine',
58
+ 'NAME': 'postgres',
59
+ 'USER': 'postgres',
60
+ 'PASSWORD': 'your-password',
61
+ 'HOST': 'db.your-project.supabase.co',
62
+ 'PORT': 5432,
63
+ }
64
+ }
65
+
66
+ STORAGES = {
67
+ 'default': {
68
+ 'BACKEND': 'django_supabase.storage.backends.SupabaseMediaStorage',
69
+ },
70
+ 'staticfiles': {
71
+ 'BACKEND': 'django_supabase.storage.backends.SupabaseStaticStorage',
72
+ },
73
+ }
74
+ ```
75
+
76
+ ## Run Migrations
77
+
78
+ ```bash
79
+ python manage.py migrate
80
+ python manage.py create_storage_buckets
81
+ ```
82
+
83
+ # Usage Example
84
+ ## Authentication
85
+
86
+ ```python
87
+ from django.contrib.auth import authenticate
88
+
89
+ # Email/Password login
90
+ user = authenticate(email='user@example.com', password='password')
91
+
92
+ # JWT Token authentication (in views)
93
+ # Token automatically extracted from Authorization: Bearer <token>
94
+ ```
95
+
96
+ ## Frontend OAuth
97
+
98
+ Use Supabase Auth in the frontend with a publishable key. After OAuth login,
99
+ send the Supabase session access token to Django:
100
+
101
+ ```javascript
102
+ const { data, error } = await supabase.auth.signInWithOAuth({
103
+ provider: 'google',
104
+ options: { redirectTo: 'https://your-frontend.example.com/auth/callback' },
105
+ })
106
+
107
+ const { data: sessionData } = await supabase.auth.getSession()
108
+
109
+ await fetch('/api/me/', {
110
+ headers: {
111
+ Authorization: `Bearer ${sessionData.session.access_token}`,
112
+ },
113
+ })
114
+ ```
115
+
116
+ Django validates the bearer token through `SupabaseAuthMiddleware` and syncs the
117
+ local user by Supabase `sub` / `user.id`, not by email alone.
118
+
119
+ ## File Upload
120
+
121
+ ```python
122
+ from django_supabase.storage.utils import upload_file_to_supabase
123
+
124
+ file = ...
125
+
126
+ url = upload_file_to_supabase(file, 'media', path='uploads/')
127
+
128
+ ```
129
+
130
+ ## Direct Supabase Queries
131
+
132
+ ```python
133
+ from django_supabase.db.client import get_supabase_client
134
+
135
+ supabase = get_supabase_client()
136
+ response = supabase.table('posts').select('*').execute()
137
+ ```
138
+
139
+ ### Test
140
+
141
+ ```python
142
+ # supabase_integration/tests.py
143
+ from django.test import TestCase
144
+ from django.contrib.auth import get_user_model
145
+ from django_supabase.db.client import get_supabase_client
146
+
147
+ User = get_user_model()
148
+
149
+ class SupabaseIntegrationTestCase(TestCase):
150
+ """Base test case with Supabase utilities"""
151
+
152
+ @classmethod
153
+ def setUpClass(cls):
154
+ super().setUpClass()
155
+ cls.supabase = get_supabase_client()
156
+
157
+ def create_test_user(self, email='test@example.com'):
158
+ """Helper to create test users"""
159
+ return User.objects.create_user(
160
+ username=email,
161
+ email=email,
162
+ password='testpass123'
163
+ )
164
+ ```
@@ -0,0 +1,26 @@
1
+ [project]
2
+ name = "django-supabase"
3
+ version = "1.0.0"
4
+ description = "A lightweight django package designed to connect Django applications to Supabase Auth, Database Access, and Storage"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Lakan", email = "leydotpy.dev@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.14"
10
+ dependencies = [
11
+ "celery>=5.6.3",
12
+ "django>=6.0.6",
13
+ "django-celery-beat>=2.9.0",
14
+ "django-celery-results>=2.6.0",
15
+ "django-guardian>=3.3.2",
16
+ "django-redis>=7.0.0",
17
+ "djangorestframework>=3.17.1",
18
+ "gunicorn>=26.0.0",
19
+ "pyjwt[crypto]>=2.10.0",
20
+ "redis>=8.0.0",
21
+ "supabase>=2.31.0",
22
+ ]
23
+
24
+ [build-system]
25
+ requires = ["uv_build>=0.9.7,<0.10.0"]
26
+ build-backend = "uv_build"
@@ -0,0 +1,4 @@
1
+ __version__ = '1.0.0'
2
+
3
+ default_app_config = 'django_supabase.apps.DjangoSupabaseConfig'
4
+
@@ -0,0 +1,13 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class DjangoSupabaseConfig(AppConfig):
5
+ default_auto_field = 'django.db.models.BigAutoField'
6
+ name = 'django_supabase'
7
+ verbose_name = 'Supabase Integration'
8
+
9
+ def ready(self):
10
+ """Initialize Supabase integration on Django startup"""
11
+ from .conf import supabase_settings
12
+ # Validate settings on startup
13
+ supabase_settings.configure()
@@ -0,0 +1 @@
1
+ default_app_config = 'django_supabase.auth.apps.SupabaseAuthConfig'
@@ -0,0 +1,8 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class SupabaseAuthConfig(AppConfig):
5
+ name = 'django_supabase.auth'
6
+ label = 'supabase_auth'
7
+ verbose_name = 'Supabase User'
8
+ verbose_name_plural = 'Supabase Users'
@@ -0,0 +1,121 @@
1
+ import logging
2
+
3
+ from django.conf import settings
4
+ from django.contrib.auth import get_user_model
5
+ from django.contrib.auth.backends import BaseBackend
6
+ from supabase import create_client
7
+
8
+ from .sync import sync_user_from_claims, sync_user_from_supabase
9
+ from .tokens import (
10
+ SupabaseTokenError,
11
+ token_algorithm,
12
+ verify_token_with_jwks,
13
+ verify_token_with_secret,
14
+ )
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class SupabaseAuthBackend(BaseBackend):
20
+ """
21
+ Authenticates against Supabase and syncs with Django User model
22
+ """
23
+
24
+ def __init__(self):
25
+ self.supabase = create_client(
26
+ settings.SUPABASE_URL,
27
+ settings.SUPABASE_KEY
28
+ )
29
+
30
+ def authenticate(self, request, token=None, **credentials):
31
+ if token:
32
+ return self._authenticate_token(token)
33
+
34
+ # Email/password auth
35
+ email = credentials.get('email')
36
+ password = credentials.get('password')
37
+
38
+ if not email or not password:
39
+ return None
40
+
41
+ try:
42
+ response = self.supabase.auth.sign_in_with_password({
43
+ "email": email,
44
+ "password": password
45
+ })
46
+
47
+ if response.user:
48
+ user, created = self._sync_user(response.user)
49
+ return user
50
+
51
+ except Exception as exc:
52
+ logger.debug('Supabase email/password authentication failed: %s', exc)
53
+ return None
54
+
55
+ def _authenticate_token(self, token):
56
+ """Validate JWT token from Supabase"""
57
+ verification_mode = getattr(
58
+ settings,
59
+ 'SUPABASE_JWT_VERIFICATION',
60
+ 'auth_server',
61
+ ).lower()
62
+
63
+ if verification_mode == 'auth_server':
64
+ return self._authenticate_token_with_auth_server(token)
65
+
66
+ if verification_mode == 'jwks':
67
+ return self._authenticate_token_with_jwks(token)
68
+
69
+ if verification_mode == 'jwt_secret':
70
+ return self._authenticate_token_with_secret(token)
71
+
72
+ logger.error('Unsupported SUPABASE_JWT_VERIFICATION mode: %s', verification_mode)
73
+ return None
74
+
75
+ def _authenticate_token_with_auth_server(self, token):
76
+ try:
77
+ response = self.supabase.auth.get_user(token)
78
+ supabase_user = getattr(response, 'user', None)
79
+ if not supabase_user:
80
+ return None
81
+ user, _ = sync_user_from_supabase(supabase_user)
82
+ return user
83
+ except Exception as exc:
84
+ logger.debug('Supabase Auth server token validation failed: %s', exc)
85
+ return None
86
+
87
+ def _authenticate_token_with_jwks(self, token):
88
+ try:
89
+ if token_algorithm(token) == 'HS256':
90
+ logger.warning(
91
+ 'Received HS256 token while SUPABASE_JWT_VERIFICATION=jwks'
92
+ )
93
+ return None
94
+ payload = verify_token_with_jwks(token)
95
+ if getattr(settings, 'SUPABASE_SYNC_USER_FROM_AUTH_ON_TOKEN', False):
96
+ return self._authenticate_token_with_auth_server(token)
97
+ user, _ = sync_user_from_claims(payload)
98
+ return user
99
+ except SupabaseTokenError as exc:
100
+ logger.debug('Supabase JWKS token validation failed: %s', exc)
101
+ return None
102
+
103
+ def _authenticate_token_with_secret(self, token):
104
+ try:
105
+ payload = verify_token_with_secret(token)
106
+ user, _ = sync_user_from_claims(payload)
107
+ return user
108
+ except SupabaseTokenError as exc:
109
+ logger.debug('Supabase JWT secret token validation failed: %s', exc)
110
+ return None
111
+
112
+ def _sync_user(self, supabase_user):
113
+ """Sync Supabase user with Django User model"""
114
+ return sync_user_from_supabase(supabase_user)
115
+
116
+ def get_user(self, user_id):
117
+ User = get_user_model()
118
+ try:
119
+ return User.objects.get(pk=user_id)
120
+ except User.DoesNotExist:
121
+ return None
@@ -0,0 +1,33 @@
1
+ from django.contrib.auth import authenticate
2
+ from django.contrib.auth.middleware import get_user
3
+ from django.utils.functional import SimpleLazyObject
4
+
5
+
6
+ class SupabaseAuthMiddleware:
7
+ """
8
+ Middleware to authenticate requests using Supabase JWT tokens
9
+ """
10
+
11
+ def __init__(self, get_response):
12
+ self.get_response = get_response
13
+
14
+ def __call__(self, request):
15
+ # Extract token from Authorization header
16
+ auth_header = request.META.get('HTTP_AUTHORIZATION', '')
17
+
18
+ scheme, _, token = auth_header.partition(' ')
19
+
20
+ if scheme.lower() == 'bearer' and token:
21
+ request.user = SimpleLazyObject(
22
+ lambda: self._authenticate_token(request, token.strip())
23
+ )
24
+ else:
25
+ request.user = SimpleLazyObject(lambda: get_user(request))
26
+
27
+ response = self.get_response(request)
28
+ return response
29
+
30
+ def _authenticate_token(self, request, token):
31
+ """Authenticate using token"""
32
+ user = authenticate(request, token=token)
33
+ return user or get_user(request)
@@ -0,0 +1,63 @@
1
+ # Generated by Django 6.0.6 on 2026-06-11 12:57
2
+
3
+ import django.contrib.auth.models
4
+ import django.contrib.auth.validators
5
+ import django.utils.timezone
6
+ from django.db import migrations, models
7
+
8
+
9
+ class Migration(migrations.Migration):
10
+
11
+ initial = True
12
+
13
+ dependencies = [
14
+ ('auth', '0012_alter_user_first_name_max_length'),
15
+ ]
16
+
17
+ operations = [
18
+ migrations.CreateModel(
19
+ name='SupabaseUser',
20
+ fields=[
21
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
22
+ ('password', models.CharField(max_length=128, verbose_name='password')),
23
+ ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
24
+ ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
25
+ ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
26
+ ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
27
+ ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
28
+ ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
29
+ ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
30
+ ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
31
+ ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
32
+ ('supabase_id', models.UUIDField(blank=True, db_index=True, null=True, unique=True)),
33
+ ('phone', models.CharField(blank=True, max_length=32)),
34
+ ('display_name', models.CharField(blank=True, max_length=150)),
35
+ ('avatar_url', models.URLField(blank=True)),
36
+ ('aud', models.CharField(blank=True, max_length=64)),
37
+ ('role', models.CharField(blank=True, max_length=64)),
38
+ ('provider', models.CharField(blank=True, max_length=64)),
39
+ ('providers', models.JSONField(blank=True, default=list)),
40
+ ('app_metadata', models.JSONField(blank=True, default=dict)),
41
+ ('user_metadata', models.JSONField(blank=True, default=dict)),
42
+ ('metadata', models.JSONField(blank=True, default=dict)),
43
+ ('identities', models.JSONField(blank=True, default=list)),
44
+ ('email_confirmed_at', models.DateTimeField(blank=True, null=True)),
45
+ ('phone_confirmed_at', models.DateTimeField(blank=True, null=True)),
46
+ ('last_sign_in_at', models.DateTimeField(blank=True, null=True)),
47
+ ('supabase_created_at', models.DateTimeField(blank=True, null=True)),
48
+ ('supabase_updated_at', models.DateTimeField(blank=True, null=True)),
49
+ ('is_anonymous', models.BooleanField(default=False)),
50
+ ('is_sso_user', models.BooleanField(default=False)),
51
+ ('banned_until', models.DateTimeField(blank=True, null=True)),
52
+ ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
53
+ ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
54
+ ],
55
+ options={
56
+ 'db_table': 'auth_user',
57
+ 'swappable': 'AUTH_USER_MODEL',
58
+ },
59
+ managers=[
60
+ ('objects', django.contrib.auth.models.UserManager()),
61
+ ],
62
+ ),
63
+ ]
@@ -0,0 +1,36 @@
1
+ from django.contrib.auth.models import AbstractUser
2
+ from django.db import models
3
+
4
+ class SupabaseUser(AbstractUser):
5
+ """
6
+ Extended user model to store Supabase-specific data
7
+ """
8
+ supabase_id = models.UUIDField(unique=True, null=True, blank=True, db_index=True)
9
+ phone = models.CharField(max_length=32, blank=True)
10
+ display_name = models.CharField(max_length=150, blank=True)
11
+ avatar_url = models.URLField(blank=True)
12
+ aud = models.CharField(max_length=64, blank=True)
13
+ role = models.CharField(max_length=64, blank=True)
14
+ provider = models.CharField(max_length=64, blank=True)
15
+ providers = models.JSONField(default=list, blank=True)
16
+ app_metadata = models.JSONField(default=dict, blank=True)
17
+ user_metadata = models.JSONField(default=dict, blank=True)
18
+ metadata = models.JSONField(default=dict, blank=True)
19
+ identities = models.JSONField(default=list, blank=True)
20
+ email_confirmed_at = models.DateTimeField(null=True, blank=True)
21
+ phone_confirmed_at = models.DateTimeField(null=True, blank=True)
22
+ last_sign_in_at = models.DateTimeField(null=True, blank=True)
23
+ supabase_created_at = models.DateTimeField(null=True, blank=True)
24
+ supabase_updated_at = models.DateTimeField(null=True, blank=True)
25
+ is_anonymous = models.BooleanField(default=False)
26
+ is_sso_user = models.BooleanField(default=False)
27
+ banned_until = models.DateTimeField(null=True, blank=True)
28
+
29
+ class Meta:
30
+ db_table = 'auth_user' # Keep Django's default table name
31
+ swappable = 'AUTH_USER_MODEL'
32
+
33
+ @property
34
+ def name(self):
35
+ return self.display_name or self.get_full_name() or self.username
36
+