I'm trying to use Django Reversion with my django-rest, but i still confuse about it or i just couldn't what i want in Document.
Here what i tried
Settings
INSTALLED_APPS = [
....
'reversion'
]
MIDDLEWARE = [
'reversion.middleware.RevisionMiddleware'
]
In Model
#reversion.register()
class History(models.Model):
pass
In ModelViewset
from reversion.views import RevisionMixin
class HistoryViewset(RevisionMixin, viewsets.ModelViewSet):
queryset = History.objects.all()
serializer_class = HistorySerializer
filter_backends = (filters.DjangoFilterBackend,)
# if pagination page = none, it will return all page
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
if 'page' in request.query_params:
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
Then i try to update My Model and try to GET request from api endpoint, i got null Value.
What do i Missing???
I'll appreciate of all ur Help. Thanks...
Related
I have a small messaging API built with DRF which sends messages between users in the system.
My messages view contains several extra actions:
class MessagesViewSet(ModelViewSet):
"""
A simple ViewSet for viewing and editing the messages
associated with the user.
"""
authentication_classes = [TokenAuthentication, ]
permission_classes = [IsAuthenticated]
serializer_class = MessageSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = [MessageFields.MARK_READ]
def get_user(self):
user = self.request.user
return user
def get_queryset(self):
return Message.objects.filter(sent_to=self.get_user())
#action(detail=True)
def sent_messages(self, request, pk):
"""
Return all messages sent by the user.
"""
queryset = Message.objects.filter(sender=self.get_user())
serialized_data = MessageSerializer(queryset, many=True)
return Response(serialized_data.data, status=HTTP_200_OK)
#action(detail=True)
def last_50_messages(self, request, pk):
"""
Return the user's 50 last messages
"""
queryset = Message.objects.filter(sent_to=self.get_user())
serialized_data = MessageSerializer(queryset, many=True)
return Response(serialized_data.data, status=HTTP_200_OK)
Urls file:
from .views import MessagesViewSet
messages_router = DefaultRouter()
messages_router.register(r'messages', MessagesViewSet, basename='messages')
urlpatterns = [
url('', include(messages_router.urls))
]
Right now the only way to access the two custom methods is opening one of the message instances and then add it to the URL line and it'll work.
How can format the url for each method so it will be via the username?
right now:
http://127.0.0.1:8000/api/messages/1/sent_messages/
I looking for something like:
http://127.0.0.1:8000/api/messages/#request.user.username/sent_messages/
You have to change lookup_field value in ModelViewSet like this:
class MessagesViewSet(ModelViewSet):
...
lookup_field = "username"
...
But be careful, API like retrieve will be work with username lookup too, not pk.
To use both (username, lookup) check hook here:
class MultipleFieldLookupORMixin(object):
"""
Actual code http://www.django-rest-framework.org/api-guide/generic-views/#creating-custom-mixins
Apply this mixin to any view or viewset to get multiple field filtering
based on a `lookup_fields` attribute, instead of the default single field filtering.
"""
def get_object(self):
queryset = self.get_queryset() # Get the base queryset
queryset = self.filter_queryset(queryset) # Apply any filter backends
filter = {}
for field in self.lookup_fields:
try: # Get the result with one or more fields.
filter[field] = self.kwargs[field]
except Exception:
pass
return get_object_or_404(queryset, **filter) # Lookup the object
class RetrieveUserView(MultipleFieldLookupORMixin, generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
lookup_fields = ('account', 'username')
I am trying to query on jQuery Datatables list.
Right now I can fetch all records from the table, but I want to fetch records to whom their parent id matches.
For example, users have posts, so in my case it would be fetch posts where user id is lets say 1.
So I am trying to implement same thing for jQuery Datatables.
I can see data is being posted but I can't figure out how to query along with datatables, so that datatables filters are not affected by this change.
My current code:
class PartRequestViewSet(CommonViewset, generics.RetrieveAPIView):
queryset = PartRequest.objects.filter(deleted_at=None)
serializer_class = PartRequestSerializer
def list(self, request, *args, **kwargs):
records = request.GET.get('records', None)
# ID of the part
part_number_id = request.GET.get('part_number_id', None)
queryset = self.get_queryset()
queryset = self.filter_queryset(queryset)
page = self.paginate_queryset(queryset)
if page is not None and records is None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
Now in above code I can get the part_number_id from request, but how can I filter records using filter_queryset(), so that only parts_requests are given back in datatables where part_number_id is 1
Update
Current helper function filter_queryset that is used in above code.
def filter_queryset(self, queryset):
format = self.request.GET.get('format', None)
if format == 'datatables':
self.filter_backends += (DatatablesFilterBackend,)
else:
self.filter_backends += (DjangoFilterBackend,)
for backend in list(self.filter_backends):
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
There is django-filter extension. In your case it may be something like the following:
from rest_framework import viewsets
from django_filters.rest_framework import DjangoFilterBackend
from .models import Post
from .serializers import PostSerializer
class PostList(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['owner']
Then you can use get request like this: /posts?owner=3,
where 3 is user id and owner is ForeignKey in Post model to User.
The way I did is I added the following lines:
if part_number_id:
queryset = queryset.filter(part_number_id=part_number_id)
so the code looks like this now and it is working great. I'm open to better and more efficient options.
class PartRequestViewSet(CommonViewset, generics.RetrieveAPIView):
queryset = PartRequest.objects.filter(deleted_at=None)
serializer_class = PartRequestSerializer
def list(self, request, *args, **kwargs):
records = request.GET.get('records', None)
# ID of the part
part_number_id = request.GET.get('part_number_id', None)
queryset = self.get_queryset()
queryset = self.filter_queryset(queryset)
if part_number_id:
queryset = queryset.filter(part_number_id=part_number_id)
page = self.paginate_queryset(queryset)
if page is not None and records is None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
I am trying to create detail API viewset but for now, I am getting page not found whenever I go to my "matches/api/matches/1/". ListCreateAPIView works fine.
Here are my viewsets:
class MatchViewset(viewsets.ViewSetMixin, generics.ListCreateAPIView):
serializer_class = MatchSerializer
def get_queryset(self):
header_token = self.request.META.get('HTTP_AUTHORIZATION', None)
print(header_token)
if header_token is not None:
try:
token = sub('Token ', '', self.request.META.get('HTTP_AUTHORIZATION', None))
token_obj = Token.objects.get(key=token)
self.request.user = token_obj.user
except Token.DoesNotExist:
pass
print(self.request.user)
return Match.objects.filter(creator=self.request.user)
class MatchDetailViewset(generics.RetrieveUpdateDestroyAPIView):
queryset = Match.objects.all()
serializer_class = MatchSerializer
and routers:
router = routers.DefaultRouter()
router.register(r'matches', matches_views.MatchViewset, base_name="matches")
and urls:
app_name = 'matches'
urlpatterns = [
path('api/', include(router.urls)),
]
According to the docs here: Routers usage
after the router.register(r'matches', matches_views.MatchViewset, base_name="matches") you must give
urlpatterns = router.urls
I guess this would solve the issue
The MatchDetailViewset is not registered in urlpatterns, that's why you're getting a 404 error
It seems like DRF router not support registering multiple viewset with the same prefix.
So maybe you should try something like this:
class MatchViewSet(viewsets.ModelViewSet):
serializer_class = MatchSerializer
queryset = Match.objects.all()
def list(self, request, *args, **kwargs):
header_token = self.request.META.get('HTTP_AUTHORIZATION', None)
print(header_token)
if header_token is not None:
try:
token = sub('Token ', '', self.request.META.get('HTTP_AUTHORIZATION', None))
token_obj = Token.objects.get(key=token)
self.request.user = token_obj.user
except Token.DoesNotExist:
pass
print(self.request.user)
# customize the queryset for listview
queryset = self.get_queryset().filter(creator=self.request.user)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
I have tried many options, but pagination still doesn't work. I'm using generic-views and code like this:
class ListFood(generics.ListAPIView):
queryset = Food.objects.all()
serializer_class = FoodSerializer
def list(self,request):
queryset = self.get_queryset()
serializer = FoodSerializer(queryset, many=True)
return Response(serializer.data)
and in settings.py, I have written this:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS':'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 20
}
I have tried to modify particular aspects of the pagination style, such as:
class StandardResultsSetPagination(PageNumberPagination):
page_size = 100
page_size_query_param = 'page_size'
max_page_size = 1000
class ListFood(generics.ListAPIView):
queryset = Food.objects.all()
serializer_class = FoodSerializer
pagination_class = LargeResultsSetPagination
def list(self,request):
queryset = self.get_queryset()
serializer = FoodSerializer(queryset, many=True)
return Response(serializer.data)
but it still didn't work.
My urls.py is:
app_name = "lab"
urlpatterns = [
re_path('^food/$', ListFood.as_view(), name="listfood"),
]
There is no error message, but when I request the URL http://127.0.0.1:8000/api/food/?page=1 or http://127.0.0.1:8000/api/food/?page=2
it's still not paginated. Django gives me 2000 pieces of data in JSON.
Original list implementation paginates response:
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
Your class overrides list with custom implementation, that is why it does not return paginated response. Just don't override list.
I'm struggling to figure out how to implement the Hyperlinked relationships for non-model querysets. I have a viewset:
class GGGViewSet(viewsets.ViewSet):
def list(self, request):
serializer_class = manufacture_serializer(ar)
serializer = serializer_class(
instance = sample.values(), many=True
)
return Response(serializer.data)
def retrieve(self, request, pk=None):
try:
anobject = sample[pk]
except KeyError:
return Response(status=status.HTTP_404_NOT_FOUND)
except ValueError:
return Response(status=status.HTTP_400_BAD_REQUEST)
serializer_class = manufacture_serializer(ar)
serializer = serializer_class(instance=anobject)
return Response(serializer.data)
I am trying to get the value resource at /data/trait/ to be rendered as a link, where:
trait-list
data/trait/
{
"value": 12334,
"another_value": 45672,
}
trait-detail
data/trait/value/
{
"value":12334
}
Attempted:
url = serializers.HyperlinkedIdentityField(view_name='trait-list')
Error: AttributeError at /asvo/data/trait/ 'AObj' object has no attribute 'pk'.
Any ideas on the best way to approach this would be appreciated. :)
You were probably quite close. Based on the information provided, here's something that demonstrates HyperlinkedIdentityField usage without relying on an actual Django model. I had to use my imagination when filling in the details of your architecture.
from rest_framework import routers
from rest_framework import serializers
from rest_framework import status
from rest_framework import viewsets
from rest_framework.response import Response
# This goes in the URL routing file
router = routers.DefaultRouter()
router.register(r'trait', GGGViewSet, base_name='trait')
urlpatterns = router.urls
# The "model"
class Thing(object):
def __init__(self, pk, value, another_value):
self.pk = pk
self.value = value
self.another_value = another_value
# The "queryset"
sample = {
'1': Thing(1, 12334, 45672),
'2': Thing(2, 12335, 45673),
'3': Thing(3, 12336, 45674)
}
# The serializer
class manufacture_serializer(serializers.Serializer):
pk = serializers.HyperlinkedIdentityField(
view_name='trait-detail', read_only=True)
value = serializers.IntegerField()
another_value = serializers.IntegerField()
class Meta:
fields = ['pk', 'value', 'another_value']
# The view
class GGGViewSet(viewsets.ViewSet):
def list(self, request):
serializer = manufacture_serializer(
instance=sample.values(), many=True, context={'request': request})
return Response(serializer.data)
def retrieve(self, request, pk=None):
try:
anobject = sample[pk]
except KeyError:
return Response(status=status.HTTP_404_NOT_FOUND)
except ValueError:
return Response(status=status.HTTP_400_BAD_REQUEST)
serializer = manufacture_serializer(
instance=anobject, context={'request': request})
return Response(serializer.data)
I did not fully understand the second half of the question regarding the data/trait/ and data/trait/value/, but hopefully this is enough to further you along.
Cheers!