Skip to content

Commit

Permalink
feat(django): add database operations and routes (#145)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbarreau authored Nov 20, 2023
1 parent 2d050a1 commit 95f3a94
Show file tree
Hide file tree
Showing 43 changed files with 1,371 additions and 221 deletions.
117 changes: 90 additions & 27 deletions src/_example/django/django_demo/.forestadmin-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,22 @@
"type": "Date",
"validations": []
},
{
"defaultValue": null,
"enums": null,
"field": "customer_id",
"integration": null,
"inverseOf": null,
"isFilterable": true,
"isPrimaryKey": false,
"isReadOnly": true,
"isRequired": false,
"isSortable": false,
"isVirtual": false,
"reference": null,
"type": "Number",
"validations": []
},
{
"defaultValue": null,
"enums": null,
Expand Down Expand Up @@ -640,6 +656,58 @@
],
"validations": []
},
{
"defaultValue": null,
"enums": null,
"field": "smart_billing_addresses",
"inverseOf": null,
"isFilterable": false,
"isPrimaryKey": false,
"isReadOnly": false,
"isRequired": false,
"isSortable": true,
"isVirtual": false,
"reference": "Address.id",
"relationship": "BelongsToMany",
"type": [
"Number"
],
"validations": []
},
{
"defaultValue": null,
"enums": null,
"field": "smart_carts",
"inverseOf": null,
"isFilterable": false,
"isPrimaryKey": false,
"isReadOnly": false,
"isRequired": false,
"isSortable": true,
"isVirtual": false,
"reference": "Cart.id",
"relationship": "HasMany",
"type": [
"Number"
],
"validations": []
},
{
"defaultValue": null,
"enums": null,
"field": "total_spending",
"integration": null,
"inverseOf": null,
"isFilterable": false,
"isPrimaryKey": false,
"isReadOnly": true,
"isRequired": false,
"isSortable": false,
"isVirtual": false,
"reference": null,
"type": "Number",
"validations": []
},
{
"defaultValue": null,
"enums": null,
Expand Down Expand Up @@ -1002,18 +1070,12 @@
"isFilterable": true,
"isPrimaryKey": false,
"isReadOnly": false,
"isRequired": true,
"isRequired": false,
"isSortable": true,
"isVirtual": false,
"reference": null,
"type": "String",
"validations": [
{
"type": "is present",
"value": null,
"message": null
}
]
"validations": []
},
{
"defaultValue": null,
Expand Down Expand Up @@ -1257,6 +1319,22 @@
"type": "Number",
"validations": []
},
{
"defaultValue": null,
"enums": null,
"field": "ordered_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": [
Expand Down Expand Up @@ -1646,17 +1724,12 @@
"isFilterable": true,
"isPrimaryKey": false,
"isReadOnly": false,
"isRequired": true,
"isRequired": false,
"isSortable": true,
"isVirtual": false,
"reference": null,
"type": "String",
"validations": [
{
"type": "is present",
"value": null,
"message": null
},
{
"type": "is shorter than",
"value": 254,
Expand All @@ -1673,17 +1746,12 @@
"isFilterable": true,
"isPrimaryKey": false,
"isReadOnly": false,
"isRequired": true,
"isRequired": false,
"isSortable": true,
"isVirtual": false,
"reference": null,
"type": "String",
"validations": [
{
"type": "is present",
"value": null,
"message": null
},
{
"type": "is shorter than",
"value": 150,
Expand Down Expand Up @@ -1798,17 +1866,12 @@
"isFilterable": true,
"isPrimaryKey": false,
"isReadOnly": false,
"isRequired": true,
"isRequired": false,
"isSortable": true,
"isVirtual": false,
"reference": null,
"type": "String",
"validations": [
{
"type": "is present",
"value": null,
"message": null
},
{
"type": "is shorter than",
"value": 150,
Expand Down Expand Up @@ -2066,6 +2129,6 @@
"engine": "python",
"engine_version": "3.10.11"
},
"schemaFileHash": "54ffd6489cfa2209d94d1a5bcb6b517e52c4d84e"
"schemaFileHash": "7918f978695f6bd166bbca20ca9de57f53a49a08"
}
}
10 changes: 9 additions & 1 deletion src/_example/django/django_demo/app/apps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
from django.apps import AppConfig
from app.forest_admin import customize_forest
from django.apps import AppConfig, apps
from forestadmin.django_agent.agent import DjangoAgent


class AppConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "app"

def ready(self) -> None:
agent: DjangoAgent = apps.get_app_config("django_agent").get_agent()
if agent:
customize_forest(agent)
agent.start()
Empty file.
33 changes: 33 additions & 0 deletions src/_example/django/django_demo/app/forest/customer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from forestadmin.datasource_toolkit.context.collection_context import CollectionCustomizationContext
from forestadmin.datasource_toolkit.interfaces.fields import Operator
from forestadmin.datasource_toolkit.interfaces.query.aggregation import Aggregation
from forestadmin.datasource_toolkit.interfaces.query.condition_tree.nodes.leaf import ConditionTreeLeaf
from forestadmin.datasource_toolkit.interfaces.query.filter.unpaginated import Filter


async def get_customer_spent_values(records, context: CollectionCustomizationContext):
record_ids = [record["id"] for record in records]
condition = Filter(
{"condition_tree": ConditionTreeLeaf(field="customer_id", operator=Operator.IN, value=record_ids)}
)

aggregation = Aggregation(
{
"operation": "Sum",
"field": "amount",
"groups": [
{
"field": "customer_id",
}
],
},
)
rows = await context.datasource.get_collection("Order").aggregate(context.caller, condition, aggregation)
# rows = await context.datasource.get_collection("order").aggregate(context.caller, condition, aggregation)
ret = []
for record in records:
filtered = [*filter(lambda r: r["group"]["customer_id"] == record["id"], rows)]
row = filtered[0] if len(filtered) > 0 else {}
ret.append(row.get("value", 0))

return ret
25 changes: 25 additions & 0 deletions src/_example/django/django_demo/app/forest_admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from app.forest.customer import get_customer_spent_values
from forestadmin.datasource_toolkit.interfaces.fields import PrimitiveType
from forestadmin.django_agent.agent import DjangoAgent


def customize_forest(agent: DjangoAgent):
agent.customize_collection("Cart").add_field(
"customer_id",
{
"dependencies": ["order:customer_id"],
"column_type": PrimitiveType.NUMBER,
"get_values": lambda records, context: [rec["order"]["customer_id"] for rec in records],
},
).emulate_field_filtering("customer_id")
agent.customize_collection("Customer").add_field(
"total_spending",
{"column_type": PrimitiveType.NUMBER, "dependencies": ["id"], "get_values": get_customer_spent_values},
).add_one_to_many_relation("smart_carts", "Cart", "customer_id").add_many_to_many_relation(
# relations
"smart_billing_addresses",
"Address",
"Order",
"customer_id",
"billing_address_id",
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import random
from datetime import datetime, timezone

from app.models import Address, Cart, Customer, Order
from django.contrib.auth.models import Group, User
Expand Down Expand Up @@ -72,6 +73,7 @@ def create_orders_cart(customers, addresses, nb_order=1000):

for i in range(nb_order):
o = Order(
ordered_at=fake.date_time_between(start_date=datetime(2015, 1, 1), tzinfo=timezone.utc),
amount=random.randint(0, 100000) / 100,
customer=customers[i % (len(customers) - 1)],
billing_address=addresses[i % (len(addresses) - 1)],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 4.2.7 on 2023-11-10 09:15
# Generated by Django 4.2.7 on 2023-11-14 17:40

import datetime
from django.db import migrations, models
Expand Down Expand Up @@ -82,6 +82,7 @@ class Migration(migrations.Migration):
max_length=10,
),
),
("ordered_at", models.DateTimeField(null=True)),
(
"billing_address",
models.ForeignKey(
Expand Down
1 change: 1 addition & 0 deletions src/_example/django/django_demo/app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class OrderStatus(models.TextChoices):
billing_address = models.ForeignKey(Address, related_name="billing_orders", on_delete=models.CASCADE)
delivering_address = models.ForeignKey(Address, related_name="delivering_orders", on_delete=models.CASCADE)
status = models.CharField(max_length=10, choices=OrderStatus.choices)
ordered_at = models.DateTimeField(null=True)
# cart


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from forestadmin.agent_toolkit.utils.context_variable_injector import ContextVariableInjector
from forestadmin.agent_toolkit.utils.context_variable_instantiator import ContextVariablesInstantiator
from forestadmin.datasource_toolkit.exceptions import ForestException
from forestadmin.datasource_toolkit.interfaces.fields import Column, PrimitiveType
from forestadmin.datasource_toolkit.interfaces.query.aggregation import Aggregation
from forestadmin.datasource_toolkit.interfaces.query.condition_tree.factory import ConditionTreeFactory
from forestadmin.datasource_toolkit.interfaces.query.condition_tree.nodes.base import ConditionTree
Expand Down Expand Up @@ -124,10 +123,6 @@ async def pie(self, request: RequestCollection) -> Response:
results: List[Dict[str, Union[str, int]]] = []
for row in rows:
key = row["group"][request.body["groupByFieldName"]]
if ":" not in request.body["groupByFieldName"]:
field = request.collection.get_field(request.body["groupByFieldName"])
if cast(Column, field)["column_type"] == PrimitiveType.ENUM:
key = key.value
results.append({"key": key, "value": row["value"]})
return self._build_success_response(results)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ async def _get_collection_permissions_data(self, force_fetch: bool = False):
del self.cache["forest.collections"]

if "forest.collections" not in self.cache:
ForestLogger.log("Debug", "Fetching environment permissions")
ForestLogger.log("debug", "Fetching environment permissions")
response = await ForestHttpApi.get_environment_permissions(self.options)
collections = {}
for name, collection in response["collections"].items():
Expand Down
Loading

0 comments on commit 95f3a94

Please sign in to comment.