Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify initial setup #230

Open
12 of 18 tasks
mofr opened this issue Aug 12, 2022 · 3 comments
Open
12 of 18 tasks

Simplify initial setup #230

mofr opened this issue Aug 12, 2022 · 3 comments
Labels
enhancement New feature or request

Comments

@mofr
Copy link
Collaborator

mofr commented Aug 12, 2022

Pain points (as shown by the winter-getting-started project):

Django-related:

  • Have to run django-admin startproject which generates a lot of visible boilerplate and clutter
    • manage.py
    • settings.py
    • urls.py
    • wsgi.py
  • Have to manually add rest_framework to INSTALLED_APPS
  • Have to call create_django_urls_for_package
  • Have to manually setup DEFAULT_RENDERER_CLASSES
  • Need to adjust ALLOWED_HOSTS before deployment
  • Non-trivial test fixture is required:
    • DJANGO_SETTINGS_MODULE
    • django.setup
    • LiveServerTestCase (replaced with httpx)
  • Complex openapi setup
    • Need to manually set SWAGGER_SETTINGS
    • Need to manually set STATIC_ROOT
    • Adding drf_yasg to INSTALLED_APPS
    • Setting up whitenoise
    • Have to write a lot of code for the most simple case

Not related to Django:

@mofr mofr added the enhancement New feature or request label Aug 12, 2022
@mofr mofr added this to the Simple start for new projects milestone Aug 12, 2022
@mofr
Copy link
Collaborator Author

mofr commented Aug 16, 2022

I'm trying to incapsulate django usage within winter_django package by creating a winter_django.create_wsgi function which sets up the application and returns ready-to-work WSGI instance.
https://github.com/WinterFramework/winter/tree/winter-django-wsgi
https://github.com/WinterFramework/winter-getting-started/tree/winter-django-wsgi

Temporarily missing functionality:

  • Broken deploy to Heroku (due to ALLOWED_HOSTS)
  • Broken Swagger UI (probably easier to migrate to apispec first)
  • Can't customize django settings

@mofr
Copy link
Collaborator Author

mofr commented Aug 20, 2022

OpenAPI resolution plan:

EDIT: Moved to a separate issue #241

@mofr
Copy link
Collaborator Author

mofr commented Oct 20, 2022

Better version of WinterWebApplication:

import winter.core
from injector import Injector


class WinterWebApplication:
    """
    Generic class to be incorporated into Winter framework.
    Performs basic autodiscovery and injector setup.
    Hides drf_yasg usage and simplifies the application setup.
    It's also supposed to absorb all global configurations from winter except annotations which are static.
    """

    class DjangoURLConf:
        def __init__(self):
            self.urlpatterns = []

    def __init__(self, routes_package: str, injector_modules):
        """
        :param routes_package: TODO Make it optional, discover the current directory by default
        :param injector_modules:
        """

        # It's public for the only purpose - to integrate it with existing Django app
        self.django_urlconf = self.DjangoURLConf()
        self._setup_django(self.django_urlconf)

        winter.core.set_injector(Injector(injector_modules))
        winter.web.setup()

        self._autodiscover_routes(routes_package)

    def _setup_django(self, urlconf: DjangoURLConf):
        from django.conf import settings

        settings.configure(
            ROOT_URLCONF=urlconf,
            REST_FRAMEWORK={
                'DEFAULT_RENDERER_CLASSES': ('winter_django.renderers.JSONRenderer',),
                'UNAUTHENTICATED_USER': object,
            },
            INSTALLED_APPS=(
                # Hack for making module discovery working
                'django.contrib.admin',
                'django.contrib.contenttypes',
                # End hack
            ),
            SWAGGER_SETTINGS={
                'DEFAULT_AUTO_SCHEMA_CLASS': 'winter_openapi.SwaggerAutoSchema',
            }
        )

        import django
        import winter_django
        import winter_openapi

        winter_django.setup()
        winter_openapi.setup(allow_missing_raises_annotation=True)
        django.setup()

    def _autodiscover_routes(self, package: str):
        import winter_django.autodiscovery
        self.django_urlconf.urlpatterns = winter_django.autodiscovery.create_django_urls_for_package(package)

    def get_wsgi(self):
        from django.core.wsgi import get_wsgi_application
        return get_wsgi_application()

    def get_openapi(self, title: str, format: str = 'json') -> str:
        from drf_yasg.generators import OpenAPISchemaGenerator
        from drf_yasg import openapi

        api_info = openapi.Info(title=title, default_version='v1')
        generator = OpenAPISchemaGenerator(api_info, urlconf=self.django_urlconf)
        schema = generator.get_schema()

        if format == 'json':
            from drf_yasg.codecs import OpenAPICodecJson
            codec = OpenAPICodecJson(validators=[], pretty=True)
            swagger_json = codec.encode(schema).decode('utf-8')
            return swagger_json
        elif format == 'yaml':
            from drf_yasg.codecs import OpenAPICodecYaml
            codec = OpenAPICodecYaml(validators=[])
            swagger_yaml = codec.encode(schema).decode('utf-8')
            return swagger_yaml

And example usage:

from injector_modules.sqlalchemy_engine import SQLAlchemyEngineModule
from injector_modules.database_url import DatabaseUrlModule

# injector modules to be automatically discovered as well

app = WinterWebApplication('api', injector_modules=[
    SQLAlchemyEngineModule,
    DatabaseUrlModule,
])
wsgi = app.get_wsgi()
openapi = app.get_openapi(title='My API', format='json')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant