Tartiflette + FastApi auth - graphql

I am building a tartiflette app with FastApi using tartiflette-asgi and I can't find a way of making regular FastApi authentication or dependency injection work.
The problem lies in how the tartiflette app is built and mounted. When doing
app = FastApi()
gql_app = TartifletteApp(..)
app.mount("/graphql", gql_app)
I have no way of specifying dependencies to execute my headers validation. I've tried using FastApi include_router but it simply doesn't work with TartifletteApp. I have also tried a small hack like
gql_app = TartifletteApp(..)
app.include_router(
gql_app.router,
prefix="/graphql",
# dependencies=[Depends(get_current_user)], # here I would add a token and get a user
)
I get the error
File "/usr/local/lib/python3.6/site-packages/uvicorn/protocols/http/h11_impl.py", line 389, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "/usr/local/lib/python3.6/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
return await self.app(scope, receive, send)
File "/usr/local/lib/python3.6/site-packages/fastapi/applications.py", line 181, in __call__
await super().__call__(scope, receive, send) # pragma: no cover
File "/usr/local/lib/python3.6/site-packages/starlette/applications.py", line 111, in __call__
await self.middleware_stack(scope, receive, send)
File "/usr/local/lib/python3.6/site-packages/starlette/middleware/errors.py", line 181, in __call__
raise exc from None
File "/usr/local/lib/python3.6/site-packages/starlette/middleware/errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "/usr/local/lib/python3.6/site-packages/starlette/exceptions.py", line 82, in __call__
raise exc from None
File "/usr/local/lib/python3.6/site-packages/starlette/exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "/usr/local/lib/python3.6/site-packages/starlette/routing.py", line 566, in __call__
await route.handle(scope, receive, send)
File "/usr/local/lib/python3.6/site-packages/starlette/routing.py", line 227, in handle
await self.app(scope, receive, send)
File "/usr/local/lib/python3.6/site-packages/tartiflette_asgi/_endpoints.py", line 84, in dispatch
graphiql = get_graphql_config(request).graphiql
File "/usr/local/lib/python3.6/site-packages/tartiflette_asgi/_middleware.py", line 18, in get_graphql_config
config = conn["graphql"]
File "/usr/local/lib/python3.6/site-packages/starlette/requests.py", line 68, in __getitem__
return self.scope[key]
KeyError: 'graphql'
I could implement the headers validation as a graphql middleware but I was hoping I could do it at theFastApi level so it applies to every endpoint.
Any suggestions on how to solve this?

Create a basic auth first, then add it to Dependencies instead of getting the current user this will add a Basic authentication to your endpoint
from fastapi.security import HTTPBasic, HTTPBasicCredentials
security = HTTPBasic()
app.include_router(
gql_app.router,
prefix="/graphql",
dependencies=[Depends(security)],
)

I have managed to solve this without tartiflette-asgi. The solution is posted here
It looks like:
import os
import json
import typing
from starlette.background import BackgroundTasks
from starlette.datastructures import QueryParams
from starlette.requests import Request
from starlette.responses import HTMLResponse, JSONResponse, PlainTextResponse, Response
from tartiflette import Engine
class GraphQLApp:
def __init__(self, app, modules, schema_path=None, error_coercer=None):
self.engine = Engine(
sdl=schema_path or os.path.join(os.path.dirname(__file__), "schema"),
modules=modules,
error_coercer=error_coercer,
)
app.on_event("startup")(self._cook)
async def _cook(self):
await self.engine.cook()
def _build_context(self, **kwargs):
return kwargs or {} # add custom logic when needed here
async def _get_response(self, request: Request, data: QueryParams, context: dict) -> Response:
try:
query = data["query"]
except KeyError:
return PlainTextResponse("No GraphQL query found in the request", 400)
def _format_error(error: typing.Any) -> dict:
import ast
try:
return ast.literal_eval(str(error))
except ValueError:
return {"message": "Internal Server Error"}
background = BackgroundTasks()
context = {"req": request, "background": background, **self._build_context(**context)}
result: dict = await self.engine.execute(
query,
context=context,
variables=data.get("variables"),
operation_name=data.get("operationName"),
)
content = {"data": result["data"]}
has_errors = "errors" in result
if has_errors:
content["errors"] = [_format_error(error) for error in result["errors"]]
status = 400 if has_errors else 200
return JSONResponse(content=content, status_code=status, background=background)
async def process_request(self, request: Request, context: dict = None) -> Response:
content_type = request.headers.get("Content-Type", "")
if "application/json" in content_type:
try:
data = await request.json()
except json.JSONDecodeError:
return JSONResponse({"error": "Invalid JSON."}, 400)
elif "application/graphql" in content_type:
body = await request.body()
data = {"query": body.decode()}
elif "query" in request.query_params:
data = request.query_params
else:
return PlainTextResponse("Unsupported Media Type", 415)
return await self._get_response(request, data=data, context=context or {})
So I can just do
app = FastApi()
gql_app = GraphQLApp(app)
#app.post("/graphql")
async def graphql_ninja(request: Request):
return await gql_app.process_request(request)

Related

How to fix "ERROR - No error handlers are registered, logging exception." with telegram.ext.dispatcher?

I downloaded bot which copy messages from telegram group and sending to my discord channel.
But when I am trying to send a message in a group, I am getting this error.
2022-12-31 08:25:19,877 - telegram.ext.dispatcher - ERROR - No error handlers are registered, logging exception.
Traceback (most recent call last):
File "C:\Python310\lib\site-packages\telegram\ext\dispatcher.py", line 557, in process_update
handler.handle_update(update, self, check, context)
File "C:\Python310\lib\site-packages\telegram\ext\handler.py", line 199, in handle_update
return self.callback(update, context)
File "C:\Discord-Telegram-Bot-main 1\main.py", line 46, in getTgAnnouncement
textUpdate = update.channel_post.text
AttributeError: 'NoneType' object has no attribute 'text'
I am using python 3.10
Python-telegram-bot 13.15
Below i will add the code
import requests
import json
import asyncio
import os
import json
import logging
from dotenv import load_dotenv
import discord
from discord.ext import commands
from telegram import Update
from telegram.ext import Updater,CallbackContext,MessageHandler,Filters
from telegram.utils import helpers
from telegram.ext.dispatcher import run_async
load_dotenv('token.env')
discordToken = os.getenv('DCTOKEN')# discord bot token
telegramToken = os.getenv('TGTOKEN')# telegram bot token
discordChannelId = os.getenv('DCCID')# discord announcement channel id
loop = asyncio.get_event_loop()
#--------------------------------------------------------------------------------------------------------------
bot = commands.Bot(
command_prefix="k!",
case_insensitive=True,
intents=discord.Intents.all(),
help_command=None
)
async def sendDcAnnouncement(textUpdate,nonTextUpdate):
announcementChannel = bot.get_channel(int(discordChannelId))
if textUpdate != None and nonTextUpdate != None:
await announcementChannel.send(textUpdate,file=discord.File(nonTextUpdate))
os.remove(nonTextUpdate)
elif textUpdate == None:
await announcementChannel.send(file=discord.File(nonTextUpdate))
os.remove(nonTextUpdate)
elif nonTextUpdate == None:
await announcementChannel.send(textUpdate)
def getTgAnnouncement(update: Update, context: CallbackContext):
textUpdate = None
nonTextUpdate = None
updateType = helpers.effective_message_type(update)
if updateType == 'text':
textUpdate = update.channel_post.text
else:
textUpdate = update.channel_post.caption
if updateType == 'photo':
nonTextUpdate = update.channel_post.photo[-1].get_file()['file_path']
elif updateType == 'video':
nonTextUpdate = update.channel_post.video.get_file()['file_path']
elif updateType == 'document':
nonTextUpdate = update.channel_post.document.get_file()['file_path']
elif updateType == 'voice':
nonTextUpdate = update.channel_post.voice.get_file()['file_path']
loop.create_task(sendDcAnnouncement(textUpdate,nonTextUpdate))
#bot.event
async def on_ready():
print(f'logged in as {bot.user}')
updater = Updater(token=telegramToken,use_context=True)
dispatcher = updater.dispatcher
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
getTgAnnouncement_handler = MessageHandler((~Filters.command),getTgAnnouncement)
dispatcher.add_handler(getTgAnnouncement_handler)
updater.start_polling()
bot.run(discordToken)
#--------------------------------------------------------------------------------------------------------------
I expect that this bot will send messages and that I will not get these errors.
MessageHandler handles all updates that have one of the fields message, edited_message, channel_post or edited_channel_post. So in getTGAnnouncement, it may very well be the case that update.channel_post is None.
If you want that handler to only handle channel_post update, please use Filters.update.channel_post.
As a side note: I see that you're using python-telegram-bot v13.x. Support for that version is ending soon in favor of the new v20.x.
Disclaimer: I'm currently the maintainer of python-telegram-bot.

Django Customer can't be null

I have an issues with the error return 1048, “Column ‘user_id’ cannot be null” and checked the code but can't find any mistake in it, does somebody else have an idea?
…/store/Serializers.py
class Meta:
model = Customer
fields = ['id', 'user_id', 'phone', 'birth_date', 'membership']
…/store/views.py
class CustomerViewSet(CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, GenericViewSet):
queryset = Customer.objects.all()
serializer_class = CustomerSerializer
#action(detail=False, methods=['GET', 'PUT'])
def me(self, request):
(customer, created) = Customer.objects.get_or_create(user_id=request.user.id)
if request.method == 'GET':
serializer = CustomerSerializer(customer)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = CustomerSerializer(customer, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
…/store/models.py
class Customer(models.Model):
MEMBERSHIP_BRONZE = ‘B’
MEMBERSHIP_SILVER = ‘S’
MEMBERSHIP_GOLD = ‘G’
MEMBERSHIP_CHOICES = [
(MEMBERSHIP_BRONZE, 'Bronze'),
(MEMBERSHIP_SILVER, 'Silver'),
(MEMBERSHIP_GOLD, 'Gold'),
]
phone = models.CharField(max_length=255)
birth_date = models.DateField(null=True, blank=True)
membership = models.CharField(
max_length=1, choices=MEMBERSHIP_CHOICES, default=MEMBERSHIP_BRONZE)
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def str(self):
return f'{self.user.first_name} {self.user.last_name}'
#admin.display(ordering='user__first_name')
def first_name(self):
return self.user.first_name
#admin.display(ordering='user__last_name')
def last_name(self):
return self.user.last_name
class Meta:
ordering = ['user__first_name', 'user__last_name']
…/core/serializers.py
class UserCreateSerializer(BaseUserCreateSerializer):
class Meta(BaseUserCreateSerializer.Meta):
fields = [‘id’, ‘username’, ‘password’, ‘email’, ‘first_name’, ‘last_name’]
class UserSerializer(BaseUserSerializer):
class Meta(BaseUserSerializer.Meta):
fields = [‘id’, ‘username’, ‘email’, ‘first_name’, ‘last_name’]
ERROR MESSAGE!!!
Traceback (most recent call last):
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/django/core/handlers/exception.py”, line 55, in inner
response = get_response(request)
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/django/core/handlers/base.py”, line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/django/views/decorators/csrf.py”, line 54, in wrapped_view
return view_func(*args, **kwargs)
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/rest_framework/viewsets.py”, line 125, in view
return self.dispatch(request, *args, **kwargs)
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/rest_framework/views.py”, line 509, in dispatch
response = self.handle_exception(exc)
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/rest_framework/views.py”, line 469, in handle_exception
self.raise_uncaught_exception(exc)
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/rest_framework/views.py”, line 480, in raise_uncaught_exception
raise exc
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/rest_framework/views.py”, line 506, in dispatch
response = handler(request, *args, **kwargs)
File “/Users/rogerlooser/Documents/Privat_Roger/Schule/Python/storefront/store/views.py”, line 91, in me
(customer, created) = Customer.objects.get_or_create(user_id=request.user.id)
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/django/db/models/manager.py”, line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/django/db/models/query.py”, line 935, in get_or_create
return self.create(**params), True
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/django/db/models/query.py”, line 671, in create
obj.save(force_insert=True, using=self.db)
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/django/db/models/base.py”, line 812, in save
self.save_base(
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/django/db/models/base.py”, line 863, in save_base
updated = self._save_table(
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/django/db/models/base.py”, line 1006, in _save_table
results = self._do_insert(
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/django/db/models/base.py”, line 1047, in _do_insert
return manager._insert(
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/django/db/models/manager.py”, line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/django/db/models/query.py”, line 1790, in _insert
return query.get_compiler(using=using).execute_sql(returning_fields)
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/django/db/models/sql/compiler.py”, line 1660, in execute_sql
cursor.execute(sql, params)
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/src/django-debug-toolbar/debug_toolbar/panels/sql/tracking.py”, line 230, in execute
return self._record(self.cursor.execute, sql, params)
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/src/django-debug-toolbar/debug_toolbar/panels/sql/tracking.py”, line 154, in _record
return method(sql, params)
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/django/db/backends/utils.py”, line 103, in execute
return super().execute(sql, params)
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/django/db/backends/utils.py”, line 67, in execute
return self._execute_with_wrappers(
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/django/db/backends/utils.py”, line 80, in _execute_with_wrappers
return executor(sql, params, many, context)
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/django/db/backends/utils.py”, line 89, in _execute
return self.cursor.execute(sql, params)
File “/Users/rogerlooser/.local/share/virtualenvs/storefront-JGOY8xqq/lib/python3.10/site-packages/django/db/backends/mysql/base.py”, line 80, in execute
raise IntegrityError(*tuple(e.args))
django.db.utils.IntegrityError: (1048, “Column ‘user_id’ cannot be null”)
[26/Dec/2022 15:16:38] “GET /store/customers/me/ HTTP/1.1” 500 206743
/Users/rogerlooser/Documents/Privat_Roger/Schule/Python/storefront/store/views.py changed, reloading.
Watching for file changes with StatReloader
Performing system checks…
Maybe somebody of you guys find the issue

Log And Mention User Who Created Role in server discord.py

Hi I Try to make Logger Bot in discord.py
My Code:
#client.event
async def on_guild_role_create(guild, role):
logs = await guild.audit_logs(limit=1, action=discord.AuditLogChanges.role_create).flatten()
channel = guild.get_channel(channel_ID)
logs = logs[0]
if logs.target == role:
#await channel.send(f'{logs.user} Created Role.')
Error I Got:
Ignoring exception in on_guild_role_create
Traceback (most recent call last):
File "C:\Users\MY PC\AppData\Roaming\Python\Python38\site-packages\discord\client.py", line 343, in _run_event
await coro(*args, **kwargs)
File "C:\Users\MY PC\source\repos\Event\Event\Event.py", line 32, in on_guild_role_create
logs = await guild.audit_logs(limit=1, action=discord.AuditLogChanges.role_create).flatten()
AttributeError: 'Role' object has no attribute 'audit_logs'
What should I do?
on_guild_role_create only takes in one parameter, the role. We have to get the guild instance form the role.
async def on_guild_role_create(role):
guild = role.guild
#other stuff here
References:
role.guild
on_guild_role_create

FastAPI + GraphQL getting error NoneType is callable when raise Exception

I'm stuck when trying to raise a validation error with FastAPI + GraphQL (graphene).
I have a resolver code:
class Query(graphene.ObjectType):
list_categories = graphene.List(CategoryGrapheneModel)
get_category = graphene.Field(CategoryGrapheneModel, id=graphene.Argument(graphene.Int, required=True))
#staticmethod
def resolve_list_categories(parent, info):
return Category.all()
#staticmethod
def resolve_get_category(parent, info, id):
try:
category = Category.find_or_fail(id)
return category
except ModelNotFound as ex:
raise Exception('Category not found')
But instead of getting 400 HTTP response with the message I got 500 Internal Server Error with traceback:
Traceback (most recent call last):
File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/uvicorn/protocols/http/h11_impl.py", line 394, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
return await self.app(scope, receive, send)
File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/fastapi/applications.py", line 199, in __call__
await super().__call__(scope, receive, send)
File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/applications.py", line 111, in __call__
await self.middleware_stack(scope, receive, send)
File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 181, in __call__
raise exc from None
File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in __call__
raise exc from None
File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/routing.py", line 566, in __call__
await route.handle(scope, receive, send)
File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/routing.py", line 227, in handle
await self.app(scope, receive, send)
File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/graphql.py", line 52, in __call__
response = await self.handle_graphql(request)
File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/graphql.py", line 105, in handle_graphql
[format_graphql_error(err) for err in result.errors]
File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/graphql.py", line 105, in <listcomp>
[format_graphql_error(err) for err in result.errors]
TypeError: 'NoneType' object is not callable
Googling is not gives me a solution. So please help.
Got handled with it.
The problem was in GraphQLApp, there was calling format_graphql_errors that was undefined (None).
To solve a problem I've created a custom child class from GraphQLApp and changed format_graphql_errors to format_error from graphql.error.graphql_error package.
import json
import typing
from starlette.graphql import GraphQLApp
from starlette import status
from starlette.background import BackgroundTasks
from starlette.concurrency import run_in_threadpool
from starlette.requests import Request
from starlette.responses import HTMLResponse, JSONResponse, PlainTextResponse, Response
from starlette.types import Receive, Scope, Send
from graphql.error.graphql_error import format_error
class CustomGraphQLApp(GraphQLApp):
async def handle_graphql(self, request: Request) -> Response:
if request.method in ("GET", "HEAD"):
if "text/html" in request.headers.get("Accept", ""):
if not self.graphiql:
return PlainTextResponse(
"Not Found", status_code=status.HTTP_404_NOT_FOUND
)
return await self.handle_graphiql(request)
data = request.query_params # type: typing.Mapping[str, typing.Any]
elif request.method == "POST":
content_type = request.headers.get("Content-Type", "")
if "application/json" in content_type:
data = await request.json()
elif "application/graphql" in content_type:
body = await request.body()
text = body.decode()
data = {"query": text}
elif "query" in request.query_params:
data = request.query_params
else:
return PlainTextResponse(
"Unsupported Media Type",
status_code=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
)
else:
return PlainTextResponse(
"Method Not Allowed", status_code=status.HTTP_405_METHOD_NOT_ALLOWED
)
try:
query = data["query"]
variables = data.get("variables")
operation_name = data.get("operationName")
except KeyError:
return PlainTextResponse(
"No GraphQL query found in the request",
status_code=status.HTTP_400_BAD_REQUEST,
)
background = BackgroundTasks()
context = {"request": request, "background": background}
result = await self.execute(
query, variables=variables, context=context, operation_name=operation_name
)
error_data = (
[format_error(err) for err in result.errors]
if result.errors
else None
)
response_data = {"data": result.data}
if error_data:
response_data["errors"] = error_data
status_code = (
status.HTTP_400_BAD_REQUEST if result.errors else status.HTTP_200_OK
)
return JSONResponse(
response_data, status_code=status_code, background=background
)
I hope this will help others.
Based on the great answer from #Vitaly Radchik, I created more universal and compressed code by redefining the existing handle_graphql after format_graphql_error is defined.
import json, typing, inspect
from starlette import status
from starlette.background import BackgroundTasks
from starlette.requests import Request
from starlette.responses import JSONResponse, PlainTextResponse, Response
from graphql.error.graphql_error import format_error as format_graphql_error
# Get source of `handle_graphql` function
code = inspect.getsource(GraphQLApp.handle_graphql).lstrip()
class CustomGraphQLApp(GraphQLApp):
# Redefine handle_graphql function
exec(code)

Django Rest Framework: Exception handler not working

The Django Rest Framework exception handler doesn't seem to be working for me. ValidationErrors are getting turned into 500 responses.
When a ValidationError is raised, it doesn't get converted into a 400.
Traceback (most recent call last):
File "/example/.local/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/example/.local/lib/python3.6/site-packages/django/core/handlers/base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/example/.local/lib/python3.6/site-packages/django/core/handlers/base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/example/.local/lib/python3.6/site-packages/django/views/generic/base.py", line 71, in view
return self.dispatch(request, *args, **kwargs)
File "/example/.local/lib/python3.6/site-packages/django/views/generic/base.py", line 97, in dispatch
return handler(request, *args, **kwargs)
File "/example/app/views.py", line 25, in post
serializer.is_valid(raise_exception=True)
File "/example/.local/lib/python3.6/site-packages/rest_framework/serializers.py", line 242, in is_valid
raise ValidationError(self.errors)
rest_framework.exceptions.ValidationError: {'email': [ErrorDetail(string='Enter a valid email address.', code='invalid')]}
[26/Feb/2020 20:44:54] "POST /login/ HTTP/1.1" 500 84465
In settings.py I have
INSTALLED_APPS = [
# ...
'rest_framework',
]
But I get the same behavior whether I have rest_framework in my INSTALLED_APPS or not.
Adding this to settings.py has no effect either:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'bla',
}
Am I missing something obvious?
It turns out it depends on the view you're sub-classing while throwing the exception.
I was subclassing View, which did not work:
from rest_framework import views
from rest_framework.exceptions import ValidationError
class LoginView(views.View):
def post(self, request):
raise ValidationError()
Switching to using GenericAPIView fixed my issue:
from rest_framework import generics
from rest_framework.exceptions import ValidationError
class LoginView(generics.GenericAPIView):
def post(self, request):
raise ValidationError()

Resources