adjango


Nameadjango JSON
Version 0.3.1 PyPI version JSON
download
home_pagehttps://github.com/Artasov/adjango
SummaryA library with many features for interacting with Django
upload_time2024-12-13 11:30:32
maintainerNone
docs_urlNone
authorxlartas
requires_python>=3.8
licenseNone
keywords adjango django utils funcs features async managers services
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # 🚀 ADjango 

> Sometimes I use this in different projects, so I decided to put it on pypi

`ADjango` is a convenient library for simplifying work with Django, which offers various useful managers, services, decorators, utilities for asynchronous programming, a task scheduler for Celery, working with transactions and much more.
- [Installation](#installation-%EF%B8%8F)
- [Settings](#settings-%EF%B8%8F)
- [Overview](#overview)
  - [Manager & Services](#manager--services-%EF%B8%8F)
  - [Utils](#utils-)
  - [Decorators](#decorators-)
  - [Other](#other)

## Installation 🛠️
```bash
pip install adjango
```

## Settings ⚙️

* ### Add the application to the project.
    ```python
    INSTALLED_APPS = [
        #...
        'adjango',
    ]
    ```
* ### In `settings.py` set the params
    ```python
    # settings.py
    # None of the parameters are required.  
  
    # For usage @a/controller decorators
    LOGIN_URL = '/login/' 
  
    # optional
    ADJANGO_BACKENDS_APPS = BASE_DIR / 'apps' # for management commands
    ADJANGO_FRONTEND_APPS = BASE_DIR.parent / 'frontend' / 'src' / 'apps' # for management commands
    ADJANGO_APPS_PREPATH = 'apps.'  # if apps in BASE_DIR/apps/app1,app2...
    ADJANGO_UNCAUGHT_EXCEPTION_HANDLING_FUNCTION = ... # Read about @acontroller, @controller
    ADJANGO_CONTROLLERS_LOGGER_NAME = 'global' # only for usage @a/controller decorators
    ADJANGO_CONTROLLERS_LOGGING = True # only for usage @a/controller decorators
    ADJANGO_EMAIL_LOGGER_NAME = 'email' # for send_emails_task logging
    ```
    ```python
    MIDDLEWARE = [
        ...
        # add request.ip in views if u need
        'adjango.middleware.IPAddressMiddleware',  
        ...
    ]
    ```
## Overview
Most functions, if available in asynchronous form, are also available in synchronous form.

### Manager & Services 🛎️
A simple example and everything is immediately clear...
```python
from adjango.fields import AManyToManyField
from adjango.managers.base import AManager
from adjango.services.base import ABaseService
from adjango.models import AModel
from adjango.polymorphic_models import APolymorphicModel

class User(AbstractUser, ABaseService):
    objects = AManager()
# Its equal with...
class User(AbstractUser, AModel): pass
    

class Product(APolymorphicModel):
    # APolymorphicManager() of course here already exists
    name = CharField(max_length=100)

class Order(AModel):
    user = ForeignKey(User, CASCADE)
    products = AManyToManyField(Product)

    
# The following is now possible...
products = await Product.objects.aall()
products = await Product.objects.afilter(name='name')
# Returns an object or None if not found
order = await Order.objects.agetorn(id=69) # aget or none
if not order: raise

# We install products in the order
await order.products.aset(products)
# Or queryset right away...
await order.products.aset(
  Product.objects.filter(name='name') 
)
await order.products.aadd(products[0])

# We get the order again without associated objects
order: Order = await Order.objects.aget(id=69)
# Retrieve related objects asynchronously.
order.user = await order.related('user')
products = await order.products.aall()
# Works the same with intermediate processing/query filters
orders = await Order.objects.prefetch_related('products').aall()
for o in orders:
    for p in o.products.all():
        print(p.id)
#thk u
```
### Utils 🔧
  `aall`, `afilter`,  `arelated`, и так далее доступны как отдельные функции
  ```python
  from adjango.utils.funcs import aall, agetorn, afilter, aset, aadd, arelated
  ```
### Decorators 🎀
* `aforce_data`

    The `aforce_data` decorator combines data from the `GET`, `POST` and `JSON` body 
    request in `request.data`. This makes it easy to access all request data in one place.

* `atomic`

    An asynchronous decorator that wraps 
    function into a transactional context. If an exception occurs, all changes are rolled back.

* `acontroller/controller`

    An asynchronous decorator that wraps 
    function into a transactional context. If an exception occurs, all changes are rolled back.
    ```python
    from adjango.adecorators import acontroller

    @acontroller(name='My View', logger='custom_logger', log_name=True, log_time=True)
    async def my_view(request):
        pass
  
    @acontroller('One More View')
    async def my_view_one_more(request):
        pass
    ```
    * These decorators automatically catch uncaught exceptions and log if the logger is configured 
    `ADJANGO_CONTROLLERS_LOGGER_NAME` `ADJANGO_CONTROLLERS_LOGGING`. 
    * You can also implement the interface:
        ```python
        class IHandlerControllerException(ABC):
            @staticmethod
            @abstractmethod
            def handle(fn_name: str, request: WSGIRequest | ASGIRequest, e: Exception, *args, **kwargs) -> None:
                """
                An example of an exception handling function.
        
                @param fn_name: The name of the function where the exception occurred.
                @param request: The request object (WSGIRequest or ASGIRequest).
                @param e: The exception to be handled.
                @param args: Positional arguments passed to the function.
                @param kwargs: Named arguments passed to the function.
        
                @return: None
                """
                pass
        ```
        and use `handle` to get an uncaught exception:
        ```python
        # settings.py
        from adjango.handlers import HCE # use my example if u need
        ADJANGO_UNCAUGHT_EXCEPTION_HANDLING_FUNCTION = HCE.handle
        ```
    
### Other

* `AsyncAtomicContextManager`🧘

    An asynchronous context manager for working with transactions, which ensures the atomicity of operations.
    ```python
    from adjango.utils.base import AsyncAtomicContextManager
    
    async def some_function():
        async with AsyncAtomicContextManager():
            ...  
    ```

* `Tasker`📋

    The Tasker class provides methods for scheduling tasks in `Celery` and `Celery Beat`.
    ```python
    from adjango.utils.tasks import Tasker
    
    task_id = Tasker.put(
        task=my_celery_task,
        param1='value1',
        param2='value2',
        countdown=60 # The task will be completed in 60 seconds
    )
    ```
    ```python
    from adjango.utils.tasks import Tasker
    from datetime import datetime
    
    # One-time task via Celery Beat
    Tasker.beat(
        task=my_celery_task,
        name='one_time_task',
        schedule_time=datetime(2024, 10, 10, 14, 30), # Start the task on October 10, 2024 at 14:30
        param1='value1',
        param2='value2'
    )
    
    # Periodic task via Celery Beat (every hour)
    Tasker.beat(
        task=my_celery_task,
        name='hourly_task',
        interval=3600, # The task runs every hour
        param1='value1',
        param2='value2'
    )
    ```

* `send_emails`

    Allows you to send emails using templates and context rendering.
    ```python
    from adjango.utils.mail import send_emails
    
    send_emails(
        subject='Welcome!',
        emails=('user1@example.com', 'user2@example.com'),
        template='emails/welcome.html',
        context={'user': 'John Doe'}
    )
    ```
    ```python
    from adjango.tasks import send_emails_task
    from adjango.utils.tasks import Tasker
  
    send_emails_task.delay(
        subject='Hello!',
        emails=('user@example.com',),
        template='emails/hello.html',
        context={'message': 'Welcome to our service!'}
    )
    # or
    Tasker.put(
        task=send_emails_task,
        subject='Hello!',
        emails=('user@example.com',),
        template='emails/hello.html',
        context={'message': 'Welcome to our service!'},
        countdown=60 # The task will be completed in 5 seconds
    )
    ```

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/Artasov/adjango",
    "name": "adjango",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "adjango django utils funcs features async managers services",
    "author": "xlartas",
    "author_email": "ivanhvalevskey@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/d6/b0/5c9c1468df05f959c21fe1387707f942f821f31217d9fe49f9004a6faf78/adjango-0.3.1.tar.gz",
    "platform": null,
    "description": "# \ud83d\ude80 ADjango \r\n\r\n> Sometimes I use this in different projects, so I decided to put it on pypi\r\n\r\n`ADjango` is a convenient library for simplifying work with Django, which offers various useful managers, services, decorators, utilities for asynchronous programming, a task scheduler for Celery, working with transactions and much more.\r\n- [Installation](#installation-%EF%B8%8F)\r\n- [Settings](#settings-%EF%B8%8F)\r\n- [Overview](#overview)\r\n  - [Manager & Services](#manager--services-%EF%B8%8F)\r\n  - [Utils](#utils-)\r\n  - [Decorators](#decorators-)\r\n  - [Other](#other)\r\n\r\n## Installation \ud83d\udee0\ufe0f\r\n```bash\r\npip install adjango\r\n```\r\n\r\n## Settings \u2699\ufe0f\r\n\r\n* ### Add the application to the project.\r\n    ```python\r\n    INSTALLED_APPS = [\r\n        #...\r\n        'adjango',\r\n    ]\r\n    ```\r\n* ### In `settings.py` set the params\r\n    ```python\r\n    # settings.py\r\n    # None of the parameters are required.  \r\n  \r\n    # For usage @a/controller decorators\r\n    LOGIN_URL = '/login/' \r\n  \r\n    # optional\r\n    ADJANGO_BACKENDS_APPS = BASE_DIR / 'apps' # for management commands\r\n    ADJANGO_FRONTEND_APPS = BASE_DIR.parent / 'frontend' / 'src' / 'apps' # for management commands\r\n    ADJANGO_APPS_PREPATH = 'apps.'  # if apps in BASE_DIR/apps/app1,app2...\r\n    ADJANGO_UNCAUGHT_EXCEPTION_HANDLING_FUNCTION = ... # Read about @acontroller, @controller\r\n    ADJANGO_CONTROLLERS_LOGGER_NAME = 'global' # only for usage @a/controller decorators\r\n    ADJANGO_CONTROLLERS_LOGGING = True # only for usage @a/controller decorators\r\n    ADJANGO_EMAIL_LOGGER_NAME = 'email' # for send_emails_task logging\r\n    ```\r\n    ```python\r\n    MIDDLEWARE = [\r\n        ...\r\n        # add request.ip in views if u need\r\n        'adjango.middleware.IPAddressMiddleware',  \r\n        ...\r\n    ]\r\n    ```\r\n## Overview\r\nMost functions, if available in asynchronous form, are also available in synchronous form.\r\n\r\n### Manager & Services \ud83d\udece\ufe0f\r\nA simple example and everything is immediately clear...\r\n```python\r\nfrom adjango.fields import AManyToManyField\r\nfrom adjango.managers.base import AManager\r\nfrom adjango.services.base import ABaseService\r\nfrom adjango.models import AModel\r\nfrom adjango.polymorphic_models import APolymorphicModel\r\n\r\nclass User(AbstractUser, ABaseService):\r\n    objects = AManager()\r\n# Its equal with...\r\nclass User(AbstractUser, AModel): pass\r\n    \r\n\r\nclass Product(APolymorphicModel):\r\n    # APolymorphicManager() of course here already exists\r\n    name = CharField(max_length=100)\r\n\r\nclass Order(AModel):\r\n    user = ForeignKey(User, CASCADE)\r\n    products = AManyToManyField(Product)\r\n\r\n    \r\n# The following is now possible...\r\nproducts = await Product.objects.aall()\r\nproducts = await Product.objects.afilter(name='name')\r\n# Returns an object or None if not found\r\norder = await Order.objects.agetorn(id=69) # aget or none\r\nif not order: raise\r\n\r\n# We install products in the order\r\nawait order.products.aset(products)\r\n# Or queryset right away...\r\nawait order.products.aset(\r\n  Product.objects.filter(name='name') \r\n)\r\nawait order.products.aadd(products[0])\r\n\r\n# We get the order again without associated objects\r\norder: Order = await Order.objects.aget(id=69)\r\n# Retrieve related objects asynchronously.\r\norder.user = await order.related('user')\r\nproducts = await order.products.aall()\r\n# Works the same with intermediate processing/query filters\r\norders = await Order.objects.prefetch_related('products').aall()\r\nfor o in orders:\r\n    for p in o.products.all():\r\n        print(p.id)\r\n#thk u\r\n```\r\n### Utils \ud83d\udd27\r\n  `aall`, `afilter`,  `arelated`, \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u043a\u0430\u043a \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438\r\n  ```python\r\n  from adjango.utils.funcs import aall, agetorn, afilter, aset, aadd, arelated\r\n  ```\r\n### Decorators \ud83c\udf80\r\n* `aforce_data`\r\n\r\n    The `aforce_data` decorator combines data from the `GET`, `POST` and `JSON` body \r\n    request in `request.data`. This makes it easy to access all request data in one place.\r\n\r\n* `atomic`\r\n\r\n    An asynchronous decorator that wraps \r\n    function into a transactional context. If an exception occurs, all changes are rolled back.\r\n\r\n* `acontroller/controller`\r\n\r\n    An asynchronous decorator that wraps \r\n    function into a transactional context. If an exception occurs, all changes are rolled back.\r\n    ```python\r\n    from adjango.adecorators import acontroller\r\n\r\n    @acontroller(name='My View', logger='custom_logger', log_name=True, log_time=True)\r\n    async def my_view(request):\r\n        pass\r\n  \r\n    @acontroller('One More View')\r\n    async def my_view_one_more(request):\r\n        pass\r\n    ```\r\n    * These decorators automatically catch uncaught exceptions and log if the logger is configured \r\n    `ADJANGO_CONTROLLERS_LOGGER_NAME` `ADJANGO_CONTROLLERS_LOGGING`. \r\n    * You can also implement the interface:\r\n        ```python\r\n        class IHandlerControllerException(ABC):\r\n            @staticmethod\r\n            @abstractmethod\r\n            def handle(fn_name: str, request: WSGIRequest | ASGIRequest, e: Exception, *args, **kwargs) -> None:\r\n                \"\"\"\r\n                An example of an exception handling function.\r\n        \r\n                @param fn_name: The name of the function where the exception occurred.\r\n                @param request: The request object (WSGIRequest or ASGIRequest).\r\n                @param e: The exception to be handled.\r\n                @param args: Positional arguments passed to the function.\r\n                @param kwargs: Named arguments passed to the function.\r\n        \r\n                @return: None\r\n                \"\"\"\r\n                pass\r\n        ```\r\n        and use `handle` to get an uncaught exception:\r\n        ```python\r\n        # settings.py\r\n        from adjango.handlers import HCE # use my example if u need\r\n        ADJANGO_UNCAUGHT_EXCEPTION_HANDLING_FUNCTION = HCE.handle\r\n        ```\r\n    \r\n### Other\r\n\r\n* `AsyncAtomicContextManager`\ud83e\uddd8\r\n\r\n    An asynchronous context manager for working with transactions, which ensures the atomicity of operations.\r\n    ```python\r\n    from adjango.utils.base import AsyncAtomicContextManager\r\n    \r\n    async def some_function():\r\n        async with AsyncAtomicContextManager():\r\n            ...  \r\n    ```\r\n\r\n* `Tasker`\ud83d\udccb\r\n\r\n    The Tasker class provides methods for scheduling tasks in `Celery` and `Celery Beat`.\r\n    ```python\r\n    from adjango.utils.tasks import Tasker\r\n    \r\n    task_id = Tasker.put(\r\n        task=my_celery_task,\r\n        param1='value1',\r\n        param2='value2',\r\n        countdown=60 # The task will be completed in 60 seconds\r\n    )\r\n    ```\r\n    ```python\r\n    from adjango.utils.tasks import Tasker\r\n    from datetime import datetime\r\n    \r\n    # One-time task via Celery Beat\r\n    Tasker.beat(\r\n        task=my_celery_task,\r\n        name='one_time_task',\r\n        schedule_time=datetime(2024, 10, 10, 14, 30), # Start the task on October 10, 2024 at 14:30\r\n        param1='value1',\r\n        param2='value2'\r\n    )\r\n    \r\n    # Periodic task via Celery Beat (every hour)\r\n    Tasker.beat(\r\n        task=my_celery_task,\r\n        name='hourly_task',\r\n        interval=3600, # The task runs every hour\r\n        param1='value1',\r\n        param2='value2'\r\n    )\r\n    ```\r\n\r\n* `send_emails`\r\n\r\n    Allows you to send emails using templates and context rendering.\r\n    ```python\r\n    from adjango.utils.mail import send_emails\r\n    \r\n    send_emails(\r\n        subject='Welcome!',\r\n        emails=('user1@example.com', 'user2@example.com'),\r\n        template='emails/welcome.html',\r\n        context={'user': 'John Doe'}\r\n    )\r\n    ```\r\n    ```python\r\n    from adjango.tasks import send_emails_task\r\n    from adjango.utils.tasks import Tasker\r\n  \r\n    send_emails_task.delay(\r\n        subject='Hello!',\r\n        emails=('user@example.com',),\r\n        template='emails/hello.html',\r\n        context={'message': 'Welcome to our service!'}\r\n    )\r\n    # or\r\n    Tasker.put(\r\n        task=send_emails_task,\r\n        subject='Hello!',\r\n        emails=('user@example.com',),\r\n        template='emails/hello.html',\r\n        context={'message': 'Welcome to our service!'},\r\n        countdown=60 # The task will be completed in 5 seconds\r\n    )\r\n    ```\r\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "A library with many features for interacting with Django",
    "version": "0.3.1",
    "project_urls": {
        "Homepage": "https://github.com/Artasov/adjango",
        "Source": "https://github.com/Artasov/adjango",
        "Tracker": "https://github.com/Artasov/adjango/issues"
    },
    "split_keywords": [
        "adjango",
        "django",
        "utils",
        "funcs",
        "features",
        "async",
        "managers",
        "services"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "3f4bbb8e66d44a9b5cb50a381c30e26161cf2af97cf8ea7572557b9eb8008e6e",
                "md5": "4585da4e209e73429d4d2f8f3c35748b",
                "sha256": "7db0920a10eb7537dfd2e7acd9a90d0b813505d699c61d7c400fc5ffe4e395b4"
            },
            "downloads": -1,
            "filename": "adjango-0.3.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "4585da4e209e73429d4d2f8f3c35748b",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 38987,
            "upload_time": "2024-12-13T11:30:30",
            "upload_time_iso_8601": "2024-12-13T11:30:30.092500Z",
            "url": "https://files.pythonhosted.org/packages/3f/4b/bb8e66d44a9b5cb50a381c30e26161cf2af97cf8ea7572557b9eb8008e6e/adjango-0.3.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "d6b05c9c1468df05f959c21fe1387707f942f821f31217d9fe49f9004a6faf78",
                "md5": "1b2d3c184c30ae9074e00720613ffc33",
                "sha256": "f03c19d4a94ad8dfe04381b0e8ec34112110505fb263e42649c28472f0d684b2"
            },
            "downloads": -1,
            "filename": "adjango-0.3.1.tar.gz",
            "has_sig": false,
            "md5_digest": "1b2d3c184c30ae9074e00720613ffc33",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 30018,
            "upload_time": "2024-12-13T11:30:32",
            "upload_time_iso_8601": "2024-12-13T11:30:32.808774Z",
            "url": "https://files.pythonhosted.org/packages/d6/b0/5c9c1468df05f959c21fe1387707f942f821f31217d9fe49f9004a6faf78/adjango-0.3.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-12-13 11:30:32",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "Artasov",
    "github_project": "adjango",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "requirements": [],
    "lcname": "adjango"
}
        
Elapsed time: 0.40871s