urls.py
from rest_framework import routers
router = routers.DefaultRouter()
router.register('fan/<str:name>', FanView)
urlpatterns = [
path(r'', include(router.urls)),
]
view.py
class FanView(viewsets.ModelViewSet):
queryset = Fan.objects.all()
serializer_class = FanSerializer
def get_queryset(self):
queryset = Fan.objects.all()
print(self.request.query_params.get('name', None))
return queryset
Hi i am trying to send name in djnago-rest-framework url.
And reading the same in my viewSet.
But, i am always getting None.
I don't wants to send data like fan/?name=foo
Please have a look
Is there any way to achive that ?
What you are trying to access is not in query_params. This is a url parameter and is stored in self.kwargs.lookup_field. You can find here how to access the url parameter.
Related
I got a DjangoREST APIView that supports Read and Create operations. Something like this:
class FirebaseUser(APIView):
...
get(request):
...
post(request):
...
urls.py:
...
path('user/', views.FirebaseUser.as_view()),
...
I need an API that would accept a read request with user id as url param
GET .../api/user/<userId>
But for create operation there's no user ID yet and I need something like this
POST .../api/user/
What is the best way to make my APIView treat url params differently depending on method?
You can define a ModelViewSet like this in your views.py:
from rest_framework import viewsets
class FirebaseUserViewSet(viewsets.ModelViewSet):
queryset = FirebaseUser.objects.all() # or whatever should your queryset be
serializer_class = FirebaseUserSerializer
Then, in your urls.py you register the viewset:
from django.urls import path
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'user', FirebaseUserViewSet)
urlpatterns = [
path('', include(router.urls)),
]
This will create a few new API endpoints and you'll be able to do all the CRUD operations.
I suggest reading a bit more about ModelViewSets in the official docs.
Also, if you require only certain operations, for example only read and create you may consider extending only certain mixins from rest_framework.mixins (read more here).
So, I came up with using ViewSet instead of APIView.
This is how it looks now:
urls.py
path('user/', views.FirebaseUser.as_view({'post': 'create'})),
path('user/<str:pk>', views.FirebaseUser.as_view({'patch': 'update', 'delete': 'destroy'})),
views.py
class FirebaseUser(ViewSet):
authentication_classes = [...]
permission_classes = [...]
#staticmethod
def create(request):
...
#staticmethod
def update(request: Request, pk=None):
uid = pk
...
#staticmethod
def destroy(request: Request, pk=None):
uid = pk
...
I have the following urls.py:
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from location import views
router = DefaultRouter()
router.register('city', views.CityViewSet, 'city')
app_name = 'location'
urlpatterns = [
path('', include(router.urls)),
]
when hitting the url /api/location/city/25/ I get all the details for that object instance (with the id of 25) as expected.
My question how do I grab the id number in my viewset?
I know how to do it with just regular query parameters /?id=25, but can't seem to figure out how to do it when it's part of the URL.
URL captures are available as the kwargs attribute (which is a dict) of the viewset instance. So from inside any viewset method, you can access them via self.kwargs.
In this case, as you're retrieving the instance (GET on detail), you can get the pk (primary key) via:
class CityViewSet(ModelViewSet):
...
def retrieve(self, request, *args, **kwargs):
pk = self.kwargs['pk']
Note that, I've assumed your lookup_url_kwarg is pk (the default); if you have something different you need to access by that key name as you can imagine.
I am working on a simple investment-tracker app, which should get stock prices from an api and display them nicely for the user. I am having trouble, however to pass the necessary data through to the API call.
views.py
class PortfolioData(APIView):
authentication_classes = []
permission_classes = []
def get(self, request, tickers ,format=None):
# how do I pass the tickers?
stock_data = get_multiple_stock_details(tickers) # returns JSON response
return Response(stock_data)
#login_required
def portfolio(request):
user = request.user
user_portfolio = Portfolio.objects.filter(user=user).first()
return render(request, 'app/portfolio.html', {'portfolio':user_portfolio})
urls.py
urlpatterns = [
path('', views.home, name="homepage"),
path('api/portfolio/data/', views.PortfolioData.as_view(),
name="portfolio-data"),
path('portfolio/', views.portfolio, name='portfolio'),
]
On the frontend I would make an ajax call to my PortfolioData view, in order to be able to process the data on the frontend. My biggest issue is how to pass the needed parameters.
I tried to get the ticker symbols from the frontend using jQuery and then pass that to the endpoint url but I am not sure if this is the best way to go about this.
You can try something like this,,,
urls.py
'''
You should bind two different url with same view. One with dynamic variable and another without it.
'''
urlpatterns = [
path('', views.home, name="homepage"),
path('api/portfolio/', views.PortfolioData.as_view(), name='api_portfolio'), # use unique name for each URL
path('api/portfolio/<tickers>/', views.PortfolioData.as_view(), name='api_portfolio_data'), # use unique name for each URL
path('portfolio/', views.portfolio, name='portfolio'),
]
views.py
class PortfolioData(APIView):
authentication_classes = []
permission_classes = []
def get(self, request, tickers=None ,format=None):
if tickers is None:
# GET /api/portfolio/
print('No tickers parameter in URL')
# implement your logic when tickers is not passed.
return Response(JSON_DATA)
# GET /api/portfolio/tickers1/
# implement your logic when tickers is passed.
stock_data = get_multiple_stock_details(tickers) # returns JSON response
return Response(stock_data)
Now, when make HTTP GET request (Does not matter, it's AJAX call or not).
http://localhost:8000/api/portfolio/
tickers variable will be None this case.
http://localhost:8000/api/portfolio/ticker1/
tickers variable will be ticker1 str this case.
Hope, it helps you.
I'm setting up an API Endpoint using Django Rest Framework viewsets and routers, and I'm trying to get the url to accept two values: first, to filter objects by a user_id, and then by the object's id. (In my case, the objects are from a model called Request.) For example, mysite.com/api/requests/1A/ would return all Request objects for user 1A, and mysite.com/api/requests/1A/23/ would return Request object with pk=23 for user 1A.
Right in my urls.py:
# urls.py
from django.conf.urls import url, include
from rest_framework import routers
from . import views
router = routers.DefaultRouter()
router.register(r'requests/(?P<user_id>.+?)(?=\/)', viewset=views.RequestsByUser, base_name='request')
urlpatterns = [
url(r'^', include(router.urls)),
]
# views.py
class RequestsByUser(viewsets.ModelViewSet):
serializer_class = RequestsSerializer
def get_queryset(self):
u_id = self.kwargs['user_id']
return Request.objects.filter(user_id=u_id)
This works well for listing all Request objects when the url is only passed the user_id. But when I try to also pass the object's id example: mysite.com/api/requests/1A/23/, rest framework returns an empty result.
So the url will properly filter by user_id, but won't properly serve the detailed view of an object when given its primary key (object_id). (It looks like the proper page for a detailed view, except it's missing the data for the object.)
Django debugging says that the following four url patterns are in my URLConf:
^api/ ^ ^test/(?P<user_id>.+?)(?=\/)/$ [name='request-list']
^api/ ^ ^test/(?P<user_id>.+?)(?=\/)\.(?P<format>[a-z0-9]+)/?$ [name='request-list']
^api/ ^ ^test/(?P<user_id>.+?)(?=\/)/(?P<pk>[^/.]+)/$ [name='request-detail']
^api/ ^ ^test/(?P<user_id>.+?)(?=\/)/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='request-detail']
I've read through the Django Rest Framework docs for url routing several times, and I feel like I must be missing something. My understanding is the router will automatically create url routing for detailed views based on primary keys, and it looks like it's doing that in the URL Conf. Is my regular expression configured wrong, or maybe something else?
Try something like this:
settings.py
INSTALLED_APPS = [
...
'rest_framework',
'django_filters',
...
]
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
}
serializers.py
import django_filters.rest_framework
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ('id', 'MyField', 'MyFavoriteField','OtherField')
class MyModelListView(generics.ListAPIView):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
filter_backends = (django_filters.rest_framework.DjangoFilterBackend,)
filter_fields = ('id', 'MyField','MyFavoriteField',)
urls.py:
path('service_name/', MyModelListView.as_view(), name="something_name"),
GET:
http://localhost:8070/services/service_name/?id=123&MyField=My%20Field%20Value
More Info:
https://www.django-rest-framework.org/api-guide/filtering/#filtering
I have a ViewSet:
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
And appropriate urls:
from .users.api.views import UserViewSet
router = routers.DefaultRouter()
router.register('users', UserViewSet, 'user')
urlpatterns = [
url(r'^v1/', include(router.urls)),
]
It works, but I want to add username-password authentification to UserViewSet:
#list_route(methods=['post'], permission_classes=[AllowAny])
def login(self, request):
#check login and password
#creare and return token
Of cource I can write It by my-self, but I interest, how I can use rest_framework.authtoken.views.ObtainAuthToken for my goals.
Per the documentation, you can expose an API endpoint that takes a username/password and returns a token using rest_framework.authtoken.view.obtain_auth_token. See the rest framework Docs for more details. You urls.py would look like this:
from .users.api.views import UserViewSet
from rest_framework.authtoken import views
router = routers.DefaultRouter()
router.register('users', UserViewSet, 'user')
urlpatterns = [
url(r'^v1/', include(router.urls)),
url(r'^v1/login, views.obtain_auth_token)
]
If you really want this url to belong to the UserViewSet that you've already defined, you will need to define a detail_route and manually call authenticate and then generate a token for the authenticated user (if authenticate succeeds). I recommend using the first pattern I described as it's less code/customization.