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

ModelSerializers / Login protected access / Groups by namespaces #70

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d036de0
(Not tested yet)
Dec 22, 2015
c6401a9
Preparing merge with upstream/master
Jan 3, 2016
deb9446
Add app_name in urls + tests
Jan 4, 2016
e2744a0
- Show the "Jump to" dropdown only if there is more than 1 value
Jan 5, 2016
b774268
Add '-' to the url patten
Jan 5, 2016
e866a41
Use the endpoint's namespace into the group url
Jan 5, 2016
2a56a35
Manage nested serializer and list fields/serializers in Endpoint fields
Jan 20, 2016
34f9683
correction of recursive call
Jan 20, 2016
5a43d41
Recursive include is now called with "only"
Jan 20, 2016
85ba4d2
Manage allowed_methods on ModelViewSets
Jan 21, 2016
a621943
Sort allowed_methods
Jan 22, 2016
8c12fdd
Merge remote-tracking branch 'upstream/master'
Jan 26, 2016
8dab8d9
Add line breaks on docstrings
Feb 19, 2016
52cf5ce
Merge from upstream/master
Feb 19, 2016
be8f4ce
Merge remote-tracking branch 'upstream/master'
Feb 29, 2016
4d2a370
Nested/List serializers + login_required
Feb 29, 2016
a97f94a
Re-enabling jumbotron block without content (too much space lost)
Feb 29, 2016
fa5871a
Merge branch 'master' into master
debnet Mar 8, 2016
3441d6f
Fix a TypeError fetching the allowed_methods when the ModelViewSet has
msaelices Mar 15, 2016
eedcae5
Merge pull request #1 from msaelices/master
debnet Mar 15, 2016
c6bbf50
Fixing flake8 issues after PR #1
Mar 16, 2016
0fd3c1d
Merge remote-tracking branch 'upstream/master'
Mar 25, 2016
1b5b1c2
Merge branch 'master' into master
debnet Mar 27, 2016
27e5d8e
Merge branch 'master' into master
debnet Mar 31, 2016
9a677dc
Merge remote-tracking branch 'upstream/master'
Jun 2, 2016
d08e25b
Merge upstream/master 0.0.10
Jun 2, 2016
fc9179f
Merge remote-tracking branch 'upstream/master'
Jul 29, 2016
550b372
Merge from upstream + allow list of drf_routers
Aug 1, 2016
15dead2
Fix flake8
Aug 1, 2016
33b41df
Update to Django 1.10
Sep 5, 2016
f36f8a1
Merge branch 'master' into master
debnet Sep 7, 2016
e5a8bef
Fix Django 2
Apr 12, 2018
ada797d
Fix _get_allowed_methods
Oct 11, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -60,7 +60,8 @@ Finally include the `rest_framework_docs` urls in your `urls.py`:
You can find detailed information about the package's settings at [the docs](http://drfdocs.com/settings/).

REST_FRAMEWORK_DOCS = {
'HIDE_DOCS': True # Default: False
'HIDE_DOCS': True, # Default: False
'LOGIN_REQUIRED': True, # Default: True
}


Expand Down
10 changes: 10 additions & 0 deletions demo/project/accounts/serializers.py
@@ -1,5 +1,6 @@
from rest_framework import serializers
from project.accounts.models import User
from rest_framework.authtoken.serializers import AuthTokenSerializer


class UserRegistrationSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -30,3 +31,12 @@ class Meta:
model = User
fields = ('id', 'token', 'password',)
extra_kwargs = {'password': {'write_only': True}}


class NestedSerializer(serializers.Serializer):
nb_test = serializers.IntegerField(default=0, required=False)
liste_codes = serializers.ListField(child=serializers.CharField())


class CustomAuthTokenSerializer(AuthTokenSerializer):
nested = NestedSerializer(many=True)
16 changes: 12 additions & 4 deletions demo/project/accounts/urls.py
@@ -1,15 +1,23 @@
import django
from django.conf.urls import url
from project.accounts import views

from rest_framework.routers import SimpleRouter

urlpatterns = [
url(r'^test/$', views.TestView.as_view(), name="test-view"),
account_router = SimpleRouter()
account_router.register('user-model-viewsets', views.UserModelViewset, base_name='account')

account_urlpatterns = [
url(r'^test/$', views.TestView.as_view(), name="test-view"),
url(r'^login/$', views.LoginView.as_view(), name="login"),
url(r'^register/$', views.UserRegistrationView.as_view(), name="register"),
url(r'^reset-password/$', view=views.PasswordResetView.as_view(), name="reset-password"),
url(r'^reset-password/confirm/$', views.PasswordResetConfirmView.as_view(), name="reset-password-confirm"),

url(r'^user/profile/$', views.UserProfileView.as_view(), name="profile"),
] + account_router.urls

]
# Django 1.9 Support for the app_name argument is deprecated
# https://docs.djangoproject.com/en/1.9/ref/urls/#include
django_version = django.VERSION
if django.VERSION[:2] >= (1, 9, ):
account_urlpatterns = (account_urlpatterns, 'accounts', )
11 changes: 8 additions & 3 deletions demo/project/accounts/views.py
Expand Up @@ -2,13 +2,13 @@
from django.views.generic.base import TemplateView
from rest_framework import parsers, renderers, generics, status
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.serializers import AuthTokenSerializer
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from project.accounts.models import User
from project.accounts.serializers import (
UserRegistrationSerializer, UserProfileSerializer, ResetPasswordSerializer
UserRegistrationSerializer, UserProfileSerializer, ResetPasswordSerializer, CustomAuthTokenSerializer
)


Expand All @@ -28,7 +28,7 @@ class LoginView(APIView):
permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
serializer_class = AuthTokenSerializer
serializer_class = CustomAuthTokenSerializer

def post(self, request):
serializer = self.serializer_class(data=request.data)
Expand Down Expand Up @@ -81,3 +81,8 @@ def post(self, request, *args, **kwargs):
if not serializer.is_valid():
return Response({'errors': serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
return Response({"msg": "Password updated successfully."}, status=status.HTTP_200_OK)


class UserModelViewset(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserProfileSerializer
17 changes: 16 additions & 1 deletion demo/project/organisations/urls.py
@@ -1,12 +1,27 @@
import django
from django.conf.urls import url
from project.organisations import views

from rest_framework.routers import SimpleRouter
from .views import OrganisationModelViewset

urlpatterns = [
organisation_router = SimpleRouter()
organisation_router.register('organisation-model-viewsets', OrganisationModelViewset, base_name='organisation')

organisations_urlpatterns = [
url(r'^create/$', view=views.CreateOrganisationView.as_view(), name="create"),
url(r'^(?P<slug>[\w-]+)/$', view=views.RetrieveOrganisationView.as_view(), name="organisation"),
url(r'^(?P<slug>[\w-]+)/members/$', view=views.OrganisationMembersView.as_view(), name="members"),
url(r'^(?P<slug>[\w-]+)/leave/$', view=views.LeaveOrganisationView.as_view(), name="leave")
] + organisation_router.urls

members_urlpatterns = [
url(r'^(?P<slug>[\w-]+)/members/$', view=views.OrganisationMembersView.as_view(), name="members"),
]

# Django 1.9 Support for the app_name argument is deprecated
# https://docs.djangoproject.com/en/1.9/ref/urls/#include
django_version = django.VERSION
if django.VERSION[:2] >= (1, 9, ):
organisations_urlpatterns = (organisations_urlpatterns, 'organisations', )
members_urlpatterns = (members_urlpatterns, 'organisations', )
6 changes: 6 additions & 0 deletions demo/project/organisations/views.py
@@ -1,5 +1,6 @@
from rest_framework import generics, status
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from project.organisations.models import Organisation, Membership
from project.organisations.serializers import (
CreateOrganisationSerializer, OrganisationMembersSerializer, RetrieveOrganisationSerializer
Expand Down Expand Up @@ -34,3 +35,8 @@ def delete(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)


class OrganisationModelViewset(ModelViewSet):
queryset = Organisation.objects.all()
serializer_class = OrganisationMembersSerializer
1 change: 0 additions & 1 deletion demo/project/settings.py
Expand Up @@ -43,7 +43,6 @@

'project.accounts',
'project.organisations',

)

MIDDLEWARE_CLASSES = (
Expand Down
32 changes: 27 additions & 5 deletions demo/project/urls.py
Expand Up @@ -13,14 +13,36 @@
1. Add an import: from blog import urls as blog_urls
2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls))
"""
import django
from django.conf.urls import include, url
from django.contrib import admin
from rest_framework_docs.views import DRFDocsView
from .accounts.urls import account_urlpatterns, account_router
from .organisations.urls import organisations_urlpatterns, members_urlpatterns, organisation_router

urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^docs/', include('rest_framework_docs.urls')),

# API
url(r'^accounts/', view=include('project.accounts.urls', namespace='accounts')),
url(r'^organisations/', view=include('project.organisations.urls', namespace='organisations')),
]

# Django 1.9 Support for the app_name argument is deprecated
# https://docs.djangoproject.com/en/1.9/ref/urls/#include
django_version = django.VERSION
if django.VERSION[:2] >= (1, 9, ):
urlpatterns.extend([
url(r'^accounts/', view=include(account_urlpatterns, namespace='accounts')),
url(r'^organisations/', view=include(organisations_urlpatterns, namespace='organisations')),
url(r'^members/', view=include(members_urlpatterns, namespace='members')),
])
else:
urlpatterns.extend([
url(r'^accounts/', view=include(account_urlpatterns, namespace='accounts', app_name='account_app')),
url(r'^organisations/', view=include(organisations_urlpatterns, namespace='organisations', app_name='organisations_app')),
url(r'^members/', view=include(members_urlpatterns, namespace='members', app_name='organisations_app')),
])


routers = [account_router, organisation_router]
urlpatterns.extend([
url(r'^docs/(?P<filter_param>[\w-]+)/$', DRFDocsView.as_view(drf_router=routers), name='drfdocs-filter'),
url(r'^docs/$', DRFDocsView.as_view(drf_router=routers), name='drfdocs'),
])
8 changes: 4 additions & 4 deletions requirements.txt
@@ -1,5 +1,5 @@
Django==1.8.7
djangorestframework==3.3.2
coverage==4.0.3
flake8==2.5.1
Django==2.1.0
djangorestframework==3.7.7
coverage==4.2
flake8<3.0.0
mkdocs==0.15.3
2 changes: 2 additions & 0 deletions rest_framework_docs/__init__.py
@@ -1 +1,3 @@
__version__ = '0.0.11'

SERIALIZER_FIELDS = {}
33 changes: 20 additions & 13 deletions rest_framework_docs/api_docs.py
@@ -1,14 +1,20 @@
from operator import attrgetter
from importlib import import_module
from django.conf import settings
from django.core.urlresolvers import RegexURLResolver, RegexURLPattern
from django.urls import URLResolver, URLPattern
from django.utils.module_loading import import_string
from rest_framework.views import APIView
from rest_framework_docs import SERIALIZER_FIELDS
from rest_framework_docs.api_endpoint import ApiEndpoint


class ApiDocumentation(object):

def __init__(self, drf_router=None):
def __init__(self, drf_router=None, filter_param=None):
"""
:param filter_param: namespace or app_name
"""
SERIALIZER_FIELDS.clear()
self.endpoints = []
self.drf_router = drf_router
try:
Expand All @@ -17,18 +23,19 @@ def __init__(self, drf_router=None):
# Handle a case when there's no dot in ROOT_URLCONF
root_urlconf = import_module(settings.ROOT_URLCONF)
if hasattr(root_urlconf, 'urls'):
self.get_all_view_names(root_urlconf.urls.urlpatterns)
self.get_all_view_names(root_urlconf.urls.urlpatterns, filter_param=filter_param)
else:
self.get_all_view_names(root_urlconf.urlpatterns)
self.get_all_view_names(root_urlconf.urlpatterns, filter_param=filter_param)

def get_all_view_names(self, urlpatterns, parent_pattern=None):
def get_all_view_names(self, urlpatterns, parent_pattern=None, filter_param=None):
for pattern in urlpatterns:
if isinstance(pattern, RegexURLResolver):
parent_pattern = None if pattern._regex == "^" else pattern
self.get_all_view_names(urlpatterns=pattern.url_patterns, parent_pattern=parent_pattern)
elif isinstance(pattern, RegexURLPattern) and self._is_drf_view(pattern) and not self._is_format_endpoint(pattern):
api_endpoint = ApiEndpoint(pattern, parent_pattern, self.drf_router)
self.endpoints.append(api_endpoint)
if isinstance(pattern, URLResolver) and (not filter_param or filter_param in [pattern.app_name, pattern.namespace]):
# parent_pattern = None if pattern._regex == "^" else pattern
self.get_all_view_names(urlpatterns=pattern.url_patterns, parent_pattern=None if pattern.pattern.regex.pattern == "^" else pattern, filter_param=filter_param)
elif isinstance(pattern, URLPattern) and self._is_drf_view(pattern) and not self._is_format_endpoint(pattern):
if not filter_param or parent_pattern:
api_endpoint = ApiEndpoint(pattern, parent_pattern, self.drf_router)
self.endpoints.append(api_endpoint)

def _is_drf_view(self, pattern):
"""
Expand All @@ -40,7 +47,7 @@ def _is_format_endpoint(self, pattern):
"""
Exclude endpoints with a "format" parameter
"""
return '?P<format>' in pattern._regex
return '?P<format>' in pattern.pattern.regex.pattern

def get_endpoints(self):
return self.endpoints
return sorted(self.endpoints, key=attrgetter('name', 'path'))