i want to have custom json response after data post sendind to my Tastypie API models django.
class MyModelResource(ModelResource):
my_field=""
class Meta:
queryset = MyModel.objects.all()
resource_name = 'nick_name'
authentication = ApiKeyAuthentication()
authorization = DjangoAuthorization()
def hydrate(self, bundle):
#on recupere les donnée injectée par bundle.data['title']
#et on inject les donnée via bundle.obj.title
#bundle.data['my_field'] ="1234"
bundle.obj.my_field=bundle.data['my_field']
self.my_field = bundle.data['my_field']
return bundle
def wrap_view(self, view):
"""
Wraps views to return custom error codes instead of generic 500's
"""
#csrf_exempt
def wrapper(request, *args, **kwargs):
try:
callback = getattr(self, view)
response = callback(request, *args, **kwargs)
if request.is_ajax():
patch_cache_control(response, no_cache=True)
lst_dic=[]
mon_dic = dict(success=True, my_field=self.my_field
)
# response is a HttpResponse object, so follow Django's instructions
# to change it to your needs before you return it.
# https://docs.djangoproject.com/en/dev/ref/request-response/
lst_dic.append(mon_dic)
response = HttpResponse(simplejson.dumps(lst_dic), content_type='application/json')
return response
except (BadRequest, fields.ApiFieldError), e:
return HttpBadRequest({'success':False,'code': 666, 'message':e.args[0]})
except ValidationError, e:
# Or do some JSON wrapping around the standard 500
return HttpBadRequest({'success':False,'code': 777, 'message':', '.join(e.messages)})
except Exception, e:
# Rather than re-raising, we're going to things similar to
# what Django does. The difference is returning a serialized
# error message.
return self._handle_500(request, e)
return wrapper
My problem here, i can't grab the self.my_field value to put in mon_dic, i always have data object, not value...
thx for help
EDIT : Add my_field global variable, and then grab value from bundle that's it ;)
Maybe I am not understanding what you want to do here. But wrap_view is for handling customer error responses. If all you want to do is return the data that was posted, you can set always_return_data to true in your Meta:
class Meta:
always_return_data = True
Or if you want to control what data gets sent back, you can use the dehydrate method:
def dehydrate(self, bundle):
bundle.data['custom_field'] = "Whatever you want"
return bundle
Related
I am kind of new to API.
I am working on a project where I can send POST and GET requests to different APIs. I just want to know how sort of the class in the view file should look like.
For example, I have a class that inherits generics.GenericAPIView. How do I send a get request to a specific URL or how do I save the data using the serializer to the database with post request?
class ArticelViewSet(generics.GenericAPIView, mixins.ListModelMixin, mixins.CreateModelMixin):
serializer_class = ArticleSerializer_Modelserializers
queryset = Article.objects.all()
lookup_field = 'id'
def get(self, request, id=None):
if id:
return self.retrieve(request)
else:
return self.list(request)
def post(self, request):
return self.create(request) #return the created object
I'm hoping to avoid any use of Celery at the moment. In Starlette's docs they give two ways to add background tasks:
Via Graphene: https://www.starlette.io/graphql/
class Query(graphene.ObjectType):
user_agent = graphene.String()
def resolve_user_agent(self, info):
"""
Return the User-Agent of the incoming request.
"""
user_agent = request.headers.get("User-Agent", "<unknown>")
background = info.context["background"]
background.add_task(log_user_agent, user_agent=user_agent)
return user_agent
Via a JSON response: https://www.starlette.io/background/
async def signup(request):
data = await request.json()
username = data['username']
email = data['email']
task = BackgroundTask(send_welcome_email, to_address=email)
message = {'status': 'Signup successful'}
return JSONResponse(message, background=task)
Does anyone know of a way to add tasks to Starlette's background with Ariadne? I am unable to return a JSONResponse in my resolver, and I do not have access to a info.context["background"]. The only thing I have attached to my context is my request object.
Solved!
Starlette Middleware:
class BackgroundTaskMiddleware(BaseHTTPMiddleware):
async def dispatch(
self, request: Request, call_next: RequestResponseEndpoint
) -> Response:
request.state.background = None
response = await call_next(request)
if request.state.background:
response.background = request.state.background
return response
Ariadne Resolver:
#query.field("getUser")
#check_authentication
async def resolve_get_user(user, obj, info):
task = BackgroundTasks()
task.add_task(test_func)
task.add_task(testing_func_two, "I work now")
request = info.context["request"]
request.state.background = task
return True
async def test_func():
await asyncio.sleep(10)
print("once!!")
async def testing_func_two(message: str):
print(message)
The functions still execute synchronously, but because they're background tasks I'm not too worried.
More discussion here.
The above which is marked as a solution does not work for me since BackgroundTask does not work properly when you use a middleware that subclasses BaseHTTPMiddleware see here:
https://github.com/encode/starlette/issues/919
In my case basically the task is not ran in the background and it is awaited to be completed, also I am not using Ariadne, but this should let you do the job and run a task in the background
Edit:
This worked for me.
executor = ProcessPoolExecutor()
main.executor.submit(
bg_process_funcs,
export_file_format,
export_headers,
data,
alert_type,
floor_subtitle,
date_subtitle,
pref_datetime,
pref_timezone,
export_file_name,
export_limit,)
executor.shutdown()
logger.info("Process Pool Shutdown")
Im working on django rest framework and using a function based views. Im using 2 functions, one to render html and another for json response. How can I combine both the function to have more effecient code
My code
def register(request):
return render(request, 'register.html')
#api_view(['POST'])
#permission_classes((AllowAny,))
def create_user(request):
if request.method == 'POST':
serializer = SignupSerializer(data=request.data)
print 'ser'
print serializer
if not serializer.is_valid():
return Response(serializer.errors,\
status=status.HTTP_400_BAD_REQUEST)
else:
serializer.save()
return Response({
'status': 'Created',
'message': 'Verification email has been sent to your email. Please verify your account.'
}, status=status.HTTP_201_CREATED)
This can be handled by your serializer, as described in part2 of the drf tutorial.
It works like this:
urls.py
from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
from your_app import views
urlpatterns = [
url(r'^blahblah/$', views.create_user),
]
urlpatterns = format_suffix_patterns(urlpatterns)
views.py
def create_user(request, format=None): # add this format argument
...
Based on the format argument, you can decide you want to the request to be rendered.
DjangoRestFramework seems to handle errors with a variety of ways. The ValidationError in the serializer class does not consistently return JSON the same.
Current response includes a JSON list/object string:
{"detail":["Unable to log in with provided credentials."]}
Looking to achieve:
{"detail":"Unable to log in with provided credentials."}
I realize that this response is a result of default functions. However, I've overridden the validate function:
class AuthCustomTokenSerializer(serializers.Serializer):
username = serializers.CharField(write_only=True)
password = serializers.CharField(write_only=True)
token = serializers.CharField(read_only=True)
def validate(self, validated_data):
username = validated_data.get('username')
password = validated_data.get('password')
# raise serializers.ValidationError({'detail': 'Unable to log in with provided credentials.'})
if username and password:
user = authenticate(phone_number=username, password=password)
try:
if UserInfo.objects.get(phone_number=username):
userinfo = UserInfo.objects.get(phone_number=username)
user = User.objects.filter(user=userinfo.user, password=password).latest('date_joined')
if user:
if user.is_active:
validated_data['user'] = user
return validated_data
else:
raise serializers.ValidationError({"detail": "User account disabled."})
except UserInfo.DoesNotExist:
try:
user = User.objects.filter(email=username, password=password).latest('date_joined')
if user.is_active:
validated_data['user'] = user
return validated_data
except User.DoesNotExist:
#raise serializers.ValidationError("s")
raise serializers.ValidationError({'detail': 'Unable to log in with provided credentials.'})
else:
raise serializers.ValidationError({"detail" : "Must include username and password."})
class Meta:
model = Token
fields = ("username", "password", "token")
I've tried adding a custom exception handler:
from rest_framework.views import exception_handler
def custom_exception_handler(exc, context):
# Call REST framework's default exception handler first,
# to get the standard error response.
response = exception_handler(exc, context)
# Now add the HTTP status code to the response.
if response is not None:
response.data['status_code'] = response.status_code
return response
views.py: if serializer.is_valid(raise_exception=True):
However, that only appends the currently raised error:
{"detail":["Unable to log in with provided credentials."],"status_code":400}
How should I use change the format of the returning text?
It only returns the JSON like this for this particular serializer within the validate function.
I've also looked into formatting the non_field_errors template, but it works with all my other serializers e.g:
{"detail": "Account exists with email address."}
Maybe you should try overriding json renderer class and hook up a custom one, where you can check for status code and detail key in response data, then re-format the value appropriately.
I never tried that, so I can't give you the exact codebase, but this is the only approach I can think of to have consistent response.
Does anyone know how can I successfully retrieve the object count of a model, in JSON format, and how I need to configure my routing? I'm trying to achieve this using a APIView and returning a Response formatted by JSONRenderer.
UPDATE:
#api_view(['GET'])
#renderer_classes((JSONRenderer, JSONPRenderer))
def InfluenciasCountView(request, format=None):
influencia_count = Influencia.objects.count()
content = {'influencia_count': influencia_count}
return Response(content)
Here's the route I'm using:
url(r'^influencias/count/$', views.InfluenciasCountView, name='influencias-count')
Check out this snippet of code (the second one). If this does not suit your need, please add some of your code (for better understanding).
UPDATE
For routing, DRF offers a default router for each view. This means that you can have the following configuration in your urls.py: (using the example from the previous link)
url(r'^users/count/$', views. UserCountView.as_view(), name='users-count')
Then, when you access the URL your_base_url/users/count/ you will see something like {'user_count': 10}.
UPDATE 2
The entire code should look like this:
class UserCountView(APIView):
"""
A view that returns the count of active users.
"""
renderer_classes = (JSONRenderer, )
def get(self, request, format=None):
user_count = User.objects.count()
content = {'user_count': user_count}
return Response(content)
I am using routers from REST Framework to build my URLs. I tried above code but doesn't get it working. One of the problems is I cannot get /count/ into the router endpoints.
I checked DRF document (3.8.2) and found that there is a (new?) #action decorator (I was using 3.7.7 and it doesn't have it). So, here is my full solutions:
Upgrade DRF to 3.8.2 (or above) in requirements.txt (or PipFile if you using that).
Add a new action count method to the ModelViewSet
Update get_permissions to include the newly added action count
Here is my views.py:
from rest_framework.decorators import action
from rest_framework.response import Response
class PostViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows recommend to be viewed or edited.
"""
model = Post
queryset = Post.objects.filter(is_active=True)
serializer_class = serializers.PostSerializer
filter_backends = (filters.SearchFilter, DjangoFilterBackend,)
search_fields = ('title', 'body',)
filter_fields = ('status', 'type')
def get_permissions(self):
if self.action in ('list', 'retrieve', 'create', 'count'):
return (AllowAny()),
if self.action in ('update', 'partial_update'):
return (IsAdminUser()),
return (IsAdminUser()),
#action(detail=False)
def count(self, request):
queryset = self.filter_queryset(self.get_queryset())
count = queryset.count()
content = {'count': count}
return Response(content)
To query count of posts: /api/posts/count/?format=json
To query count of published: /api/posts/count/?format=json&status=published
One of the important thing here is to use the queryset from filter_queryset(...), rather than Post.objects.all().
UPDATE
Since count is common, I created a mixin for that.
from rest_framework.decorators import action
from rest_framework.response import Response
class CountModelMixin(object):
"""
Count a queryset.
"""
#action(detail=False)
def count(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
content = {'count': queryset.count()}
return Response(content)
To use it, just add CountModelMixin to your ModelViewSet (also support nested ModelViewSet).
class PostViewSet(viewsets.ModelViewSet, CountModelMixin):
If you use permissions, also add 'count' to the list of granted action.