passing request variables in django/tastypie resources - python

I need to execute filter query inside tastypie resources. the input should be the headers of url for example
new Ext.data.Store({
proxy: {
url :'api/users/'
type: "ajax",
headers: {
"Authorization": "1"
}
}
})
I tried below
from tastypie.authorization import Authorization
from django.contrib.auth.models import User
from tastypie.authentication import BasicAuthentication
from tastypie import fields
from tastypie.resources import ModelResource, ALL, ALL_WITH_RELATIONS
from tastypie.validation import Validation
from userInfo.models import ExProfile
class UserResource(ModelResource,request):
class Meta:
queryset = User.objects.filter(id=request.META.get('HTTP_AUTHORIZATION'))
resource_name = 'user'
excludes = ['email', 'password', 'is_active', 'is_staff', 'is_superuser']
authorization = Authorization()
authentication=MyAuthentication()
it is saying name 'request' is not defined. How to pass filters on ORM them?

Not sure why are you inheriting request in UserResource.
I needed to do something like this and the best solution I could come up was to overwrite the dispatch method. Like this
class UserResource(ModelResource):
def dispatch(self, request_type, request, **kwargs):
self._meta.queryset.filter(id=request.META.get('HTTP_AUTHORIZATION'))
return super(UserResource, self).dispatch(request_type, request, **kwargs)

Well , i found apply_filter very useful.
We can pass link like
http://localhost:8000/api/ca/entry/?format=json&userid=a7fc027eaf6d79498c44e1fabc39c0245d7d44fdbbcc9695fd3c4509a3c67009
Code
class ProfileResource(ModelResource):
class Meta:
queryset =ExProfile.objects.select_related()
resource_name = 'entry'
#authorization = Authorization()
#authentication = MyAuthentication()
filtering = {
'userid': ALL,
'homeAddress': ALL,
'email': ALL,
'query': ['icontains',],
}
def apply_filters(self, request, applicable_filters):
base_object_list = super(ProfileResource, self).apply_filters(request, applicable_filters)
query = request.META.get('HTTP_AUTHORIZATION')
if query:
qset = (
Q(api_key=query)
)
base_object_list = base_object_list.filter(qset).distinct()
return base_object_list

Related

Django Rest Frame Work: passing User in djago rest frame work

I have a Django project as following code in model
class Report(models.Model):
created_by_user=models.ForeignKey(User,on_delete=models.CASCADE)
following code in serializer
class ReportSerializer(serializers.ModelSerializer):
class Meta:
model=Report
fields='__all__'
and following code in view
class ReportCreateView(APIView):
def post(self,request, *args, **kwargs):
received_data=ReportSerializer(data=request.data)
if received_data.is_valid():
received_data.save()
return Response(received_data.data, status=status.HTTP_201_CREATED)
return Response(received_data.errors,status.HTTP_400_BAD_REQUEST)
when I send a post request by postman and send username and password in Authorization tab
it error:
{
"created_by_user": [
"This field is required."
]
}
but if I type username or password incorrect it will be
{
"detail": "Invalid username/password."
}
can everybody help me?
Your serializer has no idea about the currently logged-in user.You to pass it as context from request. user or request.
I personally prefer CurrentUserDefault to be used in the serializer. To make it work we need to pass the request as context because CurrentUserDefault picks user from context request. We need to update our views and serializer code as follows
Views file: Add request as context context
class ReportCreateView(APIView):
def post(self,request, *args, **kwargs):
received_data=ReportSerializer(data=request.data, context = {"request": request})
if received_data.is_valid():
received_data.save()
return Response(received_data.data, status=status.HTTP_201_CREATED)
return Response(received_data.errors,status.HTTP_400_BAD_REQUEST)
serializer.py: Update your serializer to auto-populate created_by_user
class ReportSerializer(serializers.ModelSerializer):
created_by_user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model=Report
fields='__all__'
It will solve your user field required issue.
"created_by_user": ["This field is required."]
Now coming to your next part of the issue that is related to incorrect passwords.
By default, APIView picks the default authentication class from settings. Inside projects settings.py, we mostly write these lines while using DRF and they serve as default authentication for APIView:
From settings.py
REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
# Authentication settings
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.SessionAuthentication",
],
...
}
Inside APIView you can have a look at default permission_classes, and authentication_classes
From inside APIView:
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
that is when you type an invalid password:
"detail": "Invalid username/password."
Provide the correct username and password to your APIView from postman so that it gets the requested logged-in user for auto-populates at DB level.
you don't perform any process on your user data and just need to save the request user, due to that I think you don't need a serializer field for it and it's better to get your current user in view. also if you need more fields to serialize, you can make created_by_user read_only true and set its value on your view.
for example, if you have report name and report desc field in your model:
class Report(models.Model):
created_by_user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=256)
desc = models.TextField()
perform your serializer like this :
class ReportSerializer(serializers.ModelSerializer):
class Meta:
model = Report
fields = '__all__'
extra_kwargs = {
'created_by_user': {'read_only': True},
}
then set created_by_user value in your view:
class ReportCreateView(APIView):
def post(self, request, *args, **kwargs):
request.data['created_by_user'] = request.user # just need add this line
received_data = ReportSerializer(data=request.data)
if received_data.is_valid():
received_data.save()
return Response(received_data.data, status=status.HTTP_201_CREATED)
return Response(received_data.errors, status.HTTP_400_BAD_REQUEST)

Django TypeError Field 'id' expected a number but got <class 'account.models.User'>

Error: TypeError: Field 'id' expected a number but got <class 'account.models.User'>.
I have been reading the authentication documentation and I keep getting that error. I have tried a few different permutations of this but I'm really not sure what is going wrong other than its obviously a type error.
The end goal is to just return the 'id' and 'email' of the newly created user, along with the token, I haven't been able to test the token returning yet, because of this error, but I imagine it should be good.
What is working though is it is successfully posting a new user to the database with a token in another table with a matching key and now I have about 50 users from just testing and messing around.
Side note I have overridden the default User Django sets you up with, but I followed the documentation so that code should be solid, but if need I can obviously post it
Views.py
from rest_framework import status
from .models import User
from .serializers import UserSerializer, RegisterSerializer
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework.authtoken.models import Token
#api_view(['POST',])
def RegisterAPI(request):
if request.method == 'POST':
serializer = RegisterSerializer(data = request.data)
if serializer.is_valid(raise_exception=True):
user = serializer.save()
content = {
'user': {'id': user.pk, 'email': user.email},
'auth': Token.objects.get(user = User).key
}
# Return the reponse
return Response(content)`
Serializers.py
from rest_framework import serializers
from django.contrib.auth import get_user_model
from django.contrib.auth import authenticate
from .models import User
# Register Serializer
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'email', 'password')
extra_kwargs = {'password':{'write_only': True}}
def save(self):
user = User(
email = self.validated_data['email'],
)
password = self.validated_data['password']
user.set_password(password)
user.save()
return user
# User Serializer
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('email')
Documentation
Similar article that didn't help me
You are passing the User model class to the .get(...) method, but it should be the User model instance which is just got from the serializer.
#api_view(['POST', ])
def register_api(request):
if request.method == 'POST':
serializer = RegisterSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
content = {
'user': {'id': user.pk, 'email': user.email},
'auth': Token.objects.get(user=user).key
}
# Return the reponse
return Response(content)

Django Rest Framework and Auth: How to override UserDetailsView

I'm using django-rest-auth for API endpoints for my custom user model. Getting user details, I send a GET request to /rest-auth/user/. This works fine with an authenticated user, but is forbidden for an unauthenticated user marked by a 403 Forbidden error.
However, I want other users to be able to view each other's details. How can I change this?
This test is the one which demonstrates this error:
def test_get_user(self):
# /rest-auth/user/ (GET)
# params: username, first_name, last_name
# returns: pk, username, email, first_name, last_name
client = APIClient()
client.login(username=self.user2.username,
password=self.user2.password)
path = "/rest-auth/user/"
user_data = {
"username": self.username,
}
expected_response = {
"pk": self.id,
"username": self.username,
"email": self.email,
"first_name": '',
"last_name": '',
}
response = client.get(path, user_data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, expected_response)
EDIT:
I tried to override the permissions of UserDetailsView, yet I failed to do so properly. How do I do this correctly?
from rest_auth import views
from rest_framework.permissions import IsAuthenticatedOrReadOnly
class CustomUserDetailsView(views.UserDetailsView):
permission_classes = (IsAuthenticatedOrReadOnly, )
django-rest-auth /rest-auth/user/ only allow you to get details of the authenticated user.
class UserDetailsView(RetrieveUpdateAPIView):
"""
Reads and updates UserModel fields
Accepts GET, PUT, PATCH methods.
Default accepted fields: username, first_name, last_name
Default display fields: pk, username, email, first_name, last_name
Read-only fields: pk, email
Returns UserModel fields.
"""
serializer_class = UserDetailsSerializer
permission_classes = (IsAuthenticated,)
def get_object(self):
return self.request.user
def get_queryset(self):
"""
Adding this method since it is sometimes called when using
django-rest-swagger
https://github.com/Tivix/django-rest-auth/issues/275
"""
return get_user_model().objects.all()
if you want unauthenticated users to read all user objects you have write your own view.
serializers.py
User = get_user_model()
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"
views.py
class UserDetailApiView(generics.RetrieveAPIView):
permission_classes = (IsAuthenticatedOrReadOnly,)
queryset = get_user_model().objects.all()
serializer_class = UserSerializer
urls.py
path('api/users/<int:pk>',views.UserDetailApiView.as_view(),name='user')

Django Rest Framework unable to parse multipart/form data

As stated in DRF documentation http://www.django-rest-framework.org/api-guide/parsers/#multipartparser, in order to parse multipart/form-data, the MultiPart and form parser must be used. I have a supiscion this is a problem in the Django Rest Framework because I saw a solution on their github issue saying it work using APIView.
from django.contrib.auth.models import User
from rest_framework import viewsets
from api.serializers import UserSerializer,
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.response import Response
from rest_framework import status
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = User.objects.all()
parser_classes = (MultiPartParser, FormParser)
serializer_class = UserSerializer
Picture of me sending request to Postman with result:
Edit: Adding UserSerializer class
class UserSerializer(serializers.HyperlinkedModelSerializer):
snapcapsules = SnapCapsuleSerializer(
many=True,
read_only=True,
allow_null=True,
)
class Meta:
model = User
fields = ('snapcapsules', 'url', 'username', 'email', 'password', )
write_only_fields = ('password',)
def create(self, validated_data):
user = User.objects.create(
username=validated_data['username'],
email=validated_data['email'],
)
user.set_password(validated_data['password'])
user.save()
return user
def update(self, instance, validated_data):
capsules_data = validated_data.pop('snapcapsules')
for capsule in capsules_data:
SnapCapsule.objects.create(user=instance, **capsule)
return instance
This is might not the issue with the ContentType. The error response says that,the UserSerializer excpect a payloadordata which include username and password field
So, Please try to add those fields to the request body and try again
UPDATE
The problem with the Orginal Post was, he added extra headers in POSTMAN (see the pic below) .
Postman will add appropriate Headers aromatically for you. So, you don't have to explicitly mention it
I removed one line and then it worked!
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = User.objects.all()
serializer_class = UserSerializer
Also, the serializer can be rewrite as follows:
class UserSerializer(serializers.HyperlinkedModelSerializer):
...
def create(self, validated_data):
return User.objects.create_user(
username=validated_data['username'],
email=validated_data['email'],
password=validated_data['password']
)
...

Django Tastypie - Resource with object details only

In Django with Tastypie, is there a way to configure a resource such that it only shows object details?
I want to have a url /user which returns the details of the authenticated user, as opposed to a list containing a single user object. I don't want to have to use /users/<id> to get the details of a user.
Here's the relevant portion of my code:
from django.contrib.auth.models import User
from tastypie.resources import ModelResource
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
resource_name = 'user'
allowed_methods = ['get', 'put']
serializer = SERIALIZER # Assume those are defined...
authentication = AUTHENTICATION # "
authorization = AUTHORIZATION # "
def apply_authorization_limits(self, request, object_list):
return object_list.filter(pk=request.user.pk)
I was able to do this by using a combination of the following resource methods
override_urls
apply_authorization_limits
Example user resource
#Django
from django.contrib.auth.models import User
from django.conf.urls import url
#Tasty
from tastypie.resources import ModelResource
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
resource_name = 'users'
#Disallow list operations
list_allowed_methods = []
detail_allowed_methods = ['get', 'put', 'patch']
#Exclude some fields
excludes = ('first_name', 'is_active', 'is_staff', 'is_superuser', 'last_name', 'password',)
#Apply filter for the requesting user
def apply_authorization_limits(self, request, object_list):
return object_list.filter(pk=request.user.pk)
#Override urls such that GET:users/ is actually the user detail endpoint
def override_urls(self):
return [
url(r"^(?P<resource_name>%s)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
]
Using something other than the primary key for getting the details of a resource is explained in more detail in the Tastypie Cookbook

Categories

Resources