Giter Club home page Giter Club logo

drf-schema-adapter's Introduction

DRF-schema-adapter Build Status

drf-schema-adapter is a set of tools used to make it as straight-forward to declare an API endpoint as it is to declare a new ModelAdmin in Django and export the corresponding definition to frontend frameworks and libraries.

Compatibility Matrix

DRF-schema-adapter is compatible with the following matrix

Py 3.8 Py 3.9 Py 3.10 Py 3.11
Django 3.2 DRF 3.12+ DRF 3.12+ N/A N/A
Django 4.0 DRF 3.12+ DRF 3.12+ DRF 3.12+ N/A
Django 4.1 DRF 3.12+ DRF 3.12+ DRF 3.12+ DRF 3.12+

⚠️ For Python 2.7 or Django 1.x support, please use versions 1.x or prior ⚠️ For Django Version 3.1 or prior, use version 2.x

⚠️ For Python 3.11 compatibility, you will have (for now) to install Inflector directly from the repo:

pip install git+https://github.com/ixmatus/inflector@ef5c19dc2aa8df5e6b4c452ff2d9b54ec41a04a8#egg=Inflector

Installation

With pip

pip install drf-schema-adapter

If you are using python 3.11, you will have to also install Inflector directly from the repo, pending the release of Inflector 3.0.2

pip install git+https://github.com/ixmatus/inflector@ef5c19dc2aa8df5e6b4c452ff2d9b54ec41a04a8#egg=Inflector

From source

Within the source directory:

python setup.py install

Demo application

You can see a demo application running at https://djembersample.pythonanywhere.com/.

Basic usage

First of all you'll need to import the default EndpointRouter in your urls.py file.

from drf_auto_endpoint.router import router

As well as add its urls to your urlpatterns in urls.py, the same way you would with DRF's DefaultRouter.

urlpatterns = [
    ...
    path("api/", include(router.urls)),
    ...
]

Prototyping

The quickest way to get a working endpoint is to register a model with the router. Register accepts an optional keyword argument for the url associated to that endpoint. By default the url for an endpoint willbe app_label/verbose_name_plural

from django.urls import include, path
from drf_auto_endpoint.router import router
from my_app.models import MyModel, OtherModel

router.register(MyModel)
router.register(OtherModel, url='my_custom_url')

urlpatterns = [
    path("api/", include(router.urls)),
]

Adding schema information to your OPTIONS calls

Django REST framework provides the ability to customize those calls thanks to metadata classes.

Setup DRF to use one of DRF-schema-adapter's metadata classes to get schema information:

## settings.py

...
REST_FRAMEWORK = {
    'DEFAULT_METADATA_CLASS': 'drf_auto_endpoint.metadata.AutoMetadata',
}

Exporting to the frontend

First add 'export_app' to your setting's INSTALLED_APPS, then run:

./manage.py export --adapter_name EmberAdapter samples/products

Full documentation

For much more complete documentation, please see: http://drf-schema-adapter.readthedocs.io

Related projects

Contibuting guide-lines

If you'd like to contibute to DRF-schema-adapter*, you are more than welcome to do so. In order to make contributing to this project a rich experience for everyone, please follow these guide-lines:

  • First, fork the project with your own GitHub account, build your code on your own repository and submit a pull-request once your contribution is ready.
  • Before contributing a bug-fix or new feature, create an issue explaining what the problem/need is first. When submitting your pull-request, make sure to reference the original issue.
  • For any code you contribute, make sure to follow PEP8 recommendation (soft line-limit 100, hard limit 120).
  • For bug-fixes, please first write a test that will fail with the current code and passes using your patch. For easier evaluation, please do so in 2 separate commits
  • For new features, if your feature can be implemented as a third-party app (like new adapters), please don't contribute them to this repo, publish your own application and open an issue telling us about it. We will make sure to add a link to your application in this README.
  • Read and respect the Code Of Conduct

ToDo

  • Write better documentation
  • Write more/better tests

License information available here.

Contributors code of conduct is available here. Note that this COC will be enforced.

drf-schema-adapter's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

drf-schema-adapter's Issues

Failing builds

There are several tox envs that fail to build.

Some of them are related to the latest version of django-filter which support django2.0 and doing so, support for older django is not guaranteed anymore.

Also, an env is impossible due to a drf37 not supporting django19. (see http://www.django-rest-framework.org/topics/3.7-announcement/#django-20-support)

Builds with latest changes on django or drf may be broken for now. But we need to wait for updates from both django, drf et django-filter. Nothing we can do on our side for now.

A PR is on its way (from me) to fix the above problems. I think we'll need to add some more docs about dependencies with django-filters and its limitation.

How long do you want to keep support for django1.8 ? It could greatly simplify our matrix support.

app export view set not found

Hi Emma:
i am having this error

python manage.py export  --adapter_name EmberAdapter prodcuts
Exporting prodcuts using export_app.adapters.EmberAdapter
CommandError: No viewset found for prodcuts

when i try to run this the export on my cmd , i already set things i need at both side can you pin point what the problem of mine ?

here is the github of mine https://github.com/differentMonster/air-pack-2

DRF 3.7.4+ compatibility

It seems DRF added new features to the @list and @detail decorators.

DRF-Schema-Adapter need to add those as well.

Errors examples:

File "/var/lib/jenkins/jobs/Birds.ai-staging/workspace/venv/local/lib/python2.7/site-packages/rest_framework/routers.py", line 207, in _get_dynamic_route
    url_path = escape_curly_brackets(action.url_path)
AttributeError: 'function' object has no attribute 'url_path'
(venv) patrick@patrick-birds-ai:~/Documents/view/back$ python manage.py runserver
Performing system checks...

Unhandled exception in thread started by <function wrapper at 0x7fc1044cd9b0>
Traceback (most recent call last):
  File "/home/patrick/Documents/view/venv/local/lib/python2.7/site-packages/django/utils/autoreload.py", line 228, in wrapper
    fn(*args, **kwargs)
  File "/home/patrick/Documents/view/venv/local/lib/python2.7/site-packages/django/core/management/commands/runserver.py", line 125, in inner_run
    self.check(display_num_errors=True)
  File "/home/patrick/Documents/view/venv/local/lib/python2.7/site-packages/django/core/management/base.py", line 359, in check
    include_deployment_checks=include_deployment_checks,
  File "/home/patrick/Documents/view/venv/local/lib/python2.7/site-packages/django/core/management/base.py", line 346, in _run_checks
    return checks.run_checks(**kwargs)
  File "/home/patrick/Documents/view/venv/local/lib/python2.7/site-packages/django/core/checks/registry.py", line 81, in run_checks
    new_errors = check(app_configs=app_configs)
  File "/home/patrick/Documents/view/venv/local/lib/python2.7/site-packages/django/core/checks/urls.py", line 16, in check_url_config
    return check_resolver(resolver)
  File "/home/patrick/Documents/view/venv/local/lib/python2.7/site-packages/django/core/checks/urls.py", line 26, in check_resolver
    return check_method()
  File "/home/patrick/Documents/view/venv/local/lib/python2.7/site-packages/django/urls/resolvers.py", line 254, in check
    for pattern in self.url_patterns:
  File "/home/patrick/Documents/view/venv/local/lib/python2.7/site-packages/django/utils/functional.py", line 35, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/home/patrick/Documents/view/venv/local/lib/python2.7/site-packages/django/urls/resolvers.py", line 405, in url_patterns
    patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
  File "/home/patrick/Documents/view/venv/local/lib/python2.7/site-packages/django/utils/functional.py", line 35, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/home/patrick/Documents/view/venv/local/lib/python2.7/site-packages/django/urls/resolvers.py", line 398, in urlconf_module
    return import_module(self.urlconf_name)
  File "/usr/lib/python2.7/importlib/__init__.py", line 37, in import_module
    __import__(name)
  File "/home/patrick/Documents/view/back/NEST/urls.py", line 52, in <module>
    url(r'api/v2/', include(router_v2.urls)),
  File "/home/patrick/Documents/view/venv/local/lib/python2.7/site-packages/rest_framework/routers.py", line 101, in urls
    self._urls = self.get_urls()
  File "/home/patrick/Documents/view/venv/local/lib/python2.7/site-packages/rest_framework/routers.py", line 363, in get_urls
    urls = super(DefaultRouter, self).get_urls()
  File "/home/patrick/Documents/view/venv/local/lib/python2.7/site-packages/rest_framework/routers.py", line 261, in get_urls
    routes = self.get_routes(viewset)
  File "/home/patrick/Documents/view/venv/local/lib/python2.7/site-packages/rest_framework/routers.py", line 176, in get_routes
    extra_actions = viewset.get_extra_actions()
AttributeError: type object 'UserViewSet' has no attribute 'get_extra_actions'

must add drf_auto_endpoint to INSTALLED_APPS

Forgive me if I missed it, but I couldn't find anywhere in the docs that says to put 'drf_auto_endpoint' in INSTALLED_APPS. This entry is required if you are using endpoints.py file.

ROOT_URLCONF

The default urls should be django.conf.settings.ROOT_URLCONF, not 'urls'.

  File "/usr/lib/python3.8/site-packages/export_app/base.py", line 22, in __init__
    self.router = import_string(settings.ROUTER_PATH)
  File "/usr/lib/python3.8/site-packages/django/utils/module_loading.py", line 17, in import_string
    module = import_module(module_path)
  File "/usr/lib64/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 973, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'urls'

angular2 support?

Hi, I found this library.
But angular2 is not support, angular2 now have a library which call ngx-formly
Do you like to support this library?

filter_class does not respect filter_fields, Filter.fields

endpoints.py:

@register
class StageEndpoint(Endpoint):

    model = Stage
    read_only = True

    filter_class = StageFilter
    filter_fields = ('middleware', 'tshirt_size', 'service_level')

filtersets.py:

class MiddlewareSpecFilter(FilterSet):
    """ this is the base filter
        based on the intermediate table middlewarespec
        to filter specific attributes that are related to middlewarespecification """
    middleware = filters.CharFilter(label='Middleware',
            name='middlewarespecification__middleware__name',
            distinct=True)
    stage = filters.CharFilter(label='Stage',
            name='middlewarespecification__stage__stage',
            distinct=True)
    service_level = filters.CharFilter(label='Service Level',
            name='middlewarespecification__service_level',
            distinct=True)
    tshirt_size = filters.CharFilter(label='Tshirt Size',
            name='middlewarespecification__tshirt_size',
            distinct=True)

class StageFilter(MiddlewareSpecFilter):

    class Meta:
        model = Stage
        fields = ('middleware',
                  'tshirt_size',
                  'service_level')

The expected behavior is that filtering is possible for middleware, tshirt_size, service_level.
The result is that filtering is also available for stage.

Screenshot appended.

No module named 'six'

i am having this error

  File "C:\Users\_monster\Desktop\on_app\drf_api\lib\site-packages\drf_auto_endpoint\router.py", line 7, in <module>
    from .endpoints import Endpoint
  File "C:\Users\_monster\Desktop\on_app\drf_api\lib\site-packages\drf_auto_endpoint\endpoints.py", line 3, in <module>
    from six import with_metaclass
ModuleNotFoundError: No module named 'six'

some kind of module six is missing , no ideal what happen ? thank for the effort , really appreciate.

:bug: An Adapter with required_fields=True and works_with='viewset' crashes export command

Traceback (most recent call last):
  File "./manage.py", line 9, in <module>
    execute_from_command_line(sys.argv)
  File "/Users/joakim/Envs/drf-schema/lib/python2.7/site-packages/django/core/management/__init__.py", line 367, in execute_from_command_line
    utility.execute()
  File "/Users/joakim/Envs/drf-schema/lib/python2.7/site-packages/django/core/management/__init__.py", line 359, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/Users/joakim/Envs/drf-schema/lib/python2.7/site-packages/django/core/management/base.py", line 294, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/Users/joakim/Envs/drf-schema/lib/python2.7/site-packages/django/core/management/base.py", line 345, in execute
    output = self.handle(*args, **options)
  File "/Users/joakim/ProReNataJournal/drf-schema-adapter/export_app/management/commands/export.py", line 64, in handle
    fields, rels = self.get_fields_for_model(model, serializer_instance, adapter,
UnboundLocalError: local variable 'model' referenced before assignment

I'm not sure exactly what the fix would be so I'm making this an issue instead of a PR.

DRF choices not set in MultiChoiceField serializer field

I'm using this library to provide metadata information for a frontend application, one of the field in my model is a simple M2M mapped to "static" table with 15 entries.

class Waste(models.Model):
    cer = models.CharField(verbose_name='Codice CER', null=True, blank=True, max_length=10, choices=CER_CODES)
    descr = models.CharField(verbose_name='Descrizione rifiuto', null=True, blank=True, max_length=300)
    unofficial_descr = models.CharField(verbose_name='Descrizione informale del rifiuto', null=True, blank=True, max_length=300)
    categ_rifiuto = models.CharField(verbose_name='Tipologia rifiuto', null=True, blank=True, max_length=20, choices=CHOICES_CATEG_RIF)
    haz = models.BooleanField(verbose_name='Pericoloso', default=False)  # MAYBE default None?
    hp = models.ManyToManyField(HP, verbose_name='Proprietà di pericolo', blank=True)

For that reason, in the related serializer I decided to use MultiChoiceField

class WasteSerializer(serializers.ModelSerializer):
    hp = serializers.MultipleChoiceField(choices=tuple(HP.objects.all().values_list('code', 'descr')), label='Proprietà di pericolo')

    def create(self, validated_data):
        # custom code to set hp appropriately

    class Meta:
        model = Waste
        fields = '__all__'

Unfortunately the available choices are not rendered in the metadata response, the reason is that drf_auto_endpoint.get_field_dict.GetFieldDict.update_choices_from_serializer only recognize ChoiceField as a field for which choices should be populated

    def update_choices_from_serializer(self, rv, field_instance, force=False):
        if field_instance.__class__.__name__ != 'ChoiceField' and force is False:
            return # --> in my case the field is of type 'MultiChoiceField'

        if not hasattr(field_instance, 'choices'):
            return

        if rv.get('related_endpoint', None) is not None and force is False:
            return

        rv['type'] = settings.WIDGET_MAPPING['choice']
        rv['choices'] = [
            {
                'label': v,
                'value': k,
            } for k, v in field_instance.choices.items()
        ]

For this field the rendered metadata is the following (which is not particularly useful for the frontend)

  {
    "key": "hp",
    "type": "manytomany-lists",
    "read_only": false,
    "ui": {
      "label": "Proprietà di pericolo"
    },
    "validation": {
      "required": true
    },
    "extra": {},
    "translated": false,
    "related_endpoint": {
      "app": "core",
      "singular": "hp",
      "plural": "hps"
    }

Thanks,
Mattia

Support for __all__ in fields definition

Hi,
Using the all keyword in serializer fields definition won't work.
The issue is coming from using serializer.Meta.fields in several places. DRF is using serializer.fields.items() instead which seems to handle it correctly. Is there a specific reason not to use it ? (I'll try to have a PR tomorrow for it)

angular-formly Does not support the read_only option will cause failure

surefire http://angular-formly.com/#!/example/integrations/ui-select-angular-1-4

Increase read_only: false Cause failure
[
{
"key": "singleOption",
"type": "ui-select-single",
"templateOptions": {
"optionsAttr": "bs-options",
"ngOptions": "option[to.valueProp] as option in to.options | filter: $select.search",
"label": "Single Select",
"valueProp": "id",
"labelProp": "label",
"placeholder": "Select option",
"description": "Template includes the allow-clear option on the ui-select-match element",
"options": [
{
"id": 1,
"label": "Option 1"
},
{
"id": 2,
"label": "Option 2"
},
{
"id": 3,
"label": "Option 3"
}
]
}
},
{
"key": "select2Option",
"type": "ui-select-single-select2",
"templateOptions": {
"optionsAttr": "bs-options",
"ngOptions": "option[to.valueProp] as option in to.options | filter: $select.search",
"label": "Single Select (select2)",
"valueProp": "id",
"labelProp": "label",
"placeholder": "Select option",
"options": [
{
"id": 1,
"label": "Option 1"
},
{
"id": 2,
"label": "Option 2"
},
{
"id": 3,
"label": "Option 3"
}
]
}
},
{
"key": "multipleOption",
"type": "ui-select-multiple",
"templateOptions": {
"optionsAttr": "bs-options",
"ngOptions": "option[to.valueProp] as option in to.options | filter: $select.search",
"label": "Multiple Select",
"valueProp": "id",
"labelProp": "label",
"placeholder": "Select options",
"options": [
{
"id": 1,
"label": "Option 1"
},
{
"id": 2,
"label": "Option 2"
},
{
"id": 3,
"label": "Option 3"
}
]
}
},
{
"key": "singleOptionAsync",
"type": "ui-select-single-search",
"templateOptions": {
"optionsAttr": "bs-options",
"ngOptions": "option[to.valueProp] as option in to.options | filter: $select.search",
"label": "Async Search",
"valueProp": "formatted_address",
"labelProp": "formatted_address",
"placeholder": "Search",
"options": [],
"refreshDelay": 0
}
}
]

Can't install version >=3.0.1 from pypi anymore

Hi,

since 4d437c1 and the release of version 3.0.1, this package can't be installed anymore with pip from pypi, since pip#5571 requires that packages from PyPI must only install dependencies from PyPI. Installation thus fails with

ERROR: Packages installed from PyPI cannot depend on packages which are not also hosted on PyPI.

This can be worked around of course by not installing the package from PyPI, but I would guess this is not the intended outcome.

Recent version update (2.0.8) broke something

First: Big thanks for this package, great helper and saves us unlimited hours.

If i start django / run tests following happens:

  ...
  File "/path/to/package/.venv/lib/python3.9/site-packages/drf_auto_endpoint/endpoints.py", line 235, in get_url
    if self.url is not None:
AttributeError: 'Endpoint' object has no attribute 'url'

Adding url=None to BaseEndpoint class fixes the problem. Using hasattr(self, "url") on line 235 would also be a possible fix.

TypeError: register() got an unexpected keyword argument 'base_name'

Currently running ./test.sh fails with the following error:

TypeError: register() got an unexpected keyword argument 'base_name'

The reason is this change in the Django REST Framework released in version 3.9.0: "Deprecate the Router.register base_name argument in favor of basename" -- Release Notes

I'd propose the following fix for now:

  • allow both, base_name and basename for drf-schema-adapter routers
  • translate base_name to basename when passing kwargs to DRF
  • throw a ValueError when both base_name and basename are given

If that sounds good to you I can try to come up with a patch!

json-schema support

I would like to use alpaca [1] json form created from drf using a json-schema. They support most of the json-schema API. Is this project the right starting point for implementing metadata in json-scheme format?

I like the idea of creating serializer/viewset for models on the fly and struggling using the drf_auto_endpoint or drf-nested-routers [2] for this approach. Do you know the second and may explain where both differs?

[1] https://github.com/gitana/alpaca
[2] https://github.com/alanjds/drf-nested-routers

Using HyperLinkedModelSerializer with Endpoint

I'm trying to get an Endpoint working that utilizes DRFs HyperLinkedModelSerializer but haven't quite got it right. Not sure if it's a bug, expected behavior, incorrect docs or incorrect reading of said docs. Anyway...

I have a very basic model that looks like this:

   class Project(models.Model):
        title = models.CharField(max_length=255)

Attempt 1 - Failed

From the docs, it looks like it should be relatively simple so I tried:

class TestEndpoint(Endpoint):
    model = Test
    base_serializer = serializers.HyperlinkedModelSerializer

router = EndpointRouter()

router.register(endpoint=TestEndpoint)

urlpatterns = [
    path("", include(router.urls)),
]

...but only the standard (non-hyperlinked) response is returned.

{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "id": 1,
      "name": "This is a test object",
      "__str__": "Test object (1)"
    }
  ]
}

Attempt 2 - Failed

Maybe it works if I create a custom serializer and apply it directly to the Endpoint:

class TestSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Test
        fields = "__all__"

class TestEndpoint(Endpoint):
    model = Test
    serializer = TestSerializer

but the result was the exact same as the first :(

Attempt 3 - Failed

I noticed that in the app code, you tend to instantiate endpoints before registering them with the router (the docs suggest otherwise). So I tried this myself.

router.register(endpoint=TestEndpoint())

This fails in a different way. Now an ImproperlyConfigured is raised by DRF:

Could not resolve URL for hyperlinked relationship using view name "test-detail". You may have failed to include the related model in your API, or incorrectly configured the `lookup_field` attribute on this field.

This is because the url field which is autogenerated by the HyperLinkedModelSerializer can't find the detail view. It does exist, but examining the URL conf show that it exists under the name <application_name>/test-detail rather than simply test-detail.

Attempt 4 - Success (sort of)

So what seemed to work was starting from Attempt 3 but explicitly defining the url:

class TestEndpoint(Endpoint):
    model = Test
    serializer = TestSerializer
    url = 'test'

which results in the expected output. Note though that the url must equal the lower case model name.

{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "url": "http://ghfdb.localhost:8000/api/v1/test/1/",
      "name": "This is a test object"
    }
  ]
}

Conclusion

I don't know if I misinterpreted the docs but I really thought my first attempt with base_serializer should just work. Explicitly defining a serializer and url for each endpoint seems to violate the DRY principles of this package so I wanted to see if there is something I'm missing with my approach.

Exception on enum fields with React adapter

Hi,

Using Django 1.11.1, Rest framework 3.7.7, drf-schema-adapter 0.9.52, and the ReactJsonSchemaAdapter adapter, it seems I'm getting an exception accessing the schema of any model that contains a field with a choices argument (https://docs.djangoproject.com/en/2.0/ref/models/fields/#choices).

The exception is as follow:

__Traceback:__

File "/usr/share/python/scirius/local/lib/python2.7/site-packages/django/core/handlers/exception.py" in inner
  41.             response = get_response(request)

File "/usr/share/python/scirius/local/lib/python2.7/site-packages/django/core/handlers/base.py" in _get_response
  187.                 response = self.process_exception_by_middleware(e, request)

File "/usr/share/python/scirius/local/lib/python2.7/site-packages/django/core/handlers/base.py" in _get_response
  185.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/usr/share/python/scirius/local/lib/python2.7/site-packages/django/views/decorators/csrf.py" in wrapped_view
  58.         return view_func(*args, **kwargs)

File "/usr/share/python/scirius/local/lib/python2.7/site-packages/rest_framework/viewsets.py" in view
  95.             return self.dispatch(request, *args, **kwargs)

File "/usr/share/python/scirius/local/lib/python2.7/site-packages/rest_framework/views.py" in dispatch
  494.             response = self.handle_exception(exc)

File "/usr/share/python/scirius/local/lib/python2.7/site-packages/rest_framework/views.py" in handle_exception
  454.             self.raise_uncaught_exception(exc)

File "/usr/share/python/scirius/local/lib/python2.7/site-packages/rest_framework/views.py" in dispatch
  491.             response = handler(request, *args, **kwargs)

File "/usr/share/python/scirius/local/lib/python2.7/site-packages/rest_framework/views.py" in options
  505.         data = self.metadata_class().determine_metadata(request, self)

File "/usr/share/python/scirius/local/lib/python2.7/site-packages/drf_auto_endpoint/metadata.py" in determine_metadata
  113.         return adapter(metadata)

File "/usr/share/python/scirius/local/lib/python2.7/site-packages/drf_auto_endpoint/adapters.py" in __call__
  32.         return self.render(config)

File "/usr/share/python/scirius/local/lib/python2.7/site-packages/drf_auto_endpoint/adapters.py" in render
  394.         config['fields'] = super(ReactJsonSchemaAdapter, self).render(config)

File "/usr/share/python/scirius/local/lib/python2.7/site-packages/drf_auto_endpoint/adapters.py" in render
  24.             adapted.append(self.adapt_field(field))

File "/usr/share/python/scirius/local/lib/python2.7/site-packages/drf_auto_endpoint/adapters.py" in adapt_field
  301.             new_field['schema']['enum'] = [x[0] for x in field['choices']]

Exception Type: KeyError at /rest/rules/transformations/rules/
Exception Value: 0

I have been able work-around it with this small patch, not sure it's the right way to do it though:

--- adapters.py.old     2018-04-10 09:48:55.506893000 +0000
+++ adapters.py 2018-04-10 09:48:35.834893000 +0000
@@ -299,6 +299,6 @@
 
         if 'choices' in field:
-            new_field['schema']['enum'] = [x[0] for x in field['choices']]
-            new_field['schema']['enumNames'] = [x[1] for x in field['choices']]
+            new_field['schema']['enum'] = [x['value'] for x in field['choices']]
+            new_field['schema']['enumNames'] = [x['label'] for x in field['choices']]
 
         # if 'related_endpoint' in field:

Object of type Gender is not JSON serializable when calling OPTIONS request

Hello
I just figure out that when I remove:
'DEFAULT_METADATA_CLASS': 'drf_auto_endpoint.metadata.AutoMetadata',
from settings. OPTIONS request is displayed well.
Here is the description of my old issue related to django-enumfields:

#hzdg/django-enumfields#126
Maybe incompatibility with django-enumfields ?
Same problem with django-money library: I use a moneyfield in serializer and get Object Money is not json serializable only in Options request. all other requests (get, post, ..) are working well.

Thanks for your answers

Wrong endpoint used in metadata when specifying viewset

Hi,
When an endpoint is registered with a specific viewset, a different endpoint is generated in determine_metadata (hence losing all out custom attributes). The responsible part in metada.py:

        try:
            serializer_instance = view.get_serializer()
        except Exception:
            # Custom viewset is expecting something we can't guess
            serializer_instance = view.get_serializer_class()()
        endpoint = None
        if hasattr(view, 'endpoint'):
            endpoint = view.endpoint
        else:
            if hasattr(view.get_serializer_class().Meta, 'model'):
                from .endpoints import Endpoint
                endpoint = Endpoint(view.get_serializer_class().Meta.model, viewset=view)

The endpoint relation is normally set by the viewset_factory, and it's not when specifying a viewset.
Came up with this, not ideal, fix :

class Endpoint(with_metaclass(EndpointMetaClass, BaseEndpoint)):

    def __init__(self, model=None, **kwargs):
        self.inflector = Inflector(self.inflector_language)

        if model is not None:
            self.model = model

        arg_names = ('fields', 'serializer', 'permission_classes', 'filter_fields', 'search_fields',
                     'viewset', 'read_only', 'include_str', 'ordering_fields', 'page_size',
                     'base_viewset', 'fields_annotation', 'fieldsets', 'base_serializer', 'list_me')
        for arg_name in arg_names:
            setattr(self, arg_name, kwargs.pop(arg_name, getattr(self, arg_name, None)))
            if arg_name == 'viewset' and getattr(self, arg_name):
                assert getattr(self.viewset, 'endpoint', None) is None, 'This viewset is already registered by an endpoint'
                setattr(self.viewset, 'endpoint', self)

I think we have to prevent the same viewset to be registered by different endpoints or the metadata woulnd't mean anything. We could also test the class instead of the objects.. (The tests will not pass as the same viewset is registered multiple times for now)

'PlanningViewSet' object has no attribute 'get_serializer_class'

Trying to use the drf-schema-adapter router next to a default router.

When setting default METADATA_CLASS to:

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS':
    ('django_filters.rest_framework.DjangoFilterBackend',),
    'DEFAULT_METADATA_CLASS': 'drf_auto_endpoint.metadata.AutoMetadata',
}   

OPTION is broken for viewsets that are created via routers.DefaultRouter()

router = routers.DefaultRouter()
router.register(r'middleware', MiddlewareViewSet, base_name='middleware')
router.register(r'planning', PlanningViewSet, base_name='planning')

Is it possible to add the metadaclass per serializer?

pypandoc install fails of pandoc isn't installed

Collecting drf-schema-adapter
  Using cached drf-schema-adapter-0.9.52.tar.gz
    Complete output from command python setup.py egg_info:
    See http://johnmacfarlane.net/pandoc/installing.html
    for installation options
    ---------------------------------------------------------------
    
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-build-jy2sc7nj/drf-schema-adapter/setup.py", line 7, in <module>
        README = pypandoc.convert_file('README.md', 'rst')
      File "/home/path/venv/lib/python3.6/site-packages/pypandoc/__init__.py", line 140, in convert_file
        outputfile=outputfile, filters=filters)
      File "/home/path/venv/lib/python3.6/site-packages/pypandoc/__init__.py", line 260, in _convert_input
        _ensure_pandoc_path()
      File "/home/path/venv/lib/python3.6/site-packages/pypandoc/__init__.py", line 544, in _ensure_pandoc_path
        raise OSError("No pandoc was found: either install pandoc and add it\n"
    OSError: No pandoc was found: either install pandoc and add it
    to your PATH or or call pypandoc.download_pandoc(...) or
    install pypandoc wheels with included pandoc.
    
    ----------------------------------------



OPTIONS request fails for serializers.Serializer subclass

example:

from rest_framework import serializers, generics
from django.urls import path


class ExampleSerializer(serializers.Serializer):
    field_one = serializers.CharField(required=True, write_only=True)
    field_two = serializers.CharField(required=True, write_only=True)

    def create(sef, validated_data):
        # do something
        return {}


class ExampleView(generics.CreateAPIView):
    serializer_class = ExampleSerializer


urlpatterns = [
    path('example', views.Example.as_view(), name='example'),
]

Making an OPTIONS request results in a 500 error: 'ExampleSerializer' has no attribute 'Meta'
Am I misunderstanding something, or is this a bug?

Package name

Hi all,

we discussed the fact that drf-forms was a temporary name and that we should find a better name, here are a few proposals:

  • drf_admin_toolkit
  • drf_fe_toolkit
  • drf_fe_adapters

filter_filters GETTER doesn't seem to be returning any filter fields

I'm attempt to write a custom adapter from base adapter, to display field types and whether or not they are filterable. Here's my code:

from drf_auto_endpoint.adapters import BaseAdapter, MetaDataInfo, GETTER


class MyAPIAdapter(BaseAdapter):
    """
    Custom adapter from DRF-schema-adapter to provide fields
    and filter fields within endpoint OPTIONS.
    """

    metadata_info = [
        MetaDataInfo('fields', GETTER, []),
        MetaDataInfo('filter_fields', GETTER, []),
    ]

    @classmethod
    def adapt_field(self, field):
        new_field = {
            'key': field['key'],
            'type': field['type'],
        }

        return new_field

    def render_root(self, config):
        config['applications'] = [
            {
                'name': app['name'].replace('_', '-'),
                'models': app['models']
            } for app in config['applications']
        ]
        return config

    def render(self, config):
        from pprint import pprint
        pprint(config)
        fields_map = {
            field['key']: field
            for field in super(MyAPIAdapter, self).render(config)
        }

        # adapted = self._render_fieldset(deepcopy(config['fieldsets']), fields_map)
        # return adapted
        return fields_map

The pprint seems to indicate that there are no filter fields:

('filter_fields', [])])

However, for the endpoint I am testing I know there are three.

Any idea what I'm doing wrong? I've started digging through the code, but I'm not familiar and hoping I'm just doing something obviously wrong. Thanks for your efforts!

TypeError: 'NoneType' object is not callable at serializer_instance = view.serializer_class()

When setting:

REST_FRAMEWORK = {
'DEFAULT_METADATA_CLASS': 'drf_auto_endpoint.metadata.AutoMetadata',
}
DRF_AUTO_METADATA_ADAPTER = 'drf_auto_endpoint.adapters.AngularFormlyAdapter'

and using a ModelViewSet instead of auto_endpoint with a custom get_serializer_class, eg:

`class MyViewSet(viewsets.ModelViewSet):
def get_queryset(self):
model = eval(self.kwargs.get('string'))
return model.objects.all()

def get_serializer_class(self):
     MySerializer.Meta.model = eval(self.kwargs.get('string'))
     MySerializer.Meta.fields = '__all__'
     return MySerializer

class MySerializer(serializers.ModelSerializer):
class Meta():
model = None`

where I have set the url pattern:

url(r'^api/(?P[\w-]+)/$',MyViewSet.as_view({'get':'list'})),

http request GET works, however when I request OPTIONS I get an internal server error:

File ".../drf_auto_endpoint/metadata.py", line 54, in determine_metadata
serializer_instance = view.serializer_class()
TypeError: 'NoneType' object is not callable

My goal is setting up a view where I can run an OPTIONS request for any model, given its name as a parameter in the URL.

AutoMetadata discards DRF format

The AutoMetadata inherits from SimpleMetadata, but has a wildly incompatible output.

The output of the base class is

{"name":"foo","description":"","renders":["application/json","text/html"],"parses":["application/json","application/x-www-form-urlencoded","multipart/form-data"],"actions":{"GET":{...}}}

Most of that data is missing from the drf-schema-adapter AutoMetadata, and the name is moved to be under ui.label. Is the AutoMetadata response loosely following some spec; if so, which one? I suspect that the DRF SimpleMetadata has no spec, but they are implicitly the standard setter for Django REST, and there are lots of frontend apps which expect/use that format. It is a defacto standard, for good or ill.

The OPTIONS response is fairly flexible according to the spec, but drf-schema-adapter should provide the response some content-type like application/json+foobar to give a clue what it is.

I see that AutoMetadataMixin.determine_metadata does invoke the DRF SimpleMetadata.determine_metadata, and it gives that to the adapter, defaulting to adapters.BaseAdapter, which then discards all of the metadata provided by DRF SimpleMetadata.determine_metadata.

IMO the default drf-schema-adapter adapter should enrich the DRF format, not discard the elements it provides, as that makes the default adapter incompatible with DRF.

If not the default adapter, there should at least be one adapter which does extend the DRF format with extra fields, if only as a demo of how this library can be used to extend existing DRF based clients.

Unable to install drf-schema-adapter with setuptools>=67

Hi everyone,
since today a lot of our pipelines failed. After some investigation I found the cause in a version incompatibility of setuptools=>67 and drf-schema-adapter.

I get following error:

error in drf-schema-adapter setup command: 'install_requires' must be a string or list of strings containing valid project/version requirement specifiers; Expected end or semicolon (after version specifier)
          djangorestframework>=3.12<4.0

This may match with the changelog of setuptools: https://github.com/pypa/setuptools/blob/main/CHANGES.rst

Relevant part:

#3790: Bump vendored version of :pypi:packaging to 23.0 (:pypi:pyparsing is no longer required and was removed). As a consequence, users will experience a more strict parsing of requirements. Specifications that don't comply with PEP 440 and PEP 508 will result in build errors.

I am not a packaging specialist, but I think a minor change in

'djangorestframework>=3.12<4.0',
would fix this.

Steps to reproduce

[sob@host ] ~/tmp$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 22.04.1 LTS
Release:	22.04
Codename:	jammy
[sob@host ] ~/tmp$ virtualenv venv
created virtual environment CPython3.10.6.final.0-64 in 75ms
[sob@host ] ~/tmp$ source venv/bin/activate
(venv) [sob@host ] ~/tmp$ pip install --upgrade setuptools
Requirement already satisfied: setuptools in ./venv/lib/python3.10/site-packages (65.5.0)
Collecting setuptools
  Using cached setuptools-67.0.0-py3-none-any.whl (1.1 MB)
Installing collected packages: setuptools
  Attempting uninstall: setuptools
    Found existing installation: setuptools 65.5.0
    Uninstalling setuptools-65.5.0:
      Successfully uninstalled setuptools-65.5.0
Successfully installed setuptools-67.0.0

[notice] A new release of pip available: 22.3 -> 22.3.1
[notice] To update, run: pip install --upgrade pip
(venv) [sob@host ] ~/tmp$ pip install drf-schema-adapter
Collecting drf-schema-adapter
  Downloading drf-schema-adapter-3.0.0.tar.gz (33 kB)
  Preparing metadata (setup.py) ... error
  error: subprocess-exited-with-error
  
  × python setup.py egg_info did not run successfully.
  │ exit code: 1
  ╰─> [3 lines of output]
      error in drf-schema-adapter setup command: 'install_requires' must be a string or list of strings containing valid project/version requirement specifiers; Expected end or semicolon (after version specifier)
          djangorestframework>=3.12<4.0
                             ~~~~~~^
      [end of output]
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
error: metadata-generation-failed

× Encountered error while generating package metadata.
╰─> See above for output.

note: This is an issue with the package mentioned above, not pip.
hint: See above for details.

override EmberAdapter extras['choices']

How would I start customise values in extras['choices'] for the EmberAdapter?
Goal is to have filtered choices in OPTIONS for specific fields.

Just as an idea:

Filtering would be done with a custom FilterSet.
The label/values could be extracted via filter_set.qs.values_list(field_name, flat=True).

There may be some bugs

metadata.py line:76

            for field in serializer_instance.fields.keys():
                if field in {'id', '__str__'}:
                    continue

                instance_field = serializer_instance.fields[field]
                type_ = settings.WIDGET_MAPPING.get(instance_field.__class__.__name__)

                if type_ is None:
                    raise NotImplementedError()

                field_metadata = self.get_field_dict(field, serializer)

                fields_metadata.append(field_metadata)

                for meta_info in adapter.metadata_info:
                    if meta_info.attr == 'fields':
                        metadata['fields'] = fields_metadata,
                    elif meta_info.attr == 'fieldsets':
                        metadata['fieldsets'] = [{
                            'title': None,
                            'fields': [
                                {'key': field}
                                for field in serializer_instance.fields.keys()
                                if field != 'id' and field != '__str__'
                            ]
                        }]
                    else:
                        metadata[meta_info.attr] = meta_info.default

Maybe it should be like

            for field in serializer_instance.fields.keys():
                if field in {'id', '__str__'}:
                    continue

                instance_field = serializer_instance.fields[field]
                type_ = settings.WIDGET_MAPPING.get(instance_field.__class__.__name__)

                if type_ is None:
                    raise NotImplementedError()

                field_metadata = self.get_field_dict(field, serializer)

                fields_metadata.append(field_metadata)

            for meta_info in adapter.metadata_info:
                if meta_info.attr == 'fields':
                    metadata['fields'] = fields_metadata
                elif meta_info.attr == 'fieldsets':
                    metadata['fieldsets'] = [{
                        'title': None,
                        'fields': [
                            {'key': field}
                            for field in serializer_instance.fields.keys()
                            if field != 'id' and field != '__str__'
                        ]
                    }]
                else:
                    metadata[meta_info.attr] = meta_info.default

line:92 metadata['fields'] = fields_metadata,
The last comma is superfluous. Option operation will returns two-tier array
line:90 for meta_info in adapter.metadata_info:
It shouldn't be indented here

Related endpoints

Hi,
Related endpoints generated by AutoMetadataMixin are different from the ones generated by the automatic registration of endpoints.
The firsts are simply using the model_name (singular) when the seconds are automatically inflected.

Wouldn't it make more sense to generate them the same way ? Because the related_endpoint link so far doesn't represent any real resource.

Ember-cli-crudities adapater

Fields:

[
      {
            "read_only": false,
            "extra": {
                "related_model": "emberadmin/category",
                "choices": [
                    {"label": "French", "value": "fr"},
                    {"label": "English", "value": "en"},
                ]
            },
            "name": "category",
            "label": "Category",
            "widget": "foreignkey"
        },
    ...
]

Fieldsets:

[
        {
            "title": null,
            "fields": [
                {
                    "name": "campaign"
                },
                ...
       }
]

Better export of relationships + tests

  • serializer relationships with sourcedo not export related model correctly (metadata + exporter)
  • RelatedForeignKey / RelatedSlug (equivalent of foreignkey with to_field) in views with serializers that are not ModelSerializers (like used by @wizard)
  • Reverse Many2Many with no related_name

this issue is not exaustive

Why does this project not receive more attention?

The repetitiveness of writing frontend and backend model definitions has always held me back from adding modern frontend application to my Django projects. I was looking for quite a while for a project like this and can't believe there isn't a massive amount of people in a similar situation. So I would have expected this project getting much more attention.

I know there is for example django-angular but that's only AngularJS and it doesn't seem to be easily ported to a modern version of Angular. Without having really tried either of the projects, I've got the feeling that django-angular brings too much own code to the frontend part of the application and hence limiting the frontend developers options to stick to well established patterns. In my impression drf-schema-adapter is positioned just right by only defining "base models and services". Also I like that it is more generic concerning the frontend framework.

So I wondered why there aren't many more users, which made my kind of skeptical. Is there any drawback I didn't recognize? Or are there more popular, similar projects that have kept under my radar? Please don't get that wrong, what I've seen looks fantastic. I'll be happy to assist in making the project more popular. What do you think would be required most urgently?

Is export_app pretty much only for Ember guys?

Traceback:

File "/home/joshua/Development/nctools/env/lib/python3.5/site-packages/django/core/handlers/exception.py" in inner
  41.             response = get_response(request)

File "/home/joshua/Development/nctools/env/lib/python3.5/site-packages/django/core/handlers/base.py" in _legacy_get_response
  249.             response = self._get_response(request)

File "/home/joshua/Development/nctools/env/lib/python3.5/site-packages/django/core/handlers/base.py" in _get_response
  187.                 response = self.process_exception_by_middleware(e, request)

File "/home/joshua/Development/nctools/env/lib/python3.5/site-packages/django/core/handlers/base.py" in _get_response
  185.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/home/joshua/Development/nctools/env/lib/python3.5/site-packages/django/views/generic/base.py" in view
  68.             return self.dispatch(request, *args, **kwargs)

File "/home/joshua/Development/nctools/env/lib/python3.5/site-packages/django/views/generic/base.py" in dispatch
  88.         return handler(request, *args, **kwargs)

File "/home/joshua/Development/nctools/env/lib/python3.5/site-packages/django/views/generic/base.py" in get
  156.         return self.render_to_response(context)

File "/home/joshua/Development/nctools/env/lib/python3.5/site-packages/django/views/generic/base.py" in render_to_response
  130.             template=self.get_template_names(),

File "/home/joshua/Development/nctools/env/lib/python3.5/site-packages/export_app/views.py" in get_template_names
  14.         return [self.adapter_class.dynamic_template_name]

Exception Type: AttributeError at /models/address.js
Exception Value: type object 'MetadataAdapter' has no attribute 'dynamic_template_name'

So I decided maybe I'll just copy that dynamic_template_name into the BaseAdapater and that worked!.... But then I looked at the export and yep, all ember branded:

define('addresses/models/crm/address',
    ['exports', 'ember-data/model', 'ember-data/attr', 'ember-data/relationships'],
    function (exports, _emberDataModel, _emberDataAttr, _emberDataRelationships) {
  exports['default'] = _emberDataModel['default'].extend({
    
      name: (0, _emberDataAttr['default'])(),
    
      street_address: (0, _emberDataAttr['default'])(),
    
      street_address_cont: (0, _emberDataAttr['default'])(),
    
      city: (0, _emberDataAttr['default'])(),
    
      zip_code: (0, _emberDataAttr['default'])(),
    
      lat: (0, _emberDataAttr['default'])(),
    
      lng: (0, _emberDataAttr['default'])(),
    
      __str__: (0, _emberDataAttr['default'])(),
    
    
      state_or_province: (0, _emberDataRelationships['None'])('location/region', {async: true, inverse: 'address_state'}),
    
      on_store_locator: (0, _emberDataRelationships['None'])('sites/site', {async: true, inverse: null}),
    
  });
});

Then I looked at the template and figured I guess i could just copy it and format it the way I like. Is this normal? or is there a bug with the MetadataAdapter?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.