diff --git a/src/_example/django/django_demo/.forestadmin-schema.json b/src/_example/django/django_demo/.forestadmin-schema.json index d1ce14113..254127190 100644 --- a/src/_example/django/django_demo/.forestadmin-schema.json +++ b/src/_example/django/django_demo/.forestadmin-schema.json @@ -945,6 +945,768 @@ } ] }, + { + "name": "FlaskAddress", + "isVirtual": false, + "icon": null, + "isReadOnly": false, + "integration": null, + "isSearchable": true, + "onlyForRelationships": false, + "paginationType": "page", + "searchField": null, + "actions": [], + "segments": [], + "fields": [ + { + "defaultValue": null, + "enums": null, + "field": "city", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "String", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + }, + { + "defaultValue": null, + "enums": null, + "field": "country", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "String", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + }, + { + "defaultValue": null, + "enums": null, + "field": "customers", + "inverseOf": "addresses", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "FlaskCustomer.id", + "relationship": "BelongsToMany", + "type": [ + "String" + ], + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "flaskcustomersaddresses", + "inverseOf": "address", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "FlaskCustomersAddresses.id", + "relationship": "HasMany", + "type": [ + "Number" + ], + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "flaskorder", + "inverseOf": "billing_address", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "FlaskOrder.id", + "relationship": "HasMany", + "type": [ + "Number" + ], + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "id", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "Number", + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "order_delivering_address_set", + "inverseOf": "delivering_address", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "FlaskOrder.id", + "relationship": "HasMany", + "type": [ + "Number" + ], + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "street", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "String", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + }, + { + "defaultValue": null, + "enums": null, + "field": "street_number", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "String", + "validations": [ + { + "type": "is shorter than", + "value": 2, + "message": null + } + ] + }, + { + "defaultValue": null, + "enums": null, + "field": "zip_code", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "String", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 5, + "message": null + } + ] + } + ] + }, + { + "name": "FlaskCart", + "isVirtual": false, + "icon": null, + "isReadOnly": false, + "integration": null, + "isSearchable": true, + "onlyForRelationships": false, + "paginationType": "page", + "searchField": null, + "actions": [], + "segments": [], + "fields": [ + { + "defaultValue": null, + "enums": null, + "field": "created_at", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "Date", + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "id", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "Number", + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "name", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "String", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + }, + { + "defaultValue": null, + "enums": null, + "field": "order", + "inverseOf": "flaskcart", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "FlaskOrder.id", + "relationship": "BelongsTo", + "type": "Number", + "validations": [] + } + ] + }, + { + "name": "FlaskCustomer", + "isVirtual": false, + "icon": null, + "isReadOnly": false, + "integration": null, + "isSearchable": true, + "onlyForRelationships": false, + "paginationType": "page", + "searchField": null, + "actions": [], + "segments": [], + "fields": [ + { + "defaultValue": null, + "enums": null, + "field": "addresses", + "inverseOf": "customers", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "FlaskAddress.id", + "relationship": "BelongsToMany", + "type": [ + "Number" + ], + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "age", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "Number", + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "avatar", + "integration": null, + "inverseOf": null, + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "String", + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "birthday_date", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "Date", + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "first_name", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "String", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + }, + { + "defaultValue": null, + "enums": null, + "field": "flaskcustomersaddresses", + "inverseOf": "customer", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "FlaskCustomersAddresses.id", + "relationship": "HasMany", + "type": [ + "String" + ], + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "flaskorder", + "inverseOf": "customer", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "FlaskOrder.id", + "relationship": "HasMany", + "type": [ + "String" + ], + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "id", + "integration": null, + "inverseOf": null, + "isFilterable": false, + "isPrimaryKey": true, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "String", + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "is_vip", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "Boolean", + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "last_name", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "String", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + } + ] + }, + { + "name": "FlaskCustomersAddresses", + "isVirtual": false, + "icon": null, + "isReadOnly": false, + "integration": null, + "isSearchable": true, + "onlyForRelationships": false, + "paginationType": "page", + "searchField": null, + "actions": [], + "segments": [], + "fields": [ + { + "defaultValue": null, + "enums": null, + "field": "address", + "inverseOf": "flaskcustomersaddresses", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": "FlaskAddress.id", + "relationship": "BelongsTo", + "type": "Number", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "defaultValue": null, + "enums": null, + "field": "customer", + "inverseOf": "flaskcustomersaddresses", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "FlaskCustomer.id", + "relationship": "BelongsTo", + "type": "String", + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "id", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "Number", + "validations": [] + } + ] + }, + { + "name": "FlaskOrder", + "isVirtual": false, + "icon": null, + "isReadOnly": false, + "integration": null, + "isSearchable": true, + "onlyForRelationships": false, + "paginationType": "page", + "searchField": null, + "actions": [], + "segments": [], + "fields": [ + { + "defaultValue": null, + "enums": null, + "field": "amount", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "Number", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "defaultValue": null, + "enums": null, + "field": "billing_address", + "inverseOf": "flaskorder", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "FlaskAddress.id", + "relationship": "BelongsTo", + "type": "Number", + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "created_at", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "Date", + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "customer", + "inverseOf": "flaskorder", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "FlaskCustomer.id", + "relationship": "BelongsTo", + "type": "String", + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "delivering_address", + "inverseOf": "order_delivering_address_set", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "FlaskAddress.id", + "relationship": "BelongsTo", + "type": "Number", + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "flaskcart", + "inverseOf": "order", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "FlaskCart.id", + "relationship": "HasMany", + "type": [ + "Number" + ], + "validations": [] + }, + { + "defaultValue": null, + "enums": null, + "field": "id", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "Number", + "validations": [] + }, + { + "defaultValue": null, + "enums": [ + "PENDING", + "DISPATCHED", + "DELIVERED", + "REJECTED" + ], + "field": "status", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "type": "Enum", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 10, + "message": null + } + ] + } + ] + }, { "name": "Group", "isVirtual": false, @@ -2416,6 +3178,6 @@ "engine": "python", "engine_version": "3.10.11" }, - "schemaFileHash": "8c5ecc3606129fe4a61419cedc3fc5e64c1fc908" + "schemaFileHash": "a3bc8d0cbc9a4339c6b2fe0f9ca20c3df7d80f0d" } } \ No newline at end of file diff --git a/src/_example/django/django_demo/app/flask_models.py b/src/_example/django/django_demo/app/flask_models.py new file mode 100644 index 000000000..83aa6d133 --- /dev/null +++ b/src/_example/django/django_demo/app/flask_models.py @@ -0,0 +1,79 @@ +# This is an auto-generated Django model module. +# You'll have to do the following manually to clean this up: +# * Rearrange models' order +# * Make sure each model has one field with primary_key=True +# * Make sure each ForeignKey and OneToOneField has `on_delete` set to the desired behavior +# * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table +# Feel free to rename the models, but don't rename db_table values or field names. +from django.db import models + + +class FlaskAddress(models.Model): + id = models.AutoField(primary_key=True, db_column="pk") + street = models.CharField(max_length=255) + street_number = models.CharField(max_length=2, blank=True, null=True) + city = models.CharField(max_length=255) + country = models.CharField(max_length=255) + zip_code = models.CharField(max_length=5) + # customers = models.ManyToManyField("FlaskCustomer", through="FlaskCustomersAddresses") + + class Meta: + # managed = False + db_table = "address" + + +class FlaskCustomer(models.Model): + id = models.BinaryField(primary_key=True, db_column="pk") + first_name = models.CharField(max_length=255) + last_name = models.CharField(max_length=255) + birthday_date = models.DateTimeField(blank=True, null=True) + age = models.IntegerField(blank=True, null=True) + is_vip = models.BooleanField(blank=True, null=True) + avatar = models.BinaryField(blank=True, null=True) + addresses = models.ManyToManyField("FlaskAddress", through="FlaskCustomersAddresses", related_name="customers") + + class Meta: + # managed = False + db_table = "customer" + + +class FlaskCustomersAddresses(models.Model): + customer = models.ForeignKey(FlaskCustomer, models.DO_NOTHING) + address = models.ForeignKey(FlaskAddress, models.DO_NOTHING) + + class Meta: + # managed = False + db_table = "customers_addresses" + + +class FlaskOrder(models.Model): + class OrderStatus(models.TextChoices): + PENDING = ("PENDING", "Pending") + DISPATCHED = ("DISPATCHED", "Dispatched") + DELIVERED = ("DELIVERED", "Delivered") + REJECTED = ("REJECTED", "Rejected") + + id = models.AutoField(primary_key=True, db_column="pk") + created_at = models.DateTimeField(blank=True, null=True) + amount = models.IntegerField() + customer = models.ForeignKey(FlaskCustomer, models.DO_NOTHING, blank=True, null=True) + billing_address = models.ForeignKey(FlaskAddress, models.DO_NOTHING, blank=True, null=True) + delivering_address = models.ForeignKey( + FlaskAddress, models.DO_NOTHING, related_name="order_delivering_address_set", blank=True, null=True + ) + status = models.CharField(max_length=10, choices=OrderStatus.choices) + + class Meta: + # managed = False + db_table = "order" + + +class FlaskCart(models.Model): + id = models.AutoField(primary_key=True, db_column="pk") + name = models.CharField(max_length=255) + created_at = models.DateTimeField(blank=True, null=True) + order = models.ForeignKey("FlaskOrder", models.DO_NOTHING, blank=True, null=True) + + class Meta: + # managed = False + db_table = "cart" diff --git a/src/_example/django/django_demo/app/management/commands/populate-db.py b/src/_example/django/django_demo/app/management/commands/populate-db.py index 87bb4c9e4..0747142af 100644 --- a/src/_example/django/django_demo/app/management/commands/populate-db.py +++ b/src/_example/django/django_demo/app/management/commands/populate-db.py @@ -1,6 +1,8 @@ import random from datetime import datetime, timezone +from uuid import uuid4 +from app.flask_models import FlaskAddress, FlaskCart, FlaskCustomer, FlaskCustomersAddresses, FlaskOrder from app.models import Address, Cart, Customer, CustomerAddress, Order from django.contrib.auth.models import Group, User from django.core.management.base import BaseCommand @@ -19,6 +21,17 @@ def add_arguments(self, parser): help=f"create a lot of data, can take a while(~=5min)). Open this file ({__file__}) to edit the values", action="store_true", ) + parser.add_argument( + "-o", + "--only-other-database", + help="only populate 'other' database", + action="store_true", + ) + parser.add_argument( + "--only-default-database", + help="don't populate 'other' database", + action="store_true", + ) def handle(self, *args, **options): numbers = { @@ -37,21 +50,30 @@ def handle(self, *args, **options): "orders_carts": 3000000, } - users, groups = create_users_groups(numbers["groups"], numbers["users"]) - if options["verbosity"] != 0: - print(f"users({numbers['users']}) and groups({numbers['groups']}) created ") + if options["only_default_database"] or not options["only_other_database"]: + users, groups = create_users_groups(numbers["groups"], numbers["users"]) + if options["verbosity"] != 0: + print(f"users({numbers['users']}) and groups({numbers['groups']}) created ") - customers = create_customers(numbers["customers"]) - if options["verbosity"] != 0: - print(f"customers({numbers['customers']}) created") + customers = create_customers(numbers["customers"]) + if options["verbosity"] != 0: + print(f"customers({numbers['customers']}) created") - addresses = create_addresses(customers, numbers["addresses"]) - if options["verbosity"] != 0: - print(f"addresses({numbers['addresses']}) created") + addresses = create_addresses(customers, numbers["addresses"]) + if options["verbosity"] != 0: + print(f"addresses({numbers['addresses']}) created") - orders, carts = create_orders_cart(customers, addresses, numbers["orders_carts"]) - if options["verbosity"] != 0: - print(f"orders and carts ({numbers['orders_carts']}) created") + orders, carts = create_orders_cart(customers, addresses, numbers["orders_carts"]) + if options["verbosity"] != 0: + print(f"orders and carts ({numbers['orders_carts']}) created") + + if not options["only_default_database"] or options["only_other_database"]: + customers = populate_flask_customers(numbers["customers"]) + addresses = populate_flask_addresses(customers) + populate_orders(addresses) + + +# main db def create_users_groups(nb_group=4, nb_users=10): @@ -85,7 +107,11 @@ def create_users_groups(nb_group=4, nb_users=10): def create_customers(nb_customers=500): customers = [] for i in range(nb_customers): - c = Customer(first_name=fake.first_name(), last_name=fake.last_name(), birthday_date=fake.date_of_birth()) + c = Customer( + first_name=fake.first_name(), + last_name=fake.last_name(), + birthday_date=fake.date_of_birth(tzinfo=timezone.utc), + ) customers.append(c) Customer.objects.bulk_create(customers) customers = Customer.objects.all() @@ -139,3 +165,64 @@ def create_orders_cart(customers, addresses, nb_order=1000): Cart.objects.bulk_create(carts) carts = Cart.objects.all() return orders, carts + + +# other db + + +def populate_flask_customers(nb: int = 500): + FlaskCustomer.objects.bulk_create( + [ + FlaskCustomer( + id=str(uuid4()).encode("utf-8"), + first_name=fake.first_name(), + last_name=fake.last_name(), + birthday_date=datetime.fromordinal(fake.date_of_birth().toordinal()).replace(tzinfo=timezone.utc), + age=random.choices([None, random.randint(16, 120)])[0], + is_vip=random.choice([True, False]), + ) + for i in range(nb) + ], + ) + return FlaskCustomer.objects.all() + + +def populate_flask_addresses(customers): + addresses = [] + for _ in range(0, customers.count() * 2): + address = FlaskAddress( + street=fake.street_address(), city=fake.city(), country=fake.country(), zip_code=fake.postcode() + ) + addresses.append(address) + FlaskAddress.objects.bulk_create(addresses) + addresses = FlaskAddress.objects.all() + + customers_addresses = [] + for address in addresses: + known_customer = set() + for _ in range(1, random.randint(2, 4)): + customer = random.choices(customers)[0] + if customer not in known_customer: + known_customer.add(customer) + customers_addresses.append(FlaskCustomersAddresses(address=address, customer=customer)) + FlaskCustomersAddresses.objects.bulk_create(customers_addresses) + return addresses + + +def populate_orders(addresses): + orders = [] + for address in addresses: + o = FlaskOrder( + amount=random.randint(10, 10000), + created_at=fake.date_time_between_dates(datetime(2021, 1, 1), datetime.utcnow(), tzinfo=timezone.utc), + customer=address.customers.all()[0], + billing_address=address, + delivering_address=address, + status=random.choice(list(FlaskOrder.OrderStatus)), + ) + orders.append(o) + FlaskOrder.objects.bulk_create(orders) + FlaskCart.objects.bulk_create( + [FlaskCart(name=fake.language_name(), order_id=order.pk) for order in FlaskOrder.objects.all()] + ) + return orders diff --git a/src/_example/django/django_demo/app/migrations/0002_flaskaddress_flaskcustomer_flaskorder_and_more.py b/src/_example/django/django_demo/app/migrations/0002_flaskaddress_flaskcustomer_flaskorder_and_more.py new file mode 100644 index 000000000..4a77d4d63 --- /dev/null +++ b/src/_example/django/django_demo/app/migrations/0002_flaskaddress_flaskcustomer_flaskorder_and_more.py @@ -0,0 +1,171 @@ +# Generated by Django 4.2.7 on 2023-11-28 15:12 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("app", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="FlaskAddress", + fields=[ + ( + "id", + models.AutoField(db_column="pk", primary_key=True, serialize=False), + ), + ("street", models.CharField(max_length=255)), + ( + "street_number", + models.CharField(blank=True, max_length=2, null=True), + ), + ("city", models.CharField(max_length=255)), + ("country", models.CharField(max_length=255)), + ("zip_code", models.CharField(max_length=5)), + ], + options={ + "db_table": "address", + }, + ), + migrations.CreateModel( + name="FlaskCustomer", + fields=[ + ( + "id", + models.BinaryField( + db_column="pk", primary_key=True, serialize=False + ), + ), + ("first_name", models.CharField(max_length=255)), + ("last_name", models.CharField(max_length=255)), + ("birthday_date", models.DateTimeField(blank=True, null=True)), + ("age", models.IntegerField(blank=True, null=True)), + ("is_vip", models.BooleanField(blank=True, null=True)), + ("avatar", models.BinaryField(blank=True, null=True)), + ], + options={ + "db_table": "customer", + }, + ), + migrations.CreateModel( + name="FlaskOrder", + fields=[ + ( + "id", + models.AutoField(db_column="pk", primary_key=True, serialize=False), + ), + ("created_at", models.DateTimeField(blank=True, null=True)), + ("amount", models.IntegerField()), + ( + "status", + models.CharField( + choices=[ + ("PENDING", "Pending"), + ("DISPATCHED", "Dispatched"), + ("DELIVERED", "Delivered"), + ("REJECTED", "Rejected"), + ], + max_length=10, + ), + ), + ( + "billing_address", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + to="app.flaskaddress", + ), + ), + ( + "customer", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + to="app.flaskcustomer", + ), + ), + ( + "delivering_address", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="order_delivering_address_set", + to="app.flaskaddress", + ), + ), + ], + options={ + "db_table": "order", + }, + ), + migrations.CreateModel( + name="FlaskCustomersAddresses", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "address", + models.ForeignKey( + on_delete=django.db.models.deletion.DO_NOTHING, + to="app.flaskaddress", + ), + ), + ( + "customer", + models.ForeignKey( + on_delete=django.db.models.deletion.DO_NOTHING, + to="app.flaskcustomer", + ), + ), + ], + options={ + "db_table": "customers_addresses", + }, + ), + migrations.AddField( + model_name="flaskcustomer", + name="addresses", + field=models.ManyToManyField( + related_name="customers", + through="app.FlaskCustomersAddresses", + to="app.flaskaddress", + ), + ), + migrations.CreateModel( + name="FlaskCart", + fields=[ + ( + "id", + models.AutoField(db_column="pk", primary_key=True, serialize=False), + ), + ("name", models.CharField(max_length=255)), + ("created_at", models.DateTimeField(blank=True, null=True)), + ( + "order", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + to="app.flaskorder", + ), + ), + ], + options={ + "db_table": "cart", + }, + ), + ] diff --git a/src/_example/django/django_demo/app/models.py b/src/_example/django/django_demo/app/models.py index a3ed4ee93..3f850b646 100644 --- a/src/_example/django/django_demo/app/models.py +++ b/src/_example/django/django_demo/app/models.py @@ -1,5 +1,6 @@ from datetime import date +from app.flask_models import * # noqa:F401,F403 from django.db import models """ diff --git a/src/_example/django/django_demo/django_demo/db_router.py b/src/_example/django/django_demo/django_demo/db_router.py new file mode 100644 index 000000000..b9320d337 --- /dev/null +++ b/src/_example/django/django_demo/django_demo/db_router.py @@ -0,0 +1,50 @@ +from django.db import models + + +class DBRouter: + """ + A router to control all database operations on models in the + auth and contenttypes applications. + """ + + route_app_labels = {"auth", "contenttypes"} + + def db_for_read(self, model, **hints): + """ + Attempts to read auth and contenttypes models go to auth_db. + """ + if model.__name__.startswith("Flask"): + return "other" + return "default" + + def db_for_write(self, model, **hints): + """ + Attempts to write auth and contenttypes models go to auth_db. + """ + if model.__name__.startswith("Flask"): + return "other" + return "default" + + def allow_relation(self, obj1: models.Model, obj2: models.Model, **hints): + """ + Allow relations if a model in the auth or contenttypes apps is + involved. + """ + obj1_is_flask = obj1.__class__.__name__.startswith("Flask") + obj2_is_flask = obj2.__class__.__name__.startswith("Flask") + return (obj1_is_flask and obj2_is_flask) or (not obj1_is_flask and not obj2_is_flask) + + def allow_migrate(self, db, app_label, model_name=None, **hints): + """ + Make sure the auth and contenttypes apps only appear in the + 'auth_db' database. + """ + # return True + # print("db, app_label, model_name, hints") + # print(db, app_label, model_name, hints) + allow_migrate = model_name is not None and ( + (db == "default" and not model_name.startswith("flask")) + or (db == "other" and model_name.startswith("flask")) + ) + # print(db, model_name, allow_migrate) + return allow_migrate diff --git a/src/_example/django/django_demo/django_demo/settings.py b/src/_example/django/django_demo/django_demo/settings.py index 20dc793cb..9b33dd8e9 100644 --- a/src/_example/django/django_demo/django_demo/settings.py +++ b/src/_example/django/django_demo/django_demo/settings.py @@ -103,9 +103,14 @@ "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": os.path.join(BASE_DIR, "db.sqlite3"), - } + }, + "other": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": os.path.join(BASE_DIR, "db_flask_example.sqlite"), + }, } +DATABASE_ROUTERS = ["django_demo.db_router.DBRouter"] # Password validation # https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators diff --git a/src/_example/django/pyproject.toml b/src/_example/django/pyproject.toml index 34e29479c..57247117d 100644 --- a/src/_example/django/pyproject.toml +++ b/src/_example/django/pyproject.toml @@ -36,7 +36,7 @@ type = "simple" [tool.poe.tasks] init-db = { shell = "cd django_demo && python manage.py migrate" } -populate-db = { shell = "cd django_demo && python manage.py populate-db" } +populate-db = { shell = "cd django_demo && python manage.py populate-db && python manage.py migrate --database=other" } runserver = { shell = "cd django_demo && python manage.py runserver" } [tool.poetry.group.dev.dependencies]