Tastypie how to get custom Json Response after injecting Post data - json

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

POST and GET requests to a client API URL using Django REST

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

How to use background tasks with Starlette when there's no background 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")

Django:JSON response and render in function based view

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.

Format ValidationError in Serializer

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.

Django Rest Framework: Retrieving object count from a model

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.