I am using Django REST as backend and angular as front end.
I have two serializers one for readong GET requests and other for POST, PUT requests.
This was because there are few fields like interval, etc which i am entering in integer by user but in the database i am saving as timedelta so i have to multitply them to make them as seconds on front end.
so e.g interval = 5 entered by user and i am posting 5*60*60 to server.
In order to read i have made ReadSerializer where i am diving that by 60*60 to again show to user what he added.
This is working find my problem is after saving my object to database the djnago rest frameework sends the object as it is saved which has interval = 5*60*60. inerval is just an example there 4-5 felds where i am changing them in front end before posting
Is there any way that response used my READ serializer before sending
class Serializer(serializers.ModelSerializer):
interval = serializers.SerializerMethodField('get_interval')
class Meta:
model = User
fields = ('id', 'interval')
def get_interval(self, obj):
return obj.interval/60*60
class WriteSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'interval')
This is the view
class UserListCreateView(generics.ListCreateAPIView):
queryset = User.objects.filter(is_deleted=False)
def get_serializer_class(self):
if self.request.method == 'GET':
return ReadSerializer
return WriteSerializer
You can use the transform_field method which allows you to customize the representation of a field when showing it.
Documentation about it: http://www.django-rest-framework.org/api-guide/serializers/#customizing-field-representation
Btw, careful if you are planning to upgrade to new versions of drf (>=3), these methods may disappear in favour of a unique to_representation method: https://groups.google.com/forum/?fromgroups#!topic/django-rest-framework/9kp5kXCssR4
I would just change it in JavaScript on the front end.
However I have a similar issue and solved it like this, (warning, I just started using DRF, so may not be the best way):
Just change the response of the method directly by first getting the original response, then editing the data property of it, which is a dict (pre-serialized) of the data to return:
class UserListCreateView(generics.ListCreateAPIView):
...
def list(self, request, *args, **kwargs):
response = super().list(request, *args, **kwargs)
response.data['interval'] = response.data['interval'] / 60 / 60
return response
Related
I have a view that I want to use to pull Model data + get data from an external API. Using the same URL path to get database data + external API data, as they are both relatively connected in terms of business logic.
This is how I tried implementing it:
class BucketList(generics.ListAPIView):
permission_classes = [IsAuthenticated]
serializer_class = BucketListSerializer
filter_backends = [OwnerOrUserFilterBackend]
queryset = Bucket.objects.all()
# I use a field in my model as part of my GET request
def get(self, request, *args, **kwargs):
bucket_symbols = Bucket.objects.only('stock_list')
symbols_unicode = (','.join(map(str, bucket_symbols)))
postgrest_urls = f"http://localhost:3000/rpc/bucketreturn?p_stocks=%7B{symbols_unicode}%7D"
response = requests.get(postgrest_urls, headers={'Content-Type': 'application/json'}).json()
return Response(response, status=status.HTTP_200_OK)
The idea of def get(), is to extract every stock_list field in objects, and feed that into my other API on localhost. To clarify, I want every stock_list in object passed into my get requests and returned, for each object.
However I keep getting undefined errors in my JSON response.
How can I properly implement a two-in-one view solution for my view, I still want to keep my original queryset = Bucket.objects.all() in my view.
I am used to using flask for apis and Django for databases and I am trying to learn the DjangoRestAPI. I followed the official Quickstart and another tutorial from medium and did a lot of research but I still struggle to understand how to implement my own function (eg: Doing some calculations or making a request to the Twilio api to send a message after receiving an api request) in a ModelViewSet. I have read that APIView can also be another option but it will making accessing the database quite difficult? When I run a print function in a ModelViewSet class in views.py, or a #action function, it prints but only once at the start of the programme when the server is starting. This is my version of the code in Flask and what I have tried in Django.
Which class should I inherit (APIView or Viewset or ModelViewSet and where can I look for more information about this?
Thank you very much for your kind help in advance: - please note that the flask implementation is a get request
#app.route('/api/upload/<uuid>/<major>/<minor>/<rssi>',methods=['GET'])
def beacon_info_upload(uuid,major,minor,rssi):
if 'uuid' in request.args:
uuid = str(request.args['uuid'])
if 'major' in request.args:
major = str(request.args['major'])
if 'minor' in request.args:
minor = str(request.args['minor'])
if 'rssi' in request.args:
rssi = str(request.args['rssi'])
qualify = send_twilio_message(uuid,major,minor,rssi)
return jsonify(uuid,major,minor,qualify)
Attempt in Django (views.py)
class beacon_occuranceViewSet(viewsets.ModelViewSet):
queryset = beacon_occurance.objects.all().order_by('from_location')
serializer_class = beacon_occuranceSerializer
print("event occ beacon occ views.py L32 success experiment") #only prints once at the beginning
#action(detail=True, methods=['post'])
def register(self, request, pk=None):
print("hello!!!!! L38") #only prints once at the beginning
beacon = self.get_object()
print(beacon)
class beacon_occuranceSerializer(serializers.HyperlinkedModelSerializer):
print("hello") #also only prints once when booting the server
class Meta:
model = beacon_occurance
fields = ('from_location','uuid','major','minor','rssi','time')
Solved - use
def create(self, request):
#do what you want - do access data- do request.data (its a dictionary)
return super().create(request)
refer to Custom function which performs create and update on DRF modelViewSet
I am trying to make an api backend of something like reddit. I want to ensure that whoever is creating a post (model Post) within a particular subreddit is a member of that subreddit (subreddit model is Sub). Here is my latest effort, which works but seems pretty sloppy, and the serializer for some context.
Post permissions.py
class IsMemberOfSubOrReadOnly(BasePermission):
def has_permission(self, request, view):
if request.method in permissions.SAFE_METHODS:
return True
elif request.data:
# prevent creation unless user is member of the sub
post_sub_pk = get_pk_from_link(request.data['sub'])
user = request.user
user_sub_pks = [sub.pk for sub in user.subs.all()]
if not (post_sub_pk in user_sub_pks):
return False
return True
Post serializers.py
from .models import Post
from redditors.models import User
from subs.models import Sub
class PostSerializer(serializers.HyperlinkedModelSerializer):
poster = serializers.HyperlinkedRelatedField(
view_name='user-detail',
#queryset=User.objects.all(),
read_only=True
)
sub = serializers.HyperlinkedRelatedField(
view_name='sub-detail',
queryset=Sub.objects.all()
)
class Meta:
model = Post
fields = ('url', 'id', 'created', 'updated', 'title', 'body',
'upvotes', 'sub', 'poster')
The issue with this approach is that since 'sub' is a hyperlinkedRelatedField on the Post serializer what I get back from request.data['sub'] is just the string hyperlink url. I then have a function, get_pk_from_link that uses regex to read the pk off the end of the url. Then I can use that to grab the actual model I want and check things. It would be nice if there were a more direct way to access the Sub model that is involved in the request.
I have tried searching the fields of the arguments that are available and I can't find a way to get to the Sub object directly. Is there a way to access the Sub model object through the hyperlink url?
I have also solved this problem by just using a serializer field validator (not shown above), but I am interested to know how to do it this way too. Maybe this is just a bad idea and if so please let me know why.
You are right, parsing the url is not the way to go. Since you want to perform the permission check before creating a Post object, I suspect you cannot use object level permissions either, because DRF does not call get_object in a CreateAPIView (since the object does not exist in the database yet).
Considering this is a "business logic" check, a simpler approach would be to not have that permission class at all and perform the check in your perform_create hook in your view (I had asked a similar question about this earlier):
from rest_framework.exceptions import PermissionDenied
# assuming you have a view class like this one for creating Post objects
class PostList(generics.CreateApiView):
# ... other view stuff
def perform_create(self, serializer):
sub = serializer.get('sub') # serializer is already validated so the sub object exists
if not self.request.user.subs.filter(pk=sub.pk).exists():
raise PermissionDenied(detail='Sorry, you are not a member of this sub.')
serializer.save()
This saves you hassle of having to perform that url parsing as the serializer should give you the Sub object directly.
I am using Django REST Framework to create an endpoint that will produce a PDF document. The PDF document will have information that corresponds to a particular Department. I have two desired functionalities -- to be able to download a PDF document, and to be able to preview the document within the browser.
Since the PDF document changes over time based on data that is added to the app, the document needs to be generated in real time when it is requested. As a first step, I'm trying to have the document be generated in a remote file storage location when the following endpoint is hit by a GET request:
departments/<department_pk>/result/preview
Since my endpoint should only take GET requests, I am using a ListAPIView. I'm trying to override the list method so that my custom document generation logic is executed, but it looks like the method is never called. How can I have some custom document generation logic be inserted into my endpoint, so that it is executed when the endpoint is hit by a GET request?
api/urls.py
url(r'^departments/(?P<department_pk>[0-9]+)/result/preview',
include(result_document_urls.result_document_preview_router.urls,
document_app/urls.py
result_document_preview_router = routers.DefaultRouter()
result_document_preview_router.register(r'^', ResultDocumentDetailView.as_view(),
base_name='Department')
document_app/views.py
class ResultDocumentDetailView(generics.ListAPIView):
queryset = Department.objects.all()
lookup_field = 'department_pk'
lookup_url_kwarg = 'department_pk'
def list(self, request, department_pk):
queryset = self.get_queryset()
import ipdb; ipdb.set_trace() # this break point is never hit
department = get_object_or_404(queryset, department_pk=department_pk)
...generate document logic...
return Response(status=status.HTTP_200_OK)
replace list method with below code, I think it will work
class ResultDocumentDetailView(generics.ListAPIView):
queryset = Department.objects.all()
lookup_field = 'department_pk'
lookup_url_kwarg = 'department_pk'
def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
import ipdb; ipdb.set_trace() # this break point is never hit
department = get_object_or_404(
queryset, department_pk=kwargs.get('department_pk')
)
...generate document logic...
return Response(status=status.HTTP_200_OK)
for more reference see the overrinding method "list"
https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/mixins.py#L35
In your document_app/urls.py, you are incorrectly passing ResultDocumentDetailView as an argument instead of a viewset.
Router while registering accepts a ViewSet instead of an APIView.
There are two mandatory arguments to the register() method:
prefix - The URL prefix to use for this set of routes.
viewset - The viewset class.
Also, since you are only interested in the retrieve method, you can just create a ResultDocumentRetrieveView and add its corresponding url to your urls.py without the need of creating a ResultDocument router. (Routers are generally used when you want to handle both list and detail requests.)
class ResultDocumentRetrieveView(generics.RetrieveAPIView):
queryset = Department.objects.all()
lookup_field = 'department_pk'
lookup_url_kwarg = 'department_pk'
def retrieve(self, request, department_pk):
department = self.get_object()
...generate document logic...
return Response(status=status.HTTP_200_OK)
urls.py
url(r'^departments/(?P<department_pk>[0-9]+)/result/preview', ResultDocumentRetrieveView.as_view())
I'am building an API using python 2.7 and django 1.7 and I'm facing a problem. I'm not an expert in Rest Framework but I understand the basic mechanisms.
I can resume my problem by giving you an example. I have a route lets say
/api/project/
Django Rest Framework provides me all basic operations for this route and I don't need to write them e.g:
POST, GET, PUT, DELETE => /api/project/
The fact is, I want to do some operation when I create a new project. I want to add the id of the user who has created the new project.
I want to add some kind of trigger/callback to the create function:
class ProjectViewSet(viewsets.ModelViewSet):
queryset = Project.objects.all()
serializer_class = ProjectSerializer
def create(self, request, *args, **kwargs):
I want to keep the internal behavior of Rest Framework (I don't want to rewrite the route functions), but I want the route to do extra stuff and I need the request object in my trigger/callback. Something like
def callback(request, instance):
instance.created_by = request.user
instance.save()
Do you have any ideas?
Thank you.
You need to add creator_id field to your serializer as well as to model represented by the resource. Then you can do something like this in your view:-
import copy
class ProjectViewSet(viewsets.ModelViewSet):
...
def create(self, request, *args, **kwargs):
data = copy.deepcopy(request.data)
data['creator_id'] = request.user.id
request._data = data
return super(ProjectViewSet, self).create(request, *args, **kwargs)