{"id":23977972,"url":"https://github.com/ertgl/django-xformula","last_synced_at":"2025-10-14T23:39:07.136Z","repository":{"id":105041337,"uuid":"565258457","full_name":"ertgl/django-xformula","owner":"ertgl","description":"Django query evaluator, built on top of XFormula language front-end.","archived":false,"fork":false,"pushed_at":"2025-06-19T00:12:39.000Z","size":436,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-30T07:56:42.585Z","etag":null,"topics":["django","domain-specific-language","dynamic-filtering","python","query"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ertgl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-11-12T20:34:37.000Z","updated_at":"2025-06-19T00:12:35.000Z","dependencies_parsed_at":null,"dependency_job_id":"266c072c-b84d-4c10-8430-b27618efb87d","html_url":"https://github.com/ertgl/django-xformula","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ertgl/django-xformula","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ertgl%2Fdjango-xformula","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ertgl%2Fdjango-xformula/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ertgl%2Fdjango-xformula/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ertgl%2Fdjango-xformula/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ertgl","download_url":"https://codeload.github.com/ertgl/django-xformula/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ertgl%2Fdjango-xformula/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273453473,"owners_count":25108470,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-03T02:00:09.631Z","response_time":76,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["django","domain-specific-language","dynamic-filtering","python","query"],"created_at":"2025-01-07T08:15:51.630Z","updated_at":"2025-10-14T23:39:07.105Z","avatar_url":"https://github.com/ertgl.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# django-xformula\n\nA dynamic formula-to-Python and formula-to-SQL evaluator for\n[Django](https://www.djangoproject.com/) applications.\n\n## Table of Contents\n\n- [Overview](#overview)\n  - [Use Cases](#use-cases)\n  - [Features](#features)\n    - [Bidirectional Operators](#bidirectional-operators)\n    - [Zero Built-in Variables by Default](#zero-built-in-variables-by-default)\n    - [Customizable Attribute Getter](#customizable-attribute-getter)\n    - [Customizable Function Caller](#customizable-function-caller)\n- [Installation](#installation)\n- [Usage](#usage)\n- [Operators](#operators)\n- [Syntax](#syntax)\n- [Troubleshooting](#troubleshooting)\n- [License](#license)\n\n## Overview\n\ndjango-xformula is a flexible query evaluator built for Django applications. It\nuses the default syntax grammar and parser generated by the\n[XFormula](https://github.com/ertgl/xformula) project, to translate formulas\ninto Django ORM query expressions. With its dynamic evaluation engine, it\nbecomes very easy to design a DSL (domain-specific language) for defining,\nstoring, and loading complex queries, opening the door to a wide range of\nadvanced use cases.\n\n### Use Cases\n\nHere are some typical ways django-xformula can be used:\n\n- **Open APIs**: Let external clients or integrations filter, annotate, or\n  aggregate data with combinable formulas.\n- **Authorization Rules**: Store and enforce query-based authorization rules\n  directly in the database.\n- **Dynamic Business Rules**: Update business rules on the fly without\n  redeploying, giving departments control over their own rules.\n- **Personalized Experiences**: Allow end users to adjust how they interact\n  with your app, such as pre-filtering, sorting, or pre-defining conditions for\n  webhooks.\n\nOf course, these are just examples. Any situation where you need dynamic query\nevaluation can benefit.\n\n### Features\n\ndjango-xformula focuses on being minimal but powerful, giving you the building\nblocks to create query evaluators suited to your needs.\n\n#### Bidirectional Operators\n\nThe same syntax works for both Python and Django query evaluations. Expressions\nare interpreted based on context:\n\n- With a `QuerySet`, evaluation returns a `QuerySet`.\n- With a `Q` object, evaluation produces a `Q`.\n- With a `Combinable` or `Field`, it's processed as a `Combinable`.\n- With a model instance, evaluation returns a `Value` holding the instance's\n  primary key.\n- Otherwise, expressions fall back to standard Python rules.\n\nThis makes it possible to use a formula both as a database query and as a\nPython expression. It is particularly useful in scenarios such as filtering\ndata in GraphQL subscriptions, etc.\n\n#### Zero Built-in Variables by Default\n\nBy default, any undefined variable in a formula is treated as a Django `F`\nobject. This ensures that only approved variables and functions are used in\nquery evaluations, preventing potential security risks.\n\n#### Customizable Attribute Getter\n\nDirect attribute access is blocked by default. Attempting it raises a\n`ForbiddenAttribute` error (a subclass of Django's `PermissionDenied`). You can\ncustomize this to allow safe attributes, adding a strong security layer.\n\n#### Customizable Function Caller\n\nFunction calls are also restricted by default. Unauthorized calls raise a\n`ForbiddenCall` error (also a subclass of `PermissionDenied`). You can\nconfigure the evaluator to only allow specific functions, balancing flexibility\nwith safety.\n\n## Installation\n\ndjango-xformula is available on PyPI and can be installed with pip or any other\ncompatible package manager:\n\n```sh\npip install django-xformula\n```\n\n## Usage\n\nHere's a simple example of using django-xformula to filter a queryset with a\nuser-supplied formula:\n\n```py\nfrom operator import call\n\nfrom django.db.models import Q, QuerySet\nfrom django.db.models.functions import Length\nfrom django_xformula import QueryEvaluator\n\n# Import your models.\nfrom myapp.models import MyModel\n\n\nquery_evaluator = QueryEvaluator()\n\n\ndef resource_view(request):\n    query = request.GET.get(\"q\", \"\")\n    context = QueryEvaluator.Context(\n      # Provide Python objects to the formula context.\n      builtins={\n        \"Length\": Length,\n        \"me\": request.user,\n      },\n      # WARNING: Allowing arbitrary function calls is unsafe.\n      # Use a secure function caller (e.g., whitelist checking).\n      call=call,\n      # WARNING: Direct attribute access is unsafe.\n      # Restrict allowed attributes through a secure getter (e.g., whitelist\n      # checking per object type).\n      getattr=getattr,\n    )\n    q_or_result = query_evaluator.evaluate(q, context)\n    if isinstance(q_or_result, QuerySet):\n        return render_table(q_or_result)\n    if isinstance(q_or_result, Q):\n        queryset = MyModel.objects.filter(q_or_result)\n        return render_table(queryset)\n    # If the formula does not represent a database query,\n    # return the result of the evaluated expression (e.g., \"1 + 1\").\n    return render_result(q_or_result)\n```\n\nSample formulas you can use with the built-ins defined in the example above:\n\n- Records where `owner` is the current user:\n\n  ```python\n  owner is me\n  ```\n\n- Records where `owner` is not the current user:\n\n  ```python\n  owner is not me\n  ```\n\n- Records where `name` is longer than 5 characters:\n\n  ```python\n  Length(name) \u003e 5\n  ```\n\n- Records accessible if the user is staff, the owner, or the record is public:\n\n  ```python\n  me.is_staff or me is owner or is_public\n  ```\n\n- Records where `version` is the current version, but only if a condition\n  passes first:\n\n  ```python\n  check_condition() and version is CURRENT_VERSION\n  ```\n\n### Operators\n\nFor a full list of supported operators, see the\n[XFormula default precedence list](https://github.com/ertgl/xformula/blob/main/src/xformula/syntax/core/operations/default_operator_precedences.py#L16).\n\n### Syntax\n\nThe XFormula project provides a default syntax, where the top-level\nnon-terminal type is `expression`. django-xformula uses this syntax by default.  \nIf customization is needed, you can find the relevant examples in the XFormula\n[README](https://github.com/ertgl/xformula) and in its\n[features](https://github.com/ertgl/xformula/tree/4e1e3a88200ccbe22d8aa477001d9af68dd91ab4/src/xformula/syntax/core/features).\n\nYou can also check the EBNF grammar of the default syntax in the\n[out/Grammar.lark](https://github.com/ertgl/xformula/blob/main/out/Grammar.lark)\nfile.\n\n### Troubleshooting\n\nSome common issues you might run into:\n\n- **`ForbiddenAttribute` error**: The formula is trying to access an attribute\n  that isn't allowed. Update your custom attribute getter to allow it.\n- **`ForbiddenCall` error**: The formula is trying to call a function that\n  isn't allowed. Update your custom function caller to whitelist the function.\n- **Invalid query syntax**: The formula doesn't match XFormula grammar.\n  Double-check the syntax.\n- **Unsupported database function**: The database backend doesn't support the\n  function you're trying to use. Verify\n  [backend capabilities](https://docs.djangoproject.com/en/5.2/ref/databases/).\n\n## License\n\nThis project is licensed under the\n[MIT License](https://opensource.org/license/mit).\n\nSee the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fertgl%2Fdjango-xformula","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fertgl%2Fdjango-xformula","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fertgl%2Fdjango-xformula/lists"}