shared_accounts, accounts: ikkita user model qoshildi

This commit is contained in:
behruz-dev
2025-12-07 14:54:54 +05:00
parent 30c3b4df2e
commit 3c79a4c83c
31 changed files with 234 additions and 128 deletions

View File

@@ -14,14 +14,3 @@ docker exec -it <container_name> bash
python manage.py createclient python manage.py createclient
``` ```
## SuperUser yaratish uchun
``` bash
python manage.py createuser
```
- Schema name: -> client qoshishda kiritgan schema name.
- Username: -> login qilish uchun username.
- First name: -> Ism (shart emas).
- Last name: -> Familiya (shart emas).
- Phone number: -> Telefon raqam (shart emas).
- Password: -> login qilish uchun parol.

View File

@@ -10,26 +10,29 @@ env.read_env(BASE_DIR / '.env')
SECRET_KEY = env.str('SECRET_KEY') SECRET_KEY = env.str('SECRET_KEY')
DEBUG = env.bool('DEBUG') DEBUG = env.bool('DEBUG')
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS') ALLOWED_HOSTS = env.list('ALLOWED_HOSTS')
ALLOWED_HOSTS = ["*"]
# APPS # APPS
SHARED_APPS = [ SHARED_APPS = [
'django_tenants', 'django_tenants',
'jazzmin',
'core.apps.customers', 'core.apps.customers',
# django apps
'django.contrib.contenttypes', 'django.contrib.contenttypes',
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
# accounts # local apps
'core.apps.accounts', 'core.apps.shared_accounts',
] ]
TENANT_APPS = [ TENANT_APPS = [
'core.apps.accounts',
'core.apps.shared', 'core.apps.shared',
'core.apps.products',
] ]
PACKAGES = [ PACKAGES = [
@@ -40,7 +43,7 @@ PACKAGES = [
] ]
INSTALLED_APPS = SHARED_APPS + PACKAGES + TENANT_APPS INSTALLED_APPS = SHARED_APPS + TENANT_APPS + PACKAGES
# Middlewares # Middlewares
MIDDLEWARE = [ MIDDLEWARE = [
@@ -120,7 +123,7 @@ MEDIA_ROOT = BASE_DIR / 'resources/media'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
AUTH_USER_MODEL = 'accounts.User' AUTH_USER_MODEL = 'shared_accounts.AdminUser'
# Django tenants # Django tenants
TENANT_MODEL = "customers.Client" TENANT_MODEL = "customers.Client"

View File

@@ -1,6 +1,6 @@
# django # django
from django.contrib import admin from django.contrib import admin
from django.urls import path from django.urls import path, include
from django.conf import settings from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
@@ -34,5 +34,13 @@ urlpatterns += [
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
] ]
urlpatterns += [
path('api/v1/', include(
[
path('accounts/', include('core.apps.accounts.urls')),
]
)),
]
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@@ -11,7 +11,7 @@ from core.apps.accounts.models.user import User
class UserAdmin(DjangoUserAdmin): class UserAdmin(DjangoUserAdmin):
fieldsets = ( fieldsets = (
(None, {"fields": ("username", "password")}), (None, {"fields": ("username", "password")}),
(("Personal info"), {"fields": ("first_name", "last_name", "email", "client", "phone_number", "profile_image")}), (("Personal info"), {"fields": ("first_name", "last_name", "email", "phone_number", "profile_image")}),
( (
("Permissions"), ("Permissions"),
{ {
@@ -29,11 +29,12 @@ class UserAdmin(DjangoUserAdmin):
None, None,
{ {
"classes": ("wide",), "classes": ("wide",),
"fields": ("username", "password1", "password2", "client"), "fields": ("username", "first_name", "last_name", "phone_number", "password1", "password2"),
}, },
), ),
) )
list_display = ("username", "phone_number", "first_name", "last_name", "is_staff") list_display = ("username", "phone_number", "first_name", "last_name", "is_staff")
list_filter = ("is_staff", "is_superuser", "is_active", "groups") list_filter = ("is_staff", "is_superuser", "is_active")
search_fields = ("username", "first_name", "last_name", "email") search_fields = ("username", "first_name", "last_name", "email")
ordering = ("username",) ordering = ("username",)
filter_horizontal = ()

View File

@@ -1,53 +0,0 @@
# pypi
from getpass import getpass
# django
from django.contrib.auth.management.commands.createsuperuser import Command as SuperUserCommand
# django tenants
from django_tenants.utils import schema_context
# accounts
from core.apps.accounts.models import User
# customers
from core.apps.customers.models import Client
class Command(SuperUserCommand):
def handle(self, *args, **options):
while True:
schema = input("Enter schema name: ")
client = Client.objects.filter(schema_name=schema).first()
if not client:
self.stdout.write(self.style.WARNING("Schema not found"))
else:
break
while True:
username = input("Enter username: ")
if User.objects.filter(username=username).exists():
self.stdout.write(self.style.WARNING("User already exists"))
else:
break
first_name = input("Enter first name: ")
last_name = input("Enter last name: ")
phone_number = input("Enter phone number: ")
password = getpass("Enter password: ")
User.objects.create_superuser(
password=password,
username=username,
client=client,
first_name=first_name,
last_name=last_name,
phone_number=phone_number,
)
self.stdout.write(
self.style.SUCCESS(
f"Superuser created successfully in schema '{schema}'"
)
)

View File

@@ -1,4 +1,4 @@
# Generated by Django 5.2 on 2025-12-05 11:43 # Generated by Django 5.2 on 2025-12-07 09:47
import django.contrib.auth.models import django.contrib.auth.models
import django.contrib.auth.validators import django.contrib.auth.validators
@@ -12,7 +12,6 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
] ]
operations = [ operations = [
@@ -33,9 +32,7 @@ class Migration(migrations.Migration):
('created_at', models.DateTimeField(auto_now_add=True)), ('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)), ('updated_at', models.DateTimeField(auto_now=True)),
('profile_image', models.ImageField(blank=True, null=True, upload_to='user/profile_images/')), ('profile_image', models.ImageField(blank=True, null=True, upload_to='user/profile_images/')),
('phone_number', models.CharField(blank=True, max_length=15, null=True, validators=[django.core.validators.RegexValidator(message='The phone_number is invalid. The format should be like this: 998XXXXXXXXX', regex='^998\\d{9}$')])), ('phone_number', models.CharField(blank=True, max_length=15, null=True, validators=[django.core.validators.RegexValidator(message='The phone_number is invalid. The format should be like this: +998XXXXXXXXX', regex='^\\+998\\d{9}$')])),
('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')),
('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')),
], ],
options={ options={
'verbose_name': 'user', 'verbose_name': 'user',

View File

@@ -1,20 +0,0 @@
# Generated by Django 5.2 on 2025-12-05 12:07
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0001_initial'),
('customers', '0002_remove_client_on_trial_remove_client_paid_until'),
]
operations = [
migrations.AddField(
model_name='user',
name='client',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='users', to='customers.client'),
),
]

View File

@@ -21,9 +21,8 @@ class User(AbstractUser, BaseModel):
phone_number = models.CharField( phone_number = models.CharField(
max_length=15, null=True, blank=True, validators=[uz_phone_validator] max_length=15, null=True, blank=True, validators=[uz_phone_validator]
) )
client = models.ForeignKey( groups = None
Client, on_delete=models.CASCADE, related_name='users', null=True, user_permissions = None
)
def __str__(self): def __str__(self):
return f"#{self.id}: {self.first_name} {self.last_name}" return f"#{self.id}: {self.first_name} {self.last_name}"

View File

@@ -0,0 +1,21 @@
# django
from django.urls import path, include
# rest framework simplejwt
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
urlpatterns = [
path('user/', include(
[
]
)),
# ------ authentication ------
path('auth/', include(
[
path('login/', TokenObtainPairView.as_view(), name='login-api'),
path('token/refresh/', TokenRefreshView.as_view(), name='token-refresh-api'),
]
)),
]

View File

@@ -1,11 +1,16 @@
# django
from django.contrib import admin from django.contrib import admin
# django tenants
from django_tenants.admin import TenantAdminMixin
# curstomers
from core.apps.customers.models import Client from core.apps.customers.models import Client
from core.apps.customers.admin.domain import DomainInline from core.apps.customers.admin.domain import DomainInline
@admin.register(Client) @admin.register(Client)
class ClientAdmin(admin.ModelAdmin): class ClientAdmin(TenantAdminMixin, admin.ModelAdmin):
list_display = ['id', 'name', 'schema_name'] list_display = ['id', 'name', 'schema_name']
search_fields = ['name'] search_fields = ['name']
inlines = [DomainInline] inlines = [DomainInline]

View File

@@ -1,8 +1,16 @@
# django
from django.contrib import admin from django.contrib import admin
# customers
from core.apps.customers.models import Domain from core.apps.customers.models import Domain
class DomainInline(admin.TabularInline): class DomainInline(admin.TabularInline):
model = Domain model = Domain
extra = 0 extra = 0
@admin.register(Domain)
class DomainAdmin(admin.ModelAdmin):
pass

View File

@@ -1,4 +1,4 @@
# Generated by Django 5.2 on 2025-11-19 10:53 # Generated by Django 5.2 on 2025-12-07 09:47
import django.db.models.deletion import django.db.models.deletion
import django_tenants.postgresql_backend.base import django_tenants.postgresql_backend.base
@@ -19,8 +19,6 @@ class Migration(migrations.Migration):
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('schema_name', models.CharField(db_index=True, max_length=63, unique=True, validators=[django_tenants.postgresql_backend.base._check_schema_name])), ('schema_name', models.CharField(db_index=True, max_length=63, unique=True, validators=[django_tenants.postgresql_backend.base._check_schema_name])),
('name', models.CharField(max_length=100)), ('name', models.CharField(max_length=100)),
('paid_until', models.DateField()),
('on_trial', models.BooleanField()),
('created_at', models.DateField(auto_now_add=True)), ('created_at', models.DateField(auto_now_add=True)),
], ],
options={ options={

View File

@@ -1,21 +0,0 @@
# Generated by Django 5.2 on 2025-11-19 10:57
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('customers', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='client',
name='on_trial',
),
migrations.RemoveField(
model_name='client',
name='paid_until',
),
]

View File

@@ -0,0 +1 @@
from .product import *

View File

@@ -0,0 +1,9 @@
# django
from django.contrib import admin
# products
from core.apps.products.models import Product
admin.site.register(Product)

View File

@@ -0,0 +1,9 @@
from django.apps import AppConfig
class ProductsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'core.apps.products'
def ready(self):
import core.apps.products.admin

View File

@@ -0,0 +1,26 @@
# Generated by Django 5.2 on 2025-12-07 09:47
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Product',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=200)),
],
options={
'abstract': False,
},
),
]

View File

@@ -0,0 +1 @@
from .product import *

View File

@@ -0,0 +1,14 @@
# django
from django.db import models
# shared
from core.apps.shared.models import BaseModel
class Product(BaseModel):
name = models.CharField(max_length=200)
def __str__(self):
return self.name

View File

View File

View File

@@ -0,0 +1 @@
from .user import *

View File

@@ -0,0 +1,39 @@
# django
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin
# shared_accounts
from core.apps.shared_accounts.models.user import AdminUser
@admin.register(AdminUser)
class UserAdmin(DjangoUserAdmin):
fieldsets = (
(None, {"fields": ("username", "password")}),
(("Personal info"), {"fields": ("first_name", "last_name", "email")}),
(
("Permissions"),
{
"fields": (
"is_active",
"is_staff",
"is_superuser",
),
},
),
(("Important dates"), {"fields": ("last_login", "date_joined")}),
)
add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": ("username", "password1", "password2"),
},
),
)
list_display = ("username", "first_name", "last_name", "is_staff")
list_filter = ("is_staff", "is_superuser", "is_active",)
search_fields = ("username", "first_name", "last_name", "email")
ordering = ("username",)

View File

@@ -0,0 +1,9 @@
from django.apps import AppConfig
class SharedAccountsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'core.apps.shared_accounts'
def ready(self):
import core.apps.shared_accounts.admin

View File

@@ -0,0 +1,45 @@
# Generated by Django 5.2 on 2025-12-07 09:47
import django.contrib.auth.models
import django.contrib.auth.validators
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
migrations.CreateModel(
name='AdminUser',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('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')),
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('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')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('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')),
('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')),
],
options={
'verbose_name': 'Admin User',
'verbose_name_plural': 'Admin Users',
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
]

View File

@@ -0,0 +1 @@
from .user import *

View File

@@ -0,0 +1,16 @@
# django
from django.db import models
from django.contrib.auth.models import AbstractUser
# shared
from core.apps.shared.models import BaseModel
class AdminUser(AbstractUser, BaseModel):
def __str__(self):
return f"{self.first_name} {self.last_name}"
class Meta:
verbose_name = "Admin User"
verbose_name_plural = "Admin Users"

View File