Parameters URL with DRF routers - django-rest-framework

I'm using Django Rest Framework for created a API. In this project i want to capture parameters in the URL. For example i want to capture the username and password of a user and my idea is like this:
http://localhost:8000/accounts/login/?unsername=username&password=password
But i cant, I' usin routers and django-filter, but i cant get the parameters url. My project files are there:
view.py:
class AccountsData(viewsets.ModelViewSet):
queryset = models.UserData.objects.all()
serializer_class = serializers.AccountsDataSerializer
permission_classes = (IsAuthenticated,)
filter_backends = (filters.DjangoFilterBackend,)
filterset_fields = ['username', 'password']
lookup_url_kwarg = 'username'
#action(methods=['get'], detail=True, url_name='login', url_path='login')
def login(self, request, pk=None):
return Response({"Login successfully"}, 200)
urls.py:
from api import views
router = routers.SimpleRouter()
router.register(r'accounts', views.AccountsData)

Request query parameters have nothing to do with routing, they are passed with the request independently of how you configure the route. You have access to them in request.query_params, for example, request.query_params.get('username') would get the value of the username parameter.
Being said that, your idea has a terrible mistake: password or any kind of confidential data should NEVER go in query parameters, you should use an http verb that carries the data in its body (POST, for example).

Related

Django rest framework XLSX renderer + Apiview

I'm setting up an endpoint on my API which should return a XLSX-file. The DRF-docs mention https://github.com/wharton/drf-renderer-xlsx as the main external rendering package, aside from the pandas which also seem to be able to render XLSX.
In their code example they use a ReadOnlyViewset paired with a mixin, but there is no mention of how it's used with APIViews. Still, I would like to use an APIView as shown by this https://harshahegde.dev/rendering-xlsx-files-in-django-rest-framework-ckagk293p00eumks1bf4dlhie
However..
This works great when using CURL or Postman, but when done through a browser I get this error:
'Request' object has no attribute 'accepted_renderer'
From what I understand this is because there is no Accept header set (e.g 'Accept':'application/xlsx')
I fixed this by removing the Mixin from the renderer_classes, so it simply returns a file called "xlsx" but I can't figure out how to set the filename without the mixin. How do I set the filename using an APIView trying to access the URL from a browser?
My view:
class SomedataXlsx(APIView):
renderer_classes = [XLSXRenderer, JSONRenderer]
def get(self, request):
queryset = Somedata.objects.all()
serializer = SomeDataSerializer(queryset, many=True)
return Response(serializer.data)
Looking at the mixin code it became clear they change the content-disposition header, and so since the DRF Response() takes a header argument I tried changing it, and it worked perfectly.
class SomedataXlsx(APIView):
renderer_classes = [XLSXRenderer, JSONRenderer]
def get(self, request):
user_sub_fund_data = Somedata.objects.all()
serializer = SomeDataSerializer(queryset, many=True)
return Response(serializer.data, headers={"content-disposition":"attachment; filename=mynewfilename.xlsx"})

Getting user details from access token in Django rest framework -simple JWT

I am using React and Django rest framework for a project. I use Django rest framework simple JWT for authentication. Now, I want to display the username in the navbar after the user logs in. So, is there a way in simple JWT for returning user details from the access token generated after authentication, just like Djoser returns user credentials when supplied the access token?
Sorry if this question is silly but I was not able to find a solution to this anywhere.
if you want to obtain the information of the owner of the token you can consult it in REQUEST.
class ViewProtect(APIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def post(self, request, format=None):
token_user_email = request.user.email
token_user_username = request.user.username
pass
About the backend, basically I use this library
from restframework_simplejwt.tokens import AccessToken
The function AccessToken() take as input the string access_token_str and return the object access_token_obj.
To get the user_id, you can use the instruction
user_id=access_token_obj['user_id'].
In the following example I have created the function
get_user_from_access_token_in_django_rest_framework_simplejwt().
This function is just a wrapper around AccessToken()
Full code:
#Path current file
#/blabla/django/project004/core/view.py
from restframework_simplejwt.tokens import AccessToken
from django.contrib.auth.models import User
#Example data.
#access_token_str = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3BrIjoxLCJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiY29sZF9zdHVmZiI6IuKYgyIsImV4cCI6MTIzNDU2LCJqdGkiOiJmZDJmOWQ1ZTFhN2M0MmU4OTQ5MzVlMzYyYmNhOGJjYSJ9.NHlztMGER7UADHZJlxNG0WSi22a2KaYSfd1S-AuT7lU'
def get_user_from_access_token_in_django_rest_framework_simplejwt(access_token_str):
access_token_obj = AccessToken(access_token_str)
user_id=access_token_obj['user_id']
user=User.objects.get(id=user_id)
print('user_id: ', user_id )
print('user: ', user)
print('user.id: ', user.id )
content = {'user_id': user_id, 'user':user, 'user.id':user.id}
return Response(content)
Credits:
#davesque;
https://github.com/jazzband/djangorestframework-simplejwt/issues/140
Update.
The string access_token_str I write in the file is just an example. You should pass it as argument.
Here's how I've done it.
On Django, I followed the steps described on this page in order to add the user's name inside the JWT token : https://django-rest-framework-simplejwt.readthedocs.io/en/latest/customizing_token_claims.html
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
#classmethod
def get_token(cls, user):
token = super().get_token(user)
# Add name to token
token['name'] = user.get_full_name()
# You can add other information into the token here
return token
class MyTokenObtainPairView(TokenObtainPairView):
serializer_class = MyTokenObtainPairSerializer
Then, I updated my urls.py to use the custom view:
path('token/', MyTokenObtainPairView.as_view(), name='token_obtain_pair'),
Finally, in my Vue.js application, I installed the jwt-decode package and used it like this:
const token = localStorage.getItem('access_token');
const decoded = jwt_decode(token);
console.log(decoded)
// decoded.name contains the user's full name
Note that I store the access token in the local storage beforehand.

Django rest framework - exclude endpoint from authentication

I need to make view accessible without authentication, based on a variable passed in urls.py.
My idea is something like this:
urls.py
url(r'^oidc-api/', include('api.urls'), {'logged': True})
views.py
class ExampleViewSet(ModelViewSet):
if logged: # How can I get this variable, passed in urls.py?
permission_classes = () # This will exclude current view from authentication
queryset = Widget.objects.filter(visible=True)
serializer_class = ExampleSerializer
filter_backends = (DjangoFilterBackend,)
filter_fields = ('example_id',)
However, I can not access logged variable that is passed from urls.py.
John
You can't do that.
What you could do is, remove those permission classes from the views manually or set those to an empty set as,
class ExampleViewSet(ModelViewSet):
permission_classes = ()

Post from Angular 5 front-end to Django Rest Framework back-end

I am trying to create a post request from Angular 5 to DRF. The field is a form that a user submits. I created the serializer and i can post something from DRF interface
models.py
class UserForm(models.Model):
id_user_form = models.AutoField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, db_column='idUser', unique=False)
name = models.CharField(max_length=50)
type = models.CharField(max_length=25)
location = models.CharField(max_length=200)
serilizers.py
`class UserFormSerializer(serializers.ModelSerializer):
class Meta:
model = UserForm
fields = ('user', 'name', 'type', 'location')
def create(self, validated_data):
user_data = validated_data.pop('user')
user_form = UserForm.objects.create(user_id=user_data, **validated_data)
return user_form
views.py
class FormSubmit(generics.ListCreateAPIView):
queryset = UserForm.objects.all()
serializer_class = UserFormSerializer
When i try to post it via Angular I get this error:
Forbidden (CSRF token missing or incorrect.): /api/form/
Am I doing something wrong?
Had the exact same problem when I wanted to upload a profile picture to my Django REST backend.
You have 2 options, basically.
The first one is disabling the CSRF checks, which are by default enforced by DRF. Have a read here. You may do this for testing purposes.
The second option would be to pass the CSRF Token inside your request header. To do that, have a look at the $cookies API.
With that, you can get the token out of the cookie and paste it into your header like so: 'X-CSRFToken': your-csrf-token-here.
You can verify this by opening your dev tools inside the browser and navigating to your site's cookies. You should see one called csrftoken there.
I hope this sent you on the right track.

Django rest framework not indexing a custom CBV in Api Root

In Django-rest-framework I have a simple CBV
class LocationList(APIView):
"""
List all locations (id and name)
"""
def get(self, request, format=None):
# Make connection to SQL server db
dbargs = dict(
DRIVER='{FreeTDS}',
SERVER=django_settings.DB_HOST,
PORT=django_settings.DB_PORT,
DATABASE=django_settings.DB_NAME,
UID=django_settings.DB_USER,
PWD=django_settings.DB_PWD,
)
cnxn = pyodbc.connect(**dbargs)
# Query db
curs = cnxn.cursor()
select_locations_cmd = 'SELECT list_id, cast(list_name as text) FROM location_lists;'
curs = curs.execute(select_locations_cmd)
# Serialize
sdata = [dict(list_id=lid, list_name=lname) for lid, lname in curs.fetchall()]
# Close cnxn
cnxn.close()
return Response(sdata)
As you can see all it does is queries an external database, manually serializes the result and returns it in a django-rest-framework Response object.
In my urls.py I have
router = routers.DefaultRouter()
router.register(r'someothermodel', SomeOtherModelViewSet)
urlpatterns = [url(r'^', include(router.urls)),
url(r'^locationlists/$', LocationList.as_view(), name="weather-location-lists"),
]
This works OK, but what I'm concerned about is that when I visit the root API url, it only shows the endpoint for someothermodel, which was registered via the router and used a standard ViewSet. It doesn't list the locationlists endpoint at all. I can visit the /locationlists endpoint in the browser (or make a GET request otherwise to it without issue), but it's not indexed.
How can I index it at the root? So it appears alongside
Api Root
HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"someothertask": "http://127.0.0.1:8000/someothertask/",
}
The page you are referring to is powered by the router and in your case LocationList isn't registered via the router. As such it doesn't appear in the endpoint list.
As #Linovia points out, routers only deal with viewsets. With a few changes however this is simple to achieve:
# views.py
from rest_framework.viewsets import ViewSet
class LocationList(ViewSet):
def list(self, request, format=None): # get -> list
...
# urls.py
router = routers.DefaultRouter()
router.register(r'someothermodel', SomeOtherModelViewSet)
router.register(r'locationlists', LocationList, base_name='weather-location')
urlpatterns = [
url(r'^', include(router.urls)),
]
You should now see:
{
"someothertask": "http://127.0.0.1:8000/someothertask/",
"locationlists": "http://127.0.0.1:8000/locationlists/",
}
It is worth noting that the reverse name for your view has now changed from weather-location-lists to weather-location-list, but hopefully this is minor change to the places this may have been used.
Routers only work with ViewSet

Resources