Skip to content

Commit

Permalink
feat: implements tenant configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
yokwejuste committed Jul 30, 2023
1 parent b18204d commit f0f4a55
Show file tree
Hide file tree
Showing 15 changed files with 232 additions and 52 deletions.
8 changes: 7 additions & 1 deletion apps/portfolio/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
from django.contrib import admin

# Register your models here.
from apps.users.models.users import VisuleoUser

admin.site.site_header = "Visuleo Admin"
admin.site.site_title = "Visuleo Admin Portal"
admin.site.index_title = "Welcome to Visuleo Portal"

admin.site.register(VisuleoUser)
2 changes: 1 addition & 1 deletion apps/portfolio/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 4.1.7 on 2023-07-29 12:07
# Generated by Django 4.1.7 on 2023-07-30 16:31

from django.db import migrations, models
import utils.main
Expand Down
2 changes: 1 addition & 1 deletion apps/users/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 4.1.7 on 2023-07-29 12:07
# Generated by Django 4.1.7 on 2023-07-30 16:31

import django.contrib.auth.validators
from django.db import migrations, models
Expand Down
36 changes: 20 additions & 16 deletions apps/users/models/base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,22 @@ class BaseModelDeletionManager(models.Manager):
Custom model manager that returns all objects, including those that are
deleted.
"""

def get_queryset(self):
return super().get_queryset()

def all_with_deleted(self):
return super().get_queryset().filter(is_deleted=True)



class BaseMdeoelManager(models.Manager):
class BaseModelManager(models.Manager):
"""
Custom model manager that returns only objects that are not deleted.
"""

def get_queryset(self):
return super().get_queryset().filter(is_deleted=False)

def all_with_deleted(self):
return super().get_queryset()

Expand All @@ -32,34 +33,37 @@ class BaseModel(models.Model):
Abstract model that provides self-updating ``created`` and ``modified``
fields.
"""

id = models.UUIDField(
_('id'),
_("id"),
primary_key=True,
default=generate_uuid,
editable=False,
help_text=_('Unique identifier for this object.'),
help_text=_("Unique identifier for this object."),
)
created = models.DateTimeField(
_('created'),
_("created"),
auto_now_add=True,
help_text=_('Date and time when this object was created.'),
help_text=_("Date and time when this object was created."),
)
modified = models.DateTimeField(
_('modified'),
_("modified"),
auto_now=True,
help_text=_('Date and time when this object was last modified.'),
help_text=_("Date and time when this object was last modified."),
)
is_deleted = models.BooleanField(
_('is deleted'),
_("is deleted"),
default=False,
help_text=_('Boolean field to mark if this object is deleted.'),
help_text=_("Boolean field to mark if this object is deleted."),
)

objects = BaseMdeoelManager()

deleted_obkects = BaseModelDeletionManager()
objects = BaseModelManager()

deleted_obkects = BaseModelDeletionManager()

class Meta:
abstract = True
ordering = ('-created', '-modified',)
ordering = (
"-created",
"-modified",
)
1 change: 1 addition & 0 deletions apps/users/models/tenant_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class Client(TenantMixin):
paid_until = models.DateField()
on_trial = models.BooleanField()
created_on = models.DateField(auto_now_add=True)
auto_create_schema = True

def __str__(self)->str:
return self.name
Expand Down
87 changes: 60 additions & 27 deletions apps/users/models/users.py
Original file line number Diff line number Diff line change
@@ -1,70 +1,103 @@
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.models import AbstractUser, BaseUserManager
from django.utils.translation import gettext_lazy as _
from django.utils import timezone
from apps.users.models import BaseModel


class VisuleoUserManager(BaseUserManager):
"""
Custom user model manager for Visuleo.
"""

def create_user(self, email, password, **extra_fields):
"""
Create and save a Visuleo user with the given email and password.
"""
if not email:
raise ValueError(_("The email must be set."))
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save()
return user

def create_superuser(self, email, password, **extra_fields):
"""
Create and save a Visuleo superuser with the given email and password.
"""
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
extra_fields.setdefault("is_email_verified", True)
extra_fields.setdefault("is_phone_number_verified", True)
return self.create_user(email, password, **extra_fields)


class VisuleoUser(BaseModel, AbstractUser):
"""
Custom user model for Visuleo.
"""

name = models.CharField(
_('name'),
_("name"),
max_length=255,
blank=True,
help_text=_('User\'s full name.'),
help_text=_("User's full name."),
)
email = models.EmailField(
_('email address'),
_("email address"),
unique=True,
help_text=_('User\'s email address.'),
help_text=_("User's email address."),
)
phone_number = models.CharField(
_('phone number'),
_("phone number"),
max_length=20,
blank=True,
help_text=_('User\'s phone number.'),
help_text=_("User's phone number."),
)
is_email_verified = models.BooleanField(
_('is email verified'),
_("is email verified"),
default=False,
help_text=_('Boolean field to mark if this user\'s email is verified.'),
help_text=_("Boolean field to mark if this user's email is verified."),
)
is_phone_number_verified = models.BooleanField(
_('is phone number verified'),
_("is phone number verified"),
default=False,
help_text=_('Boolean field to mark if this user\'s phone number is verified.'),
help_text=_("Boolean field to mark if this user's phone number is verified."),
)
is_active = models.BooleanField(
_('is active'),
_("is active"),
default=True,
help_text=_('Boolean field to mark if this user is active.'),
help_text=_("Boolean field to mark if this user is active."),
)
is_superuser = models.BooleanField(
_('is superuser'),
_("is superuser"),
default=False,
help_text=_('Boolean field to mark if this user is superuser.'),
help_text=_("Boolean field to mark if this user is superuser."),
)
last_login = models.DateTimeField(
_('last login'),
_("last login"),
default=timezone.now,
help_text=_('Date and time when this user last logged in.'),
help_text=_("Date and time when this user last logged in."),
)
date_joined = models.DateTimeField(
_('date joined'),
_("date joined"),
default=timezone.now,
help_text=_('Date and time when this user joined.'),
help_text=_("Date and time when this user joined."),
)

USERNAME_FIELD = 'email'

objects = VisuleoUserManager()

USERNAME_FIELD = "email"
REQUIRED_FIELDS = []

class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
ordering = ('-created', '-modified',)

verbose_name = _("user")
verbose_name_plural = _("users")
ordering = (
"-created",
"-modified",
)

def __str__(self) -> str:
return self.email

2 changes: 1 addition & 1 deletion apps/users/serializers/auth_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class Meta:
)


class UserResponseSerializer(serializers.ModelSerializer):
class UserResponseSerializer(serializers.Serializer):
"""
Serializer for user.
"""
Expand Down
1 change: 1 addition & 0 deletions apps/users/views/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from .auth_views import RegistrationView, LoginView, LogoutView
from .default import DefaultView
3 changes: 3 additions & 0 deletions apps/users/views/auth_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
from drf_yasg.utils import swagger_auto_schema
from django.utils.timezone import now

from utils.main import load_document


User = get_user_model()

Expand Down Expand Up @@ -118,6 +120,7 @@ def get_serializer(self, *args, **kwargs):
@swagger_auto_schema(
operation_id="Login",
operation_summary="Login a user",
operation_description=load_document("auth/login_docs.html"),
request_body=LoginSerializer,
tags=["Authentication and Management"],
responses={
Expand Down
14 changes: 14 additions & 0 deletions apps/users/views/default.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from rest_framework.views import APIView
from rest_framework.response import Response


class DefaultView(APIView):
"""
Default view for the users app.
"""
authentication_classes = ()
permission_classes = ()


def get(self, request):
return Response({"message": "No tenants found."})
Empty file.
3 changes: 1 addition & 2 deletions visuleo_port/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@


MIDDLEWARE = [
"django_tenants.middleware.main.TenantMainMiddleware",
"django_tenants.middleware.TenantSubfolderMiddleware",
'django_tenants.middleware.main.TenantMainMiddleware',
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
Expand Down
27 changes: 24 additions & 3 deletions visuleo_port/settings/extra.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,33 @@
# djanfo tenant conf
TENANT_MODEL = "users.Client"
TENANT_DOMAIN_MODEL = "users.Domain"
TENANT_SUBFOLDER_PREFIX = "clients"
DATABASE_ROUTERS = ("django_tenants.routers.TenantSyncRouter",)

TENANT_APPS = ["apps.portfolio.apps.PortfolioConfig", "apps.users.apps.UsersConfig"]
DATABASE_ROUTERS = ["django_tenants.routers.TenantSyncRouter"]
TENANT_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"apps.portfolio.apps.PortfolioConfig",
"apps.users.apps.UsersConfig",
"apps.portfolio.apps.PortfolioConfig",
"apps.users.apps.UsersConfig",
"rest_framework",
"oauth2_provider",
"drf_yasg",
"django_filters",
"corsheaders",
"simple_history",
"allauth",
"allauth.account",
"storages",
]
DATABASE_ROUTERS = ("django_tenants.routers.TenantSyncRouter",)


PUBLIC_SCHEMA_URLCONF = "visuleo_port.urls_public"

SHARED_APPS = [
"django.contrib.admin",
Expand Down
1 change: 1 addition & 0 deletions visuleo_port/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,5 @@
path("admin/", admin.site.urls),
path(ROUTE_BASE_VERSION, include("apps.portfolio.routes.api")),
path(ROUTE_BASE_VERSION, include("apps.users.routes.api")),
path('api-auth/', include('rest_framework.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Loading

0 comments on commit f0f4a55

Please sign in to comment.