Django-Rest-Framework Relationships & Hyperlinked API issues - python

I am having a go at the django-rest-framework. It was all going fine until I got to the Relationships & Hyperlinked API part of the tutorial. The error I am getting now after messing with it for a bit is:
ImproperlyConfigured at /api/users/ "^\.(?P<format>[a-z0-9]+)\.(?P<format>[a-z0-9]+)$" is not a valid regular expression: redefinition of group name u'format' as group2; was group 1
I tried doing some research into this but can't seem to find anything and more I mess with it the more that goes wrong
Heres my code:
modules.py
class Home(models.Model):
user = models.ForeignKey(User)
#address ect
serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
username = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='home-detail')
class Meta:
model = User
fields = ('url', 'username', 'home')
class HomeSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.Field(source='owner.username')
highlight = serializers.HyperlinkedIdentityField(view_name='home-highlight', read_only=True, format='html')
class Meta:
model = Home
fields = ('url', 'owner', 'postcode')
api.py
#api_view(('GET',))
def api_root(request, format=None):
return Response({
'users': reverse('user-list', request=request, format=format),
'homes': reverse('home-list', request=request, format=format)
})
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class HomeList(generics.ListCreateAPIView):
queryset = Home.objects.all()
serializer_class = HomeSerializer
class HomeDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Home.objects.all()
serializer_class = HomeSerializer
class HomeHighlight(generics.GenericAPIView):
queryset = Home.objects.all()
renderer_classes = (renderers.StaticHTMLRenderer,)
def get(self, request, *args, **kwargs):
snippet = self.get_object()
return Response(snippet.highlighted)
urls.py
urlpatterns = format_suffix_patterns([
url(r'^$', api.api_root),
url(r'^users/$',
api.UserList.as_view(),
name='user-list'),
url(r'^users/(?P<pk>[0-9]+)/$',
api.UserDetail.as_view(),
name='user-detail'),
url(r'^home/$',
api.HomeList.as_view(),
name='home-list'),
url(r'^home/(?P<pk>[0-9]+)/$',
api.HomeDetail.as_view(),
name='home-detail'),
url(r'^home/(?P<pk>[0-9]+)/highlight/$',
api.HomeHighlight.as_view(),
name='home-highlight')
])
urlpatterns += [
url(r'^api-auth/', include('rest_framework.urls',
namespace='rest_framework')),
]
urlpatterns = format_suffix_patterns(urlpatterns)

You are calling format_suffix_patterns twice, so Django has no idea how to parse the URL because there are two format groups.
You shouldn't need the first call, as the second call takes care of it for you (and allows for TokenAuthentication to still have the suffixes).

Atleast change router.DefaultRouter() to router.SimpleRouter() in urls.py file

Related

DRF filter/search return everything?

I'm currently doing self project inventory app and trying to implement filter and search.
what I've done
#views.py
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters
class ListInventories(generics.ListAPIView):
serializer_class = Inventory
filter_backends = [filters.SearchFilter]
search_fields = ['name']
filter_backends = [DjangoFilterBackend]
filterset_fields = ['name', 'stock']
def get(self, request):
inventories = Inventory.objects.all()
serializer = InventorySerializers(inventories, many=True)
return Response({'inventories': serializer.data})
class InventoryDetails(APIView):
def get(self, request, pk):
inventories = Inventory.objects.get(id=pk)
serializer = InventorySerializers(inventories)
return Response({'inventory': serializer.data})
in settings.py
INSTALLED_APPS = [
...
'django_filters',
...
]
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}
in url:
http://127.0.0.1:8000/api/inventory?name=para
I tried ?search=para and ?search=anynonsense . and the DRF would always return everything.
just for the sake of clarification here is the screenshot(the total of item is just 3 for now, but even then filter/search should work):
I expect the filter/search to function at least on a minimal level. What do I do to make this right?
Cause of the error:
class ListInventories(generics.ListAPIView):
serializer_class = Inventory
# You are settings search filter here
* filter_backends = [filters.SearchFilter]
search_fields = ['name']
# and overriding it here, that's why search is not working
* filter_backends = [DjangoFilterBackend]
filterset_fields = ['name', 'stock']
it should be filter_backends = [DjangoFilterBackend, filters.SearchFilter]
After some time, I solved the problem.
It is the get method that keep returning everything. I commented out the method get and everything works fine.

Django url dispetcher route by id

this is my
views.py
class UserAPI(generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated,]
serializer_class = UserSerializer
def get_object(self):
return self.request.user
and this is my url.py
path('V1/api/users/', UserAPI.as_view(), name='user'),
when i enter id,mail,username and go to
localhost/v1/api/users/1 want to open user's which id is 1.
what is best solutions ?
Your problem comes from your url settings your should add primary key to the url. Change this
path('V1/api/users/', UserAPI.as_view(), name='user')
To this
path('V1/api/users/<pk>/', UserAPI.as_view(), name='user'),
path('V1/api/users/<int:user_id>', UserAPI.as_view(), name='user'),
class UserApi(generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated,]
serializer_class = UserSerializer
def get(self, request, user_id, format=None):
print(user_id) # do whatever you want with the user id

Is it possible to use a django viewset with two serializers?

I have a model for Articles which has a lot of fields. When someone fetches a specific article by id I want to send almost all of the fields back to the client.
However, when the articles list is generated I don't want to send all of the articles with all of that data, but rather limit each article to a few important listing fields, and forgo long fields like content etc..
Can I achieve these with an elegant tweak to the the django_restframework.viewsets module, or should I just build the api methods myself using django_restframwork.generics instead?
example:
# articles/api/urls.py #
# -------------------- #
from articles.api.views import ArticlesViewSet
from django.urls import path
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'', ArticlesViewSet, basename='articles')
urlpatterns = router.urls
# articles/api/views.py #
# --------------------- #
from rest_framework.viewsets import ModelViewSet
from ..models import Article
from .serializers import ArticleSerializerFull, ArticleSerializerShort
class ArticlesViewSet(ModelViewSet):
# Perhaps some conditional code here?
serializer_class = ArticleSerializerFull
queryset = Article.objects.all()
You can use get_serializer_class() method in DRF to solve the problem...
serializers.py
class ArticleFullSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ('__all__')
class ArticleShortSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ('field1', 'field2' ... 'fieldn')
views.py
from rest_framework.viewsets import ModelViewSet
class ArticleViewSet(ModelViewSet):
serializer_class = ArticleSerializerFull
queryset = Article.objects.all()
def get_serializer_class(self):
if self.action == 'list':
serializer = ArticleShortSerializer
else:
serializer = ArticleFullSerializer
return serializer
**i believe you can achieve this by using generics views check_here **
class ArticleFullSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ('__all__')
class ArticleShortSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ('field1', 'field2'..... 'fieldn')
and in your views
class ArticleListView(ListAPIView):
permission_classes = (permissions.IsAuthenticated,)
queryset = Article.objects.all()
serializer_class = ArticleShortSerializer
class ArticleDetailView(RetrieveAPIView):
permission_classes = (permissions.IsAuthenticated,)
queryset = Article.objects.all()
serializer_class = ArticleFullSerializer
lookup_field = 'pk'
****and in urls for details****
path('detail/<int:pk>/', ArticleDetailView.as_view(), name='article-detail'),

How to display only specific data from the entire list storage on python django server (rest framework)

I have to display (as in topic title) only specific data from the list which is storage on server (PYTHON/DJANGO/REST_FRAMEWORK).
F.e. I want to choose data which has planted on the server with 'id=1', but I always get back all items. Can you explain how to correct this?
'urls' file (project level) :
# URL routes - known as endpoints API
urlpatterns = [
path('admin/', admin.site.urls),
path('devices/', include('efota.api.urls')),
]
'urls' file (subordinate folder) :
urlpatterns = [
url('', views.DeviceList.as_view()),
url('<int:pk>/', views.DeviceDetail.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)
'views' file :
class DeviceList(generics.ListAPIView):
queryset = Device.objects.all()
serializer_class = DeviceSerializer
class DeviceDetail(generics.RetrieveAPIView):
queryset = Device.objects.all()
serializer_class = DeviceSerializer
'serializers' file:
class DeviceSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
# the 'fields' controls which database attributes are available
class Meta:
model = Device
fields = (
'id',
'user',
'id_token',
'current_firmware',
'carrier_code',
'model_name',
'owner',
)
read_only_fields = ['id']
def get_url(self, obj):
request = self.context.get("request")
return obj.get_api_url(request=request)
'models' file:
class Device(models.Model):
created = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey('auth.User', related_name='owner',
on_delete=models.CASCADE)
user = models.CharField(max_length=50, verbose_name='user')
id_token = models.CharField(max_length=1000,
verbose_name='id_token')
current_firmware = models.CharField(max_length=41,
verbose_name='current_firmware')
carrier_code = models.CharField(max_length=5,
verbose_name='carrier_code')
model_name = models.CharField(max_length=10,
verbose_name='model_name')
class Meta:
ordering = ('created', )
def __str__(self):
return self.model_name
'admin' file:
from django.contrib import admin
from .models import Device
# Register your models here.
admin.site.register(Device)
You are confusing old-style URL and new-style path syntax in your urls.py. The url() function takes a regex, and r'' matches every URL, while <int:pk>/ would only match that literal string. Use path instead.
urlpatterns = [
path('', views.DeviceList.as_view()),
path('<int:pk>/', views.DeviceDetail.as_view()),
]
To filter result returned by ListAPIView you can modify its get_queryset method as follows.
class DeviceDetail(generics.ListAPIView):
serializer_class = DeviceSerializer
def get_queryset(self):
return Device.objects.filter(id=self.kwargs['pk'])

Django Rest Framework Filtering Not working

Using Django 1.11 & djangorestframework==3.7.7, I want to return Videos where is_thumbnail=True on a GET call. However, all of my testing with filters have been returning all Videos objects.
Model:
class Videos(models.Model):
"""This class represents the Videos model."""
uid = models.UUIDField(
primary_key=True, default=uuid.uuid4, editable=False)
is_thumbnail = models.BooleanField(default=False)
file_name = models.CharField(unique=True, max_length=64)
file_path = models.CharField(unique=True, max_length=256)
file_created_time = models.DateTimeField()
owner = models.ForeignKey('auth.User',
related_name='videos',
on_delete=models.CASCADE)
created_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
"""Return a human readable representation of the model instance."""
return "{}".format(self.file_name)
view:
class DetailsView(generics.RetrieveUpdateDestroyAPIView):
"""This class handles the http GET, PUT and DELETE requests."""
serializer_class = VideosSerializer
permission_classes = (permissions.IsAuthenticated, IsOwner)
lookup_field = 'uid'
def get_queryset(self):
return Videos.objects.filter(is_thumbnail=True)
If I put a print statement inside of the get_queryset function, I don't see that statement in the log. So it seems like the function isn't being called.
api/urls.py
urlpatterns = {
url(r'^auth/', include('rest_framework.urls',
namespace='rest_framework')),
url(r'^api/videos/$', CreateView.as_view(), name="create"),
url(r'^api/videos/(?P<uid>[0-9a-f-]+)/$',
DetailsView.as_view(), name="details"),
url(r'^get-token/', obtain_auth_token),
}
securedash_project/urls.py
urlpatterns = format_suffix_patterns(urlpatterns)
urlpatterns = [
url(r'', include('secureDash.dash.urls')),
url(r'^dash/', include('secureDash.dash.urls')),
url(r'^admin/', admin.site.urls),
url(r'^', include('secureDash.api.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Ok, you want to replace the value of the "get_queryset" method of the generic view "generics.RetrieveUpdateDestroyAPIView", which accesses the objects using the "uid" field setting like lookup_field to make the modification later, so I recommend that when you overwrite it the "get_queryset" method replaces them with the following:
def get_queryset(self):
return self.get_queryset().filter(is_thumbnail=True)
The issue that I was having was that I had a CreateView class that implemented generics.ListCreateAPIView. This view has r/w endpoints so my GET calls were never getting to my DetailsView. I need to adjust my views, but for now this works to only show is_thumbnail=True Videos.
class CreateView(generics.ListCreateAPIView):
"""This class defines the (GET & POST) behavior of the rest api."""
serializer_class = VideosSerializer
permission_classes = (permissions.IsAuthenticated, )
def perform_create(self, serializer):
"""Save the post data when creating a new Videos object."""
serializer.save(owner=self.request.user)
def get_queryset(self):
queryset = Videos.objects.all()
return queryset.filter(is_thumbnail=True)
class DetailsView(generics.RetrieveUpdateDestroyAPIView):
"""This class handles the http GET, PUT and DELETE requests."""
queryset = Videos.objects.all()
serializer_class = VideosSerializer
permission_classes = (permissions.IsAuthenticated, IsOwner)
lookup_field = 'uid'

Categories