Django HyperlinkedRelatedField read_only False gives queryset Error - django

Dog model has a field "cat'
class Dog(models.Model):
...
cat = models.ForeignKey(Cat)
...
class CatSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Cat
# Replace ID with SLUG in urls
lookup_field = 'slug'
fields = ('url', 'slug')
extra_kwargs = {
'url': {'lookup_field': 'slug'}
}
class DogSerializer(serializers.HyperlinkedModelSerializer):
cat= serializers.HyperlinkedRelatedField(
view_name='cat-detail',
lookup_field='slug',
many=False,
read_only=True
)
class Meta:
model = Dog
fields = ('url', 'slug', 'cat')
lookup_field = 'slug'
extra_kwargs = {
'url': {'lookup_field': 'slug'}
}
class CatViewSet(viewsets.ModelViewSet):
def get_serializer_context(self):
context = super().get_serializer_context()
context['slug'] = self.kwargs.get('slug')
return context
queryset = Cat.objects.all()
serializer_class = CatSerializer
lookup_field = 'slug'
class DogViewSet(viewsets.ModelViewSet):
queryset = Dog.objects.all()
lookup_field = 'slug'
serializer_class = DogSerializer
router = routers.DefaultRouter()
router.register(r'cats', rest_views.CatViewSet)
router.register(r'dogs', rest_views.DogViewSet)
How can I set:
read_only=False
The error I get when I set it to False is:
'Relational field must provide a queryset argument, '
AssertionError: Relational field must provide a queryset argument, override get_queryset, or set read_only=True.

cat= serializers.HyperlinkedRelatedField(
view_name='cat-detail',
lookup_field='slug',
many=False,
read_only=False,
queryset=Cat.objects.all()
)
This worked just fine.

Related

How to pass a query parameter to SerializerMethodField in django rest framework?

I want to filter out some instance based on the query parameter I get in the GET call.
class RevisionSerializer(serializers.ModelSerializer):
slot_info = serializers.SerializerMethodField(required=False, read_only=True)
batch_config = serializers.SerializerMethodField(required=False, read_only=True)
class Meta:
model = Revision
fields = ['id', 'status', 'revision_id', 'instructor', 'number_of_classes', 'start_date',
'slot', 'slot_info', 'tinyurl', 'zoom_link', 'batch_config']
read_only_fields = ['revision_id']
def get_batch_config(self, obj):
# filter this on the incoming data from the GET call
related_batches = CompensationClass.objects.select_related('style', 'instructor').filter(
compensation_class_id=obj.revision_id)
batch_config_values = related_batches.values('batch_id', 'age_group', 'level', 'course_version', 'style__name',
'class_number')
return batch_config_values
This is my serializer and I will be passing one date and based on that date I want to filter my serializermethodfield. How can I achieve this?
You can pass parameters to a Serializer using its context:
# views.py
class RevisionView(GenericAPIView):
serializer_class = RevisionSerializer
def get_serializer_context(self):
return {'revision_id': self.request.GET.get('revision_id')}
# serializers.py
class RevisionSerializer(serializers.ModelSerializer):
def get_batch_config(self, obj):
related_batches = CompensationClass.objects.select_related('style', 'instructor').filter(
compensation_class_id=self.context.get('revision_id'))
Thanks to #ThomasGth. I did it like this.
SERIALIZER
class RevisionSerializer(serializers.ModelSerializer):
slot_info = serializers.SerializerMethodField(required=False, read_only=True)
batch_config = serializers.SerializerMethodField(required=False, read_only=True,
)
class Meta:
model = Revision
# fields = '__all__'
fields = ['id', 'status', 'revision_id', 'instructor', 'number_of_classes', 'start_date',
'slot', 'slot_info', 'tinyurl', 'zoom_link', 'batch_config']
read_only_fields = ['revision_id']
def get_batch_config(self, obj):
calendar_date = self.context.get('calendar_date')
related_batches = CompensationClass.objects.select_related('style', 'instructor').filter(
compensation_class_id=obj.revision_id)
if calendar_date:
related_batches = related_batches.filter(calendar_date)
batch_config_values = related_batches.values('batch_id', 'age_group', 'level', 'course_version', 'style__name',
'class_number')
return batch_config_values
VIEWSET
class RevisionViewset(viewsets.ModelViewSet):
queryset = Revision.objects.all().order_by('-modified_at').select_related('instructor', 'slot')
serializer_class = RevisionSerializer
pagination_class = LimitOffsetPagination
filter_backends = [DjangoFilterBackend, filters.SearchFilter]
filterset_class = RevisionFilter
def get_serializer_context(self):
context = {'request': self.request}
calendar_date = self.request.GET.get('calendar_date')
if calendar_date:
context['calendar_date'] = calendar_date
return context

Show only two model properties in the item list with Django REST Framework

I want to show only two properties of the model in the list of items, but then in a specific item show all the properties
/api/character <-- show a list with name and id properties
/api/character/30 <-- show all properties of the item with id 30
Code i have in serializer.py:
class CharacterSerializer(serializers.ModelSerializer):
language = LanguageSerializer(read_only=True)
region = RegionSerializer(read_only=True)
realm = RealmSerializer(read_only=True)
faction = FactionSerializer(read_only=True)
race = RaceSerializer(read_only=True)
wow_class = ClassSerializer(read_only=True)
spec = SpecSerializer(read_only=True)
talents = TalentSerializer(many=True, read_only=True)
pvp_talents = PvpTalentSerializer(many=True, read_only=True)
covenant = CovenantSerializer(read_only=True)
soulbind = SoulbindSerializer(read_only=True)
conduits = ConduitSerializer(many=True, read_only=True)
class Meta:
model = Character
fields = ['id', 'name', 'language', 'region', 'realm', 'faction', 'race', 'wow_class', 'spec', 'talents', 'pvp_talents',
'covenant', 'covenant_rank', 'soulbind', 'soulbind_abilities', 'conduits', 'item_level', 'media', 'avatar', 'rating_2v2',
'rating_3v3', 'rating_rbg', 'max_2v2', 'max_3v3', 'max_rbg', 'achievement_points', 'account_achievements', 'seasons',
'alters', 'date_added', 'last_search', 'checked']
code in views.py
class CharacterViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = CharacterSerializer
permission_classes = []
queryset = Character.objects.all()
filter_backends = [filters.SearchFilter, DjangoFilterBackend]
search_fields = ['name']
filterset_fields = ['language', 'region', 'realm', 'faction', 'race', 'wow_class', 'spec', 'covenant', 'soulbind']
Thx a lot!
Solved.
I have created two serializer for the model one for list and another for detail
class TalentSerializer(serializers.ModelSerializer):
class Meta:
model = Talent
fields = ['id', 'name']
class TalentDetailSerializer(serializers.ModelSerializer):
language = LanguageSerializer(read_only=True)
wow_class = ClassSerializer(read_only=True)
spec = SpecSerializer(many=True, read_only=True)
class Meta:
model = Talent
fields = ['id', 'name', 'language', 'description', 'spell_id', 'talent_id', 'wow_class', 'spec', 'tier_index', 'column_index', 'level', 'icon']
in the viewset I have redefined the get of the class:
class TalentViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = TalentSerializer
permission_classes = []
queryset = Talent.objects.all()
filter_backends = [filters.SearchFilter, DjangoFilterBackend]
search_fields = ['name']
filterset_fields = ['language', 'spell_id', 'talent_id', 'wow_class', 'spec', 'tier_index', 'column_index', 'level']
def get_serializer_class(self):
if self.action == 'retrieve':
return TalentDetailSerializer
return TalentSerializer
Now works well /api/talent return a list with id and name of items and /api/talent/id return the item with all props of model :)

FieldError at / Related Field got invalid lookup: is_null

I'm creating a comment api but when i run the server give this error:
FieldError at /
Related Field got invalid lookup: is_null
i don't know how to fix it. i'm creating a nested comment api. this is my code:
#serializer
class CommentSerializer(serializers.ModelSerializer):
loadParent = serializers.SerializerMethodField("loadPrentData")
def loadPrentData(self, comment):
comments = Comment.objects.filter(parent=comment)
comments_ser = CommentSerializer(comments, many=True).data
return comments_ser
class Meta:
model = Comment
fields = ['id', 'user', 'product', 'parent', 'body', 'created', 'loadParent']
class ProductSerializer(serializers.ModelSerializer):
comments = serializers.SerializerMethodField("loadProductComments")
def loadProductComments(self, _product):
_comments = Comment.objects.filter(product=_product, parent__is_null=True)
_comments_ser = CommentSerializer(_comments, many=True, read_only=True).data
return _comments_ser
class Meta:
model = Product
fields = ['id', 'category', 'name', 'slug', 'image_1',
'image_2', 'image_3', 'image_4', 'image_5',
'description', 'price', 'available', 'created', 'updated', 'comments']
lookup_field = 'slug'
extra_kwargs = {
'url': {'lookup_field': 'slug'}
}
#views:
#api_view()
def AddComent(request, parent_id=None):
parent = request.data.get("parent_id")
serializer = CommentSerializer(data=request.data)
if serializer.is_valid():
if parent is not None:
comment = Comment.objects.create(user=request.user, product=serializer.validated_data['product'],
parent_id=serializer.validated_data['parent'],
body=serializer.validated_data['body'])
else:
comment = Comment.objects.create(user=request.user, product=serializer.validated_data['product'],
body=serializer.validated_data['body'])
comments_ser = CommentSerializer(comment,many=False, read_only=True).data
return Response(comments_ser, status=status.HTTP_200_OK)
return Response(status=status.HTTP_400_BAD_REQUEST)
Your line
_comments = Comment.objects.filter(product=_product, parent__is_null=True)
should be
_comments = Comment.objects.filter(product=_product, parent__isnull=True)

TypeError: Object of type '_Serializer' is not JSON serializable

I am newer in Django rest api. My code is bellow:
class PatientViewSet(viewsets.ModelViewSet):
queryset = Patient.objects.all()
serializer_class = PatientSerializer
filter_backends = (DjangoFilterBackend, filters.OrderingFilter)
filterset_fields = ['id', 'email', 'mobile', 'status', 'type', 'gender']
ordering_fields = ['id', 'name']
def get_queryset(self):
queryset = Patient.objects.all()
status = self.request.query_params.get('status')
name = self.request.query_params.get('name')
if not status:
queryset = queryset.exclude(status="DELETE")
if name:
queryset = queryset.filter(name__icontains=name)
return queryset
def retrieve(self, request, pk=None):
queryset = Patient.objects.all()
patient = get_object_or_404(queryset, pk=pk)
serializer = PatientSerializer(patient)
summary = dict()
summary['payment'] = list(PatientPayment.objects.filter(patient_id=pk).aggregate(Sum('amount')).values())[0]
summary['appointment'] = DoctorAppointment.objects.filter(patient_id=pk).count()
d_appoint = DoctorAppointment.objects.filter(patient__id=pk).last()
appoint_data = DoctorAppointmentSerializer(d_appoint)
summary['last_appointment'] = appoint_data
content = {"code": 20000, "data": serializer.data, "summary": summary}
return Response(content)
Here url is:
http://127.0.0.1:8000/api/patients/2/
When I run in postman it getting the error bellow:
TypeError at /api/patients/2/
Object of type 'DoctorAppointmentSerializer' is not JSON serializable
Here problem with the code snippet:
d_appoint = DoctorAppointment.objects.filter(patient__id=pk).last()
appoint_data = DoctorAppointmentSerializer(d_appoint)
My question is how can I getting my result?
DoctorAppointmentSerializer Class:
class DoctorAppointmentSerializer(serializers.HyperlinkedModelSerializer):
patient = PatientSerializer(read_only=True)
patient_id = serializers.IntegerField()
doctor = DoctorSerializer(read_only=True)
doctor_id = serializers.IntegerField()
doc_image = Base64ImageField(
allow_null=True, max_length=None, use_url=True, required=False
)
doc_file = Base64ImageField(
allow_null=True, max_length=None, use_url=True, required=False
)
class Meta:
model = DoctorAppointment
fields = ['id', 'name', 'mobile', 'problem', 'age', 'gender', 'description', 'doctor', 'doctor_id', 'patient',
'patient_id', 'advice', 'doc_image', 'doc_file', 'created_at']
You have to call the .data property of DoctorAppointmentSerializer class
appoint_data = DoctorAppointmentSerializer(d_appoint).data
^^^^^^

Django Rest Framework serilize relations

How to serialize a fields in related models.
I got a models:
class Order(models.Model):
order_id = models.BigIntegerField(verbose_name='Order ID', unique=True)
order_name = models.CharField(verbose_name='Order name', max_length=255)
order_type = models.IntegerField(verbose_name='Campaign type')
class Types(models.Model):
delimiter = models.CharField(verbose_name='Delimiter', max_length=255)
status = models.BooleanField(verbose_name='Status', default=True)
title = models.CharField(verbose_name='Title', max_length=255)
class User(models.Model):
name = models.CharField(verbose_name='User name', max_length=200, unique=True)
class Report(models.Model):
order = models.ForeignKey(Order, to_field='order_id', verbose_name='Order ID')
user = models.ForeignKey(User, verbose_name='User ID')
ad_type = models.ForeignKey(Types, verbose_name='Type')
imp = models.IntegerField(verbose_name='Total imp')
month = models.DateField(verbose_name='Month', default=datetime.datetime.today)
View:
class ReportLisAPIView(ListAPIView):
serializer_class = ReportSerializer
def get_queryset(self):
month = parse_date(self.kwargs['month']) - relativedelta(day=1)
queryset = (
Report.objects.filter(month=month)
.values_list(
'user', 'user__name', 'order__order_id',
'order__order_name', 'order__order_type'
).all().annotate(Sum('imp'))
)
return queryset
Serializer:
class ReportSerializer(ModelSerializer):
class Meta:
model = Report
depth = 1
I need to get all field like in 'queryset' in get_queryset()
but I got an error:
Got AttributeError when attempting to get a value for field imp on
serializer ReportSerializer. The serializer field might be named
incorrectly and not match any attribute or key on the tuple
instance. Original exception text was: 'tuple' object has no attribute
'imp'.
But if I return in get_queryset() just Report.objects.filter(month=month).all() I'll get all objects and related object with all field, without aggregate of imp and not grouping.
So the question is how to make serializer return structure that set in queryset?
The get_queryset method requires to return a queryset but you are returning a tuple beacause of values_list. Either drop it to return a queryset or go with a more generic view like APIView.
I found a way how to do it.
As I use .values_list() it return list object instead of queryset object. So for serializer do understand what is inside the list I defined all fields in serializer. And in to_representation() I return dictionary like it should be.
Serializer:
class ReportSerializer(serializers.ModelSerializer):
user = serializers.IntegerField()
user_name = serializers.CharField()
order_id = serializers.IntegerField()
order_name = serializers.CharField()
order_type = serializers.IntegerField()
imp = serializers.IntegerField()
class Meta:
model = Report
fields = [
'user', 'user_name', 'order_id', 'order_name',
'order_type', 'imp'
]
depth = 1
def to_representation(self, instance):
Reports = namedtuple('Reports', [
'user',
'user_name',
'order_id',
'order_name',
'order_type',
'imp',
])
return super(ReportSerializer, self).to_representation(
Reports(*instance)._asdict()
)
View:
class ReportLisAPIView(ListAPIView):
serializer_class = ReportSerializer
def get_queryset(self):
month = parse_date(self.kwargs['month']) - relativedelta(day=1)
queryset = (
Report.objects.filter(month=month)
.values_list(
'user', 'user__name', 'order__order_id',
'order__order_name', 'order__order_type'
).all().annotate(Sum('imp'))
)
return queryset
def list(self, *args, **kwargs):
queryset = self.get_queryset()
serializer = self.serializer_class(queryset, many=True)
# actualy that's it! part of which is below can be pass and just
# return Response(serializer.data)
result = {
'month': parse_date(self.kwargs['month']).strftime('%Y-%m'),
'reports': []
}
inflcr = {}
for item in serializer.data:
inflcr.setdefault(item['user'], {
'id': item['user'],
'name': item['user_name'],
'campaigns': []
})
orders = {
'id': item['order_id'],
'name': item['order_name'],
'type': item['order_type'],
'impressions': item['imp'],
}
inflcr[item['user']]['campaigns'].append(orders)
result['reports'] = inflcr.values()
return Response(result)