DRF name undefined in custom serializer - django-rest-framework

When I try to hit my api/atoms/ endpoint in the browser, I am getting a name undefined error in the views.py file, but it has a base name in urls.
Note: this is a non-model serializer and a ViewSet.
error
...views.py", line 74, in list
instance = atom.values(), many=True)
NameError: name 'atoms' is not defined
views.py
class AtomViewSet(viewsets.ViewSet):
serializer_class = AtomSerializer
def list(self, request):
serializer = AtomSerializer(
instance = atoms.values(), many=True) #<-------------
return Response(serializer.data)
urls.py
# for viewsets in views.py
router = DefaultRouter()
router.register(r'snippets', views.SnippetViewSet)
router.register(r'atoms', views.AtomViewSet, base_name='atoms')
urlpatterns = [
path('', views.api_root),
path('', include(router.urls)),
]
serializer.py
class AtomSerializer(serializers.Serializer):
uid = UniqueIdProperty()
created_at = DateTimeProperty()
updated_at = DateTimeProperty()
charge = IntegerProperty()
mass = FloatProperty()
def create(self, validated_data):
return Atom(id=None, **validated_data)
def update(self, instance, validated_data):
for field, value in validated_data.items():
setattr(instance, field, value)
return instance

This is a basic python NameError exception raised when a local or global name is not found.
The variable atoms is not defined in the list() method or globally, that's why the python interpreter raised the exception.
In your code, you'd write atoms.values(), which forces me to think that you are dealing with a QuerySet, which might be an Atom model.
class AtomViewSet(viewsets.ViewSet):
serializer_class = AtomSerializer
def list(self, request):
serializer = AtomSerializer(instance=Atom.objects.all(), many=True)
return Response(serializer.data)
Note: this is a non-model serializer and a ViewSet.
You are doing create and update operations in your AtomSerializer class, and those are directly connected to the model. I don't see any particular reason that pulls you back from using a ModelSerializer here. Apart from that, you are using the routers, which become a good choice when you deal with the CRUD operations, hence I strongly suggest you use the combination of ModelViewset and ModelSerializer in your code.

In your views.py you did not define atom, you need to define it first before using it or else you will get that error.
class AtomViewSet(viewsets.ViewSet):
serializer_class = AtomSerializer
def list(self, request):
# You need to define the atom first before passing it to your AtomSerializer
atoms = [] # or atom = Atom.objects.all()
serializer = AtomSerializer(
data=atoms,
many=True
)
return Response(serializer.data)

Related

Data not getting into the variable. how to pass id in destroy method

how to pass id in a destroy method in modelviewset
model
class Matchscore(TimeStampedModel):
gameevent = models.ForeignKey(GameEvent, null=True, related_name='game_event',on_delete=models.DO_NOTHING)
match_round = models.IntegerField(null=True,blank=True)
team_a = models.ForeignKey(Team,null=True,related_name='team_one',on_delete=models.DO_NOTHING)
team_a_score = models.PositiveIntegerField(null=True,blank=True)
team_b = models.ForeignKey(Team,null=True,related_name='team_two',on_delete=models.DO_NOTHING)
team_b_score = models.PositiveIntegerField(null=True,blank=True)
team_won = models.ForeignKey(Team,null=True,related_name='team', on_delete=models.DO_NOTHING)
modelviewset
class MatchscoreViewSet(viewsets.ModelViewSet):
authentication_classes = (CsrfExemptSessionAuthentication, JWTAuthentication)
queryset = Matchscore.objects.all()
serializer_class = MatchScoreSerializer
permission_classes = (IsAuthenticated,)
def destroy(self,request, *args, **kwargs):
print("delete")
match = Matchscore.objects.filter().order_by('match_round').first()
print(match)
if match :
match.delete()
response=({"result":"successfully removed"})
else:
response=({"result":"can't delete this round"})
return Response(response)
how can i pass the id in destroy function in modelviewset, in this format of code i didnt get the data .only when i can satisfy the condition while getting the data in the variable.
If you are using routers for this viewset, then you have pk arg in your URL by default.
You can probably find it in kwargs.
beside that, implemented destroy method in ModelViewSet can handle all of these for you

Using django-filter overriding list method in drf

I'm using django-filter to filter my viewsets in drf.
When I have a ModelViewset, works fine like example bellow:
class MyExampleViewSet(viewsets.ModelViewSet):
queryset = myqueryset
model = ModelExample
filter_backends = (DjangoFilterBackend, OrderingFilter,)
filterset_fields = {
"field_example": ["exact", "icontains"],
"another_field_example": ["exact", "range"],
}
serializer_class = MyExampleViewSet
My problem is when I override the list method using a ViewSet, like this:
class MyExampleViewSet(viewsets.ViewSet):
def list(self, request, queryset=queryset, *args, **kwargs):
return something
In this case my filters does not working. Is there a way of using django-filter in this case (overriding list)?
I know what I can do with query_params, but I would like to use django-filter.
First, you should take a look at how the list method is implemented:
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
Django-filter is applied at this point queryset = self.filter_queryset(self.get_queryset()).
So if you want to override the list method but keep the the filtering feature, then make sure to call self.filter_queryset() with the queryset.

AttributeError: 'collections.OrderedDict' object has no attribute 'model_id' and 'model_id' is missing from visible fields

Something strange happened: I was defining an endpoint and initially two fields were visible in the API form: model_id and payload, as given in the model definition:
### models.py:
class CarModel(models.Model):
model_id = models.CharField(max_length=10, primary_key=True)
name = models.CharField(max_length=40)
active = models.BooleanField(default=True)
def __str__(self):
return self.model_id
class Calculator(models.Model):
model = models.ForeignKey(CarModel, on_delete=models.CASCADE)
payload = models.TextField()
def model_id(self):
return self.model.model_id
def __str__(self):
return f"Calculations for {self.model.name}"
### serializers.py:
class CalculatorSerializer(serializers.ModelSerializer):
model_id = serializers.SerializerMethodField()
class Meta:
model = Calculator
fields = ['model_id', 'payload']
def get_model_id(self, obj):
return obj.model_id()
### views.py:
class CalculatorViewSet(viewsets.ModelViewSet):
serializer_class = CalculatorSerializer
queryset = Calculator.objects.all()
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
return Response(f"{serializer.data.upper()}", status=status.HTTP_200_OK)
So, both fields were visible, but POST requests ended in the AttributeError: 'collections.OrderedDict' object has no attribute 'model_id'. Trying to fix that, I eventually and accidentally removed model_id from view - it doesn't display in DRF's forms. And the AttributeError still persists.
What is wrong with this piece of code?
OK, it turns out that defining fields in this manner:
fields = '__all__'
makes also the model_id visible. Still, no idea why explicit insert doesn't work.
In case of the other issue, the AttributeError, I had to pull the value out of an OrderedDict. Modified method looks like this:
def get_model_id(self, obj):
return obj["model"].model_id
Beside that, I found one more error inside views.py's create method: serializer.data won't implement upper() method; some key, in my case serializer.data['payload'], has to be referenced, so for example:
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
res = {
"payload": f"{serializer.data['payload'].upper()}"
}
return Response(res, status=status.HTTP_200_OK)

how to update serializer.validated_data in DRF

good day
me need update validated_data dict
example
class ProductsApiView(APIView):
permission_classes = [AllowAny]
def post(self, request):
serializer = ProductSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
product = serializer.save()
print(serializer.validated_data)
return Response({'message': 'created'}, status=status.HTTP_201_CREATED)
class ProductSerializer(serializers.ModelSerializer):
def create(self, validated_data):
validated_data['my_data'] = 'my_new_data'
return super(ProductSerializer, self).create(validated_data)
class Meta:
model = Product
fields = ['id', 'title']
when i make print(serializer.validated_data)
OrderedDict([('title', 'testttt')])
why in dict no my data "my_data" ?
You can provide arbitrary additional context by passing a context argument when instantiating the serializer. For example:
serializer = AccountSerializer(account, context={'request': request})
serializer.data
The context dictionary can be used within any serializer field logic, such as a custom .to_representation() method, by accessing the self.context attribute.
Reference

DRF: Pagination without queryset

I am trying to make use of Django Rest Framework's pagination mechanisms in my case without success.
class TransactionView(viewsets.ViewSet):
serializer_class = TransactionSerializer
def list(self, request):
# fetching data from external API...
serializer = self.serializer_class(data=list_of_json, many=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors)
class TransactionSerializer(serializers.Serializer):
# Serializer (transaction's) fields ...
def create(self, validated_data):
return APITransaction(**validated_data)
class APITransaction(object):
def __init__(self, arg1, arg2, ...):
self.arg1 = arg1
...
The problem is that registering the pagination_class (like I have done for the rest of my resources which are represented by Models), doesn't work since the data are created/fetched on the fly, thus I don't have a Model/queryset.
Any ideas on how I could use DRF's pagination mechanism?
Here's the class I wound up creating and using locally for this sort of thing. (Thanks to stelios above for the initial clues.) Of course the contents of "data" must be JSONable.
from typing import List, Any
from collections import OrderedDict
from django.core.paginator import Paginator
from django.http.response import JsonResponse
from rest_framework.request import Request
class ListPaginator:
def __init__(self, request: Request):
# Hidden HtmlRequest properties/methods found in Request.
self._url_scheme = request.scheme
self._host = request.get_host()
self._path_info = request.path_info
def paginate_list(self, data: List[Any], page_size: int, page_number: int) -> JsonResponse:
paginator = Paginator(data, page_size)
page = paginator.page(page_number)
previous_url = None
next_url = None
if self._host and self._path_info:
if page.has_previous():
previous_url = '{}://{}{}?limit={}&page={}'.format(self._url_scheme, self._host, self._path_info, page_size, page.previous_page_number())
if page.has_next():
next_url = '{}://{}{}?limit={}&page={}'.format(self._url_scheme, self._host, self._path_info, page_size, page.next_page_number())
response_dict = OrderedDict([
('count', len(data)),
('next', next_url),
('previous', previous_url),
('results', page.object_list)
])
return JsonResponse(response_dict, status=200, safe=False)
You can't reuse existing DRF's pagination because they are supposed to work with queryset.
However, you may roll your own class by inheriting BasePagination though I haven't done myself.

Resources