django REST framework api without using models - django

I'm new to django rest framework. In my project i need to send api on a request, here i'm not using model data instead i want to send a dictionary as api response which is read from external database like mongodb. how to do this?
viewset code
class LivedataViewSet(viewsets.ModelViewSet):
queryset = LiveData.objects.all()
serializer_class = LiveDataSerializer
def get_queryset(self):
qs = super().get_queryset()
user_id = str(self.request.query_params.get('user'))
if user_id:
queryset = qs.filter(user=user_id)
return queryset
else:
return qs
and serializer code is
class LiveDataSerializer(serializers.ModelSerializer):
class Meta:
model = LiveData
fields = ('id', 'user', 'status')
this code works but it uses model here i need same function without model.

You need APIView instead of ModelViewSet, with that you can define your own endpoints, without tying it to a model.
Docs: https://www.django-rest-framework.org/api-guide/views/

Related

Django Rest Framework filtering against serializer method fields

In my serializers, I have added the custom field "step_type" that grabs a value from another model.
class AccountSerializer(serializers.ModelSerializer):
step_type= serializers.SerializerMethodField()
class Meta:
model = Account
fields = '__all__'
def get_step_type(self, obj):
step = Step.objects.get(step_name=obj.step_name)
return step.step_type
I want to use query parameters to filter my REST API
class AccountViewSet(viewsets.ModelViewSet):
def get_queryset(self):
queryset = Account.objects.all().order_by('-date')
query_step_type = self.request.query_params.get("type")
if query_step_type is not None:
queryset = queryset.filter(step_type=query_step_type)
return queryset
However, this won't work because step_type isn't part of the original model fields. How can I filter the queryset using the step type serializer method field?

Get external API information using serializer data before creating a new object in Django Rest Framework

I've added a Profile model linked to User model in Django REST Framework.
Before create a new object through a ModelViewSet I have to create a "customer" in an external API (Python requests) using profile information (not saved yet) and I want to save the returned ID in my Profile model.
Now my ModelViewSet looks like:
views.py
class ProfileViewSet(viewsets.ModelViewSet):
serializer_class = ProfileSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
return Profile.objects.filter(user=self.request.user)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
serializers.py
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
# fields = '__all__'
exclude = ('user',)
When and which is the best way to create the external customer and pass the ID to the serializer?
You can do it on perform_create:
def perform_create(self,serializer):
response = requests.post('your request url',data={*customer_save_data*})
if response.status_code == 200:
customer_id = response.json()['customer_id']
serializer.save(user=self.request.user,customer_id=customer_id)
else:
#you can raise exception here

How to covert two ListAPIViews that are different into a single ModelViewSet

I have a django project I am working on that needs a users and account model. I am using and integrating Django Rest Framework. I was initially using individual API generic views from DRF. I am thinking of converting the individual generic views into a view set. I was able to do it for the user model. I wan to convert the Account model views to a view set.
My issue is that I have two versions of the same ListAPIView for the profile model. The top View lists all of the accounts in the database and the second one list all the accounts for an idividual user based on the User__Username foreignkey in the Account model.
class UserViewSet(viewsets.ModelViewSet):
serializer_class = UserSerializer
queryset = User.objects.all()
lookup_field = 'username'
class AccountListView(ListAPIView):
queryset = Account.objects.all()
serializer_class = AccountSerializer
class AccountUserListView(ListAPIView):
queryset = Account.objects.all()
serializer_class = AccountSerializer
filter_backends = (filters.DjangoFilterBackend,)
filterset_fields = ('user', '_id', '_class')
def get_queryset(self):
return self.queryset.filter(user_username=self.kwargs.get('username'))
It says that I can specifically define the properties of a view within the viewset but I want to define two versions of the ListAPIView for the single model. Is there a way to double define the same view in a single viewset.
I basically want to define both of my Account ListAPIViews in the same viewset. How should i go about doing that if it is possible??
you can use the #action decorator to define.
class UserViewSet( mixins.ListModelMixin, viewsets.GenericViewSet):
"""
A simple ViewSet for listing or retrieving users.
"""
def list(self, request):
queryset = User.objects.all()
serializer = UserSerializer(queryset, many=True)
return Response(serializer.data)
#acttion(method="get", details=False, serializer_class=AccountSerializer)
def account_list(self, request, *args, **kwargs):
queryset = User.objects.all()
data = self.get_serializer_class().(instance=queryset, many=True).data
return response.Response(data)

Use serializer validated data in get_queryset()

I want to write custom get_queryset() method for serializer based on query params.
Here is my serializer:
class SearchRequestSerializer(serializers.Serializer):
name = serializers.CharField(max_length=255, required=False)
nickname = serializers.RegexField(
r'^(?!(.*?\_){2})(?!(.*?\.){2})[A-Za-z0-9\._]{3,24}$',
max_length=24,
min_length=3,
required=False,
)
modelA_id = serializers.CharField(max_length=11, min_length=11,
required=False)
def validate_modelA_id(self, value):
queryset = modelA.objects.filter(id=value)
if queryset.exists():
return queryset.first()
else:
raise serializers.ValidationError(_('Not found'))
If object of modelA exists - validation will return an instance. But I
don't want to perform the same query in get_queryset() in my if branch.
def get_queryset(self):
name = self.request.query_params.get('name', None)
nickname = self.request.query_params.get('nickname', None)
queryset = User.objects.filter(Q(name__contains=name)|Q(nickname__contains=nickname))
if 'modelA_id' in self.request.query_params:
# in this case will be annotated extra field to queryset
# extra field will be based on 'modelA' instance which should be returned by serializer
return queryset
I found only one solution - add the following line in my GET method:
self.serializer = self.get_serializer()
Than it will be possible to get validated values in my get_queryset() method. But PyCharm don't like this solution
I'm under strong impression that you are misusing the Serializer. After quick analysis of your issue i think you need DRF filtering
Serializers process request.data which under the hood is just Django request.POST and request.FILES yet in your get_queryset implementation you make lookups in request.query_params which in Django terms is request.GET. Check the DRF docs on this.
In order to achieve what you need with Serializers you would have to abuse the Views, Serializer Fields and Serializer itself. It is simply not what its supposed to do.
Additionaly I don't think that you need so much validation on search.
Customization and use of Django Filter should solve your problem.
class UserFilter(filters.FilterSet):
class Meta:
model = User
fields = ['name', 'nickname', 'modelA_id']
def filter_queryset(self, queryset):
name = self.form.cleaned_data.get('name', None)
nickname = self.form.cleaned_data.get('nickname', None)
queryset = queryset.filter(Q(name__contains=name)|Q(nickname__contains=nickname))
if 'modelA_id' in self.form.cleaned_data:
# in this case will be annotated extra field to queryset
# extra field will be based on 'modelA' instance which should be returned by serializer
return queryset
class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = (django_filters.rest_framework.DjangoFilterBackend,)
filterset_class = UserFilter
NOTE I did not test code above but it should show you how to tackle this problem.
How is about user = get_object_or_404(queryset, id=ModelA_id). It looks better as for me.
get_object_or_404 will catch an object you need, or will raise Not Found responce.

what is the control flow of django rest framework

I am developing an api for a webapp. I was initially using tastypie and switched to django-rest-framework (drf). Drf seems very easy to me. What I intend to do is to create nested user profile object. My models are as below
from django.db import models
from django.contrib.auth.models import User
class nestedmodel(models.Model):
info = models.CharField(null=True, blank=True, max_length=100)
class UserProfile(models.Model):
add_info = models.CharField(null=True, blank=True, max_length=100)
user = models.OneToOneField(User)
nst = models.ForeignKey(nestedmodel)
I have other models that have Foreignkey Relation. My Serializers are as below
from django.contrib.auth.models import User, Group
from rest_framework import serializers
from quickstart.models import UserProfile, nestedmodel
class NestedSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = nestedmodel
fields = ('info', )
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email', 'groups')
class GroupSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Group
fields = ('url', 'name')
class UserProfileSerializer(serializers.HyperlinkedModelSerializer):
user = UserSerializer()
nst = NestedSerializer()
class Meta:
model = UserProfile
user = UserSerializer(many=True)
nested = NestedSerializer(many=True)
fields = ('nst', 'user')
I can override methods like create(self, validated_data): without any issues. But What I want to know is to which method should the response returned by create() goes, or in other words Which method calls create(). In tastypie Resources.py is the file to override to implement custom methods. And Resources.py contains the order in which methods are being called. Which is the file in drf that serves the same purpose and illustrates the control flow like Resources.py in tastypie?.
So the flow goes something like:
Viewset's create() method which is implemented in CreateModelMixin
That creates serializer and validates it. Once valid, it uses viewset's perform_create()
That calls serializer's save() method
That then in turn calls serializer's either create() or update() depending if instance was passed to serializer (which it was not in step 1)
create() or update() then create/update instance which is then saved on serializer.instance
Viewset then returns response with data coming from serializer.data
serializer.data is actually a property on serializer which is responsible for serializing the instance to a dict
To serialize data, to_representation() is used.
Then response data (Python dict) is rendered to a output format via renderers which could be json, xml, etc
And Resources.py contains the order in which methods are being called. Which is the file in drf that serves the same purpose and illustrates the control flow like Resources.py in tastypie?.
Guess that would be a combination of files. Its probably better to think in terms of classes/concepts you are touching since in DRF you can inherit from multiple things for create your classes. So the thing which glues everything together are viewsets. Then there are various viewset mixins which actually glue the viewset to the serializer and different CRUD operations.
I figured out second part of question myself. get/create object can be done by using custom code in overriden def create(self, request, *args, **kwargs): in views.py. Code is as pasted below. Once Again, for clarity this is views.py not serializers.py. Also json with posted values can be accessed from request.DATA
class NestedViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows Nested objects to be viewed or edited.
"""
queryset = nestedmodel.objects.all()
serializer_class = NestedSerializer
def create(self, request, *args, **kwargs):
info = request.DATA['info']
user = User.objects.get(username=request.DATA['user']['username'])
profile = UserProfile.objects.get(user=user)
nst = nestedmodel.objects.create(info=info, user=user, profile=profile)
serialized_obj = serializers.serialize('json', [ nst, ])
json_serialized = json.loads(serialized_obj)
data = json.dumps(json_serialized[0])
return Response(data)
Thanks for the help #miki275 :)