TemplateResponseMixin requires either a definition of 'template_name' or an implementation of 'get_template_names()' in DRF - django-rest-framework

i am trying to confirm email address in django rest framework but i am getting the error
i changed url patterns and import confirm_email view form allauth but still getting the error
urls.py
from allauth.account.views import confirm_email
urlpatterns = [
path('rest-auth/registration/account-confirm-email/(?P<key>.+)/', confirm_email, name='account_confirm_email'),
]
views.py
from django.shortcuts import render
from rest_framework import generics
#for facebook authentication
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
from rest_auth.registration.views import SocialLoginView
from . import models
from . import serializers
class UserListView(generics.ListCreateAPIView):
queryset = models.CustomUser.objects.all()
serializer_class = serializers.UserSerializer
class FacebookLogin(SocialLoginView):
adapter_class = FacebookOAuth2Adapter

Related

Django Rest Framework - How to implement a view to return a javascript file?

I need to serve/return javascript files from a Django Rest Framework api, so in any other website client I can do:
<script src="https://myserveraddress.com/django-api/my-view/my-js-file.js"></script>
and import the content from it to my clients websites by coding this tag on them.
How can i do the view and the url pattern for it?
I want to use DRF because of the CORS policy.
Thanks in advance!
use django HttpResponse and return it
return HttpResponse("<script src="https://myserveraddress.com/django-api/my-view/my-js-file.js"></script>",content_type="application/x-javascript")
HttpResponse comes from django -> from django.http import HttpResponse
Thanks, #mehran heydarian for your contribution.
With your tip and looking inside the rest_framework views module, using views.APIView, I have solved my problem and implemented both the view and the url. Additionaly to the view I have implemented a filter, so I can pass an argument on the url to be filtered on a query:
# views.py
from django.http import HttpResponse
from rest_framework import views
from . models import MYMODEL,
class MYMODELApiView(views.APIView):
#classmethod
def get_extra_actions(cls):
return []
#api_view(('GET',))
def my_def(request, some_string):
"""
some code to implement a condition
for validation
"""
def get_queryset(some_string):
"""
This view returns a string.
"""
if condition:
res = MYMODEL.objects.filter(some_field_from_model=some_string).values('file')
the_file = open('/myserver/directory_before_static/' + res[0]['file'], 'r')
file_content = the_file.read()
the_file.close()
return file_content
else:
the_file = open('/myserver/directory_before_static/static/another_directory/another.js', 'r')
file_content = the_file.read()
the_file.close()
return file_content
file_content = get_queryset(some_string)
return HttpResponse(file_content, content_type="application/javascript", charset='utf-8')
# urls.py
from django.urls import re_path
from .views import MYMODELApiView
urlpatterns = [
...
re_path('^my_url/(?P<some_string>.+)/$', MYMODELApiView.my_def, name='my_url'),
...
]
This works fine to me.

Django Rest Framework Routing: Reverse for 'orders' not found. 'orders' is not a valid view function or pattern name

I'm not quite sure I'm understanding how routing works in DRF. I went through the documentation but still haven't grasp the differences.
I've got the following view:
from django.shortcuts import render
from django.shortcuts import get_object_or_404
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
from .models import Order
from .serializer import OrderSerializer
class OrderAPIViewSet(ViewSet):
def post(self, request):
print(request)
and this is urls.py within my app:
from django.urls import include, path
from rest_framework import routers
from .views import OrderAPIViewSet
router = routers.DefaultRouter()
router.register(r'orders', OrderAPIViewSet, basename='order')
urlpatterns = router.urls
and this is the main urls.py:
from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('products.urls')),
path('', include('orders.urls'))
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
but when I try to access the orders endpoint via a simple test:
ORDERS_URL = reverse('orders')
class PublicOrderApiTests(TestCase):
"""
Test the public facing Order API
"""
def setUp(self):
self.client = APIClient()
def test_sample(self):
data = {
"product_id": 1,
"price": 5.80
}
res = self.client.post(ORDERS_URL, data)
print(res)
I'm getting the following error:
django.urls.exceptions.NoReverseMatch: Reverse for 'orders' not found.
'orders' is not a valid view function or pattern name.
what am I doing wrong? the endpoint for products works just fine but not for orders.
Found the issue or issues in this case:
In my test I had defined the wrong parameter to get the url, so instead of ORDERS_URL = reverse('orders') it needed to beORDERS_URL = reverse('order-list') . *-list is used for GET /orders/ and POST /orders/ while *-detail is for all other endpoints: PUT /orders/{id} GET /orders/{id} etc
The method in my view was post but ViewSetwhich I'm inheriting from doesn't have that method, it has the create method instead, so I needed to rename my post to create

How to disable the browsable API for non staff users (is_staff=False)?

in my case I am using Django REST Framework (DRF) as internal api. it is not intended to be consumed by regular users. therefore I would like to disable it for regular users.
an admin (is_staff=True) should be able to access it and see it:
https://restframework.herokuapp.com/
a non staff user (is_staff=False) should just get the JSON response of a GET request like:
https://restframework.herokuapp.com/?format=json
he should not(!) see the browsable api. this applies for the root view and all endpoints.
to configure this, I applied the following:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework.authentication.SessionAuthentication'],
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated'],
}
my endpoints are the following (to keep the example simple I just show 1):
# api/urls.py
from django.urls import include, path
from rest_framework import routers
from . import views
app_name = 'api'
router = routers.DefaultRouter() # browseable api
router.register('segments', views.SegmentViewSet)
# there are a lot more...
urlpatterns = [
path('', include(router.urls)),
]
based on answer https://stackoverflow.com/a/58894198/420953 my settings.py looks like this:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework.authentication.SessionAuthentication'],
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated'],
# enable JSON renderer by default
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
],
}
and my api/views.py:
# api/views.py
from django_filters import rest_framework as drf_filters
from rest_framework import filters, renderers, viewsets
from . import serializers
from segment.models import Segment
class StaffBrowsableAPIMixin:
def get_renderers(self):
"""
add BrowsableAPIRenderer if user is staff (regular users see JSONRenderer response)
"""
# explicitly set renderer to JSONRenderer (the default for non staff users)
rends = [renderers.JSONRenderer]
if self.request.user.is_staff:
# staff users see browsable API
rends.append(renderers.BrowsableAPIRenderer)
return [renderer() for renderer in rends]
class SegmentViewSet(StaffBrowsableAPIMixin, viewsets.ReadOnlyModelViewSet):
queryset = Segment.objects.all()
serializer_class = serializers.SegmentSerializer
this works fine for all endpoints (when a regular user calls the endpoint via GET, they only see the JSON, not the browsable API). Unfortunately it does not work for APIRootView (the root view of the api, e.g. https://restframework.herokuapp.com/).
how to get this to work for APIRootView as well?
I believe you can lock the base URL of your API down pretty simply (the mixin should probably be moved to another file but just kept everything together for clarity):
# api/urls.py
from django.urls import include, path
from rest_framework import permissions, renderers, routers
from . import views
app_name = 'api'
class StaffBrowsableAPIMixin:
def get_renderers(self):
"""
add BrowsableAPIRenderer if user is staff (regular users see JSONRenderer response)
"""
# explicitly set renderer to JSONRenderer (the default for non staff users)
rends = [renderers.JSONRenderer]
if self.request.user.is_staff:
# staff users see browsable API
rends.append(renderers.BrowsableAPIRenderer)
return [renderer() for renderer in rends]
class CustomAPIRootView(StaffBrowsableAPIMixin, routers.APIRootView):
permission_classes = (permissions.IsAdminUser,)
class CustomDefaultRouter(routers.DefaultRouter):
APIRootView = CustomAPIRootView
router = CustomDefaultRouter() # browseable api
router.register('segments', views.SegmentViewSet)
# there are a lot more...
urlpatterns = [
path('', include(router.urls)),
]
The permission_classes will handle not showing any of your endpoints to non-Admin users but the Browsable API template will still be shown. To remove that as well, you need to change the renderer using the StaffBrowsableAPIMixin.
Original Answer
One way to do this is using DRF's renderer settings and methods.
In your settings.py:
REST_FRAMEWORK = {
# Only enable JSON renderer by default.
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
],
}
And in your views.py:
from rest_framework import generics, renderers
class StaffBrowsableMixin(object):
def get_renderers(self):
"""
Add Browsable API renderer if user is staff.
"""
rends = self.renderer_classes
if self.request.user and self.request.user.is_staff:
rends.append(renderers.BrowsableAPIRenderer)
return [renderer() for renderer in rends]
class CustomListApiView(StaffBrowsableMixin, generics.ListAPIView):
"""
List view.
"""
# normal stuff here
Basically, use StaffBrowsableMixin for any APIView you want the BrowsableAPI to be enabled for staff.
Similar question, as linked above in comments, and my answer there as well: https://stackoverflow.com/a/58762483/4599228

namespace with router.urls in django2

In django2.1.5 and DRF 3.9.1, I am trying to add router.urls namespace which doesn't work.
path('api/v2/', include(router.urls, namespace="v2"))
The error in my terminal is
"Specifying a namespace in include() without providing an app_name '
django.core.exceptions.ImproperlyConfigured: Specifying a namespace in include() without providing an app_name is not supported. Set the app_name attribute in the included module, or pass a 2-tuple containing the list of patterns and app_name instead"
I won't find any suitable solution as I set app_name for a namespace. How can I use the namespace on router.urls or there is no way to use it in Django 2 version?
Trying to add app_name but it won't solve my problem
Here is my code.
config.urls.py
from django.urls import path, include
from django.contrib import admin
from rest_framework import routers
from project.courses import views
router = routers.SimpleRouter()
router.register(r'courses', views.CourseViewSet)
router.register(r'reviews', views.ReviewViewSet)
urlpatterns = [
path('admin/', admin.site.urls),
path('api-auth/', include('rest_framework.urls')),
path('api/v1/courses/', include('project.courses.urls', namespace='courses')),
path('api/v2/', include(router.urls, namespace="v2")),
]
courses.urls.py
from django.urls import path
from . import views
app_name = 'project.courses'
urlpatterns = [
path('', views.ListCreateCourse.as_view(), name='course_list'),
path('<int:pk>', views.RetrieveUpdateDestroyCourse.as_view(),
name='course_detail'),
path('/reviews/', views.ListCreateReview.as_view(), name='review_list'),
path('/reviews/', views.RetrieveUpdateDestroyReview.as_view(), name='review_detail'),
]
Here is the code I want to write.
reviews = serializers.HyperlinkedRelatedField(
many=True,
read_only=True,
view_name='v2:review-detail'
)
I want to access review_detail with namespace v2.
Thanks.
You try like this,
urlpatterns = [
url(r'^api/', include((router.urls, 'app_name'), namespace='instance_name')),
]
Inside the include you can't add namespace. Follow the above way. If you any doubt refer this https://www.django-rest-framework.org/api-guide/routers/

DRF - Get not allowed

I have the following function (showing imports just for the sake of completeness):
from django.utils import timezone
from rest_framework import status
from rest_framework.response import Response
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.views import ObtainAuthToken
class RefreshAuthToken(ObtainAuthToken):
def post(self, request):
key = request.query_params.get('auth_token').strip()
try:
token = self.model.objects.get(key=key)
token.delete()
token = Token.objects.create(user=serializer.object['user'])
token.created = datetime.datetime.utcnow().replace(tzinfo=utc)
token.save()
return Response({'token': token.key})
except self.model.DoesNotExist:
return Response("Error", status=status.HTTP_400_BAD_REQUEST)
When I visit the url with /?auth_token=619f853ac32e171facb3068c990c6eded81a59c9, I get an error:
{"detail":"Method \"GET\" not allowed."}
How can I solve this?
You should request via POST or change the function name to get.

Resources