diff --git a/DjangoFiles/Administration/__init__.py b/DjangoFiles/Administration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/DjangoFiles/Administration/admin.py b/DjangoFiles/Administration/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/DjangoFiles/Administration/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/DjangoFiles/Administration/apps.py b/DjangoFiles/Administration/apps.py new file mode 100644 index 0000000..bad86a2 --- /dev/null +++ b/DjangoFiles/Administration/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class AdministrationConfig(AppConfig): + name = 'Administration' diff --git a/DjangoFiles/Administration/migrations/__init__.py b/DjangoFiles/Administration/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/DjangoFiles/Administration/models.py b/DjangoFiles/Administration/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/DjangoFiles/Administration/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/DjangoFiles/Administration/tests.py b/DjangoFiles/Administration/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/DjangoFiles/Administration/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/DjangoFiles/Administration/views.py b/DjangoFiles/Administration/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/DjangoFiles/Administration/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/DjangoFiles/Customers/admin.py b/DjangoFiles/Customers/admin.py index 8c38f3f..60b2a4f 100644 --- a/DjangoFiles/Customers/admin.py +++ b/DjangoFiles/Customers/admin.py @@ -1,3 +1,8 @@ from django.contrib import admin +from django_tenants.admin import TenantAdminMixin -# Register your models here. +from Customers.models import Client + +@admin.register(Client) +class ClientAdmin(TenantAdminMixin, admin.ModelAdmin): + list_display = ('name', 'paid_until') \ No newline at end of file diff --git a/DjangoFiles/Customers/migrations/0001_initial.py b/DjangoFiles/Customers/migrations/0001_initial.py new file mode 100644 index 0000000..6d1bf81 --- /dev/null +++ b/DjangoFiles/Customers/migrations/0001_initial.py @@ -0,0 +1,42 @@ +# Generated by Django 2.2 on 2021-06-10 12:04 + +from django.db import migrations, models +import django.db.models.deletion +import django_tenants.postgresql_backend.base + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Client', + fields=[ + ('id', models.AutoField(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])), + ('name', models.CharField(max_length=100)), + ('paid_until', models.DateField()), + ('on_trial', models.BooleanField()), + ('created_on', models.DateField(auto_now_add=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Domain', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('domain', models.CharField(db_index=True, max_length=253, unique=True)), + ('is_primary', models.BooleanField(db_index=True, default=True)), + ('tenant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='domains', to='Customers.Client')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/DjangoFiles/Customers/migrations/0002_auto_20210610_1605.py b/DjangoFiles/Customers/migrations/0002_auto_20210610_1605.py new file mode 100644 index 0000000..7b53538 --- /dev/null +++ b/DjangoFiles/Customers/migrations/0002_auto_20210610_1605.py @@ -0,0 +1,46 @@ +# Generated by Django 2.2.13 on 2021-06-08 10:08 +import os +from django.db import migrations + + +def create_premier_tenant(apps, schema_editor): + # We can't import the Person model directly as it may be a newer + # version than this migration expects. We use the historical version. + Client = apps.get_model('Customers', 'Client') + Domain = apps.get_model('Customers', 'Domain') + DNS = os.getenv('DOMAIN') + + tenant_public = Client.objects.get_or_create(schema_name='public', + name='Tibillet inc.', + paid_until='2200-12-05', + on_trial=False)[0] + + # Add one or more domains for the tenant + domaine_seul = Domain.objects.get_or_create(domain=DNS, + tenant=tenant_public, + is_primary=True, + ) + + domaine_www = Domain.objects.get_or_create(domain=f'www.{DNS}', + tenant=tenant_public, + is_primary=False, + ) + + return tenant_public, domaine_seul[0], domaine_www[0] + + +def reverse(apps, schema_editor): + tenant_public, domaine_seul, domaine_www = create_premier_tenant(apps, schema_editor) + tenant_public.delete() + domaine_seul.delete() + domaine_www.delete() + + +class Migration(migrations.Migration): + dependencies = [ + ('Customers', '0001_initial'), + ] + + operations = [ + migrations.RunPython(create_premier_tenant, reverse), + ] diff --git a/DjangoFiles/Customers/models.py b/DjangoFiles/Customers/models.py index 71a8362..318080b 100644 --- a/DjangoFiles/Customers/models.py +++ b/DjangoFiles/Customers/models.py @@ -1,3 +1,14 @@ from django.db import models +from django_tenants.models import TenantMixin, DomainMixin -# Create your models here. +class Client(TenantMixin): + name = models.CharField(max_length=100) + paid_until = models.DateField() + on_trial = models.BooleanField() + created_on = models.DateField(auto_now_add=True) + + # default true, schema will be automatically created and synced when it is saved + auto_create_schema = True + +class Domain(DomainMixin): + pass \ No newline at end of file diff --git a/DjangoFiles/TiBillet/settings.py b/DjangoFiles/TiBillet/settings.py index 5f6a0b8..ae9942e 100644 --- a/DjangoFiles/TiBillet/settings.py +++ b/DjangoFiles/TiBillet/settings.py @@ -1,45 +1,69 @@ """ Django settings for TiBillet project. -Generated by 'django-admin startproject' using Django 2.2. +Generated by 'django-admin startproject' using Django 3.1. For more information on this file, see -https://docs.djangoproject.com/en/2.2/topics/settings/ +https://docs.djangoproject.com/en/3.1/topics/settings/ For the full list of settings and their values, see -https://docs.djangoproject.com/en/2.2/ref/settings/ +https://docs.djangoproject.com/en/3.1/ref/settings/ """ - import os +from pathlib import Path -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve(strict=True).parent.parent # Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ +# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '(4ux&3#$@yn=dxpi%sl^*&ib+mtg2c0-2gxwjl-m&^ci@+a!o!' +SECRET_KEY = os.environ.get('DJANGO_SECRET') # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -ALLOWED_HOSTS = [] +# noinspection DjangoDebugModeSettings +if os.environ.get('DEBUG_DJANGO') == "True": + DEBUG = True +else: + DEBUG = False +ALLOWED_HOSTS = ['*'] # Application definition -INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', +SHARED_APPS = ( + 'django_tenants', # mandatory + 'Customers', # you must list the app where your tenant model resides in + 'django.contrib.contenttypes', + + # everything below here is optional + 'django.contrib.auth', 'django.contrib.sessions', + 'django.contrib.sites', 'django.contrib.messages', - 'django.contrib.staticfiles', -] + 'django.contrib.admin', + 'django_extensions', + 'Administration', +) + +TENANT_APPS = ( + # The following Django contrib apps must be in TENANT_APPS + 'django.contrib.contenttypes', + + # your tenant-specific apps +) + +INSTALLED_APPS = list(SHARED_APPS) + [app for app in TENANT_APPS if app not in SHARED_APPS] +TENANT_MODEL = "Customers.Client" # app.Model +TENANT_DOMAIN_MODEL = "Customers.Domain" # app.Model +# ROOT_URLCONF = 'TiBillet.urls_tenants' +# PUBLIC_SCHEMA_URLCONF = 'TiBillet.urls_public' + MIDDLEWARE = [ + 'django_tenants.middleware.main.TenantMainMiddleware', 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', @@ -69,20 +93,26 @@ TEMPLATES = [ WSGI_APPLICATION = 'TiBillet.wsgi.application' - # Database -# https://docs.djangoproject.com/en/2.2/ref/settings/#databases +# https://docs.djangoproject.com/en/3.1/ref/settings/#databases DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + 'ENGINE': 'django_tenants.postgresql_backend', # Add 'postgresql', 'mysql', 'sqlite3' or 'oracle'. + 'NAME': os.environ.get('POSTGRES_DB'), + 'USER': os.environ.get('POSTGRES_USER'), + 'PASSWORD': os.environ.get('POSTGRES_PASSWORD'), + 'HOST': os.environ.get('POSTGRES_HOST'), + 'PORT': os.environ.get('POSTGRES_PORT', '5432'), } } +DATABASE_ROUTERS = ( + 'django_tenants.routers.TenantSyncRouter', +) # Password validation -# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators +# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { @@ -99,13 +129,12 @@ AUTH_PASSWORD_VALIDATORS = [ }, ] - # Internationalization -# https://docs.djangoproject.com/en/2.2/topics/i18n/ +# https://docs.djangoproject.com/en/3.1/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = 'fr-fr' -TIME_ZONE = 'UTC' +TIME_ZONE = os.environ.get('TIME_ZONE', 'UTC') USE_I18N = True @@ -113,8 +142,20 @@ USE_L10N = True USE_TZ = True - # Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/2.2/howto/static-files/ +# https://docs.djangoproject.com/en/3.1/howto/static-files/ +STATIC_ROOT = os.path.join(BASE_DIR, "www", "static") STATIC_URL = '/static/' + +MEDIA_ROOT = os.path.join(BASE_DIR, "www", "media") +MEDIA_URL = '/media/' + + +# EMAIL +EMAIL_HOST = os.environ.get('EMAIL_HOST') +EMAIL_PORT = os.environ.get('EMAIL_PORT') +EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER') +EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD') +EMAIL_USE_TLS = os.environ.get('EMAIL_USE_TLS', False) +EMAIL_USE_SSL = os.environ.get('EMAIL_USE_SSL', True) \ No newline at end of file