I am new to DRF. I want to get saved the model.
In models.py, PackageDetails and PhysicalDetail have foreignkey relationship to Member
My serializers.py is as follows:
from rest_framework import serializers
from .models import Member, PackageDetails, PhysicalDetail
class PackageDetailsSerializer(serializers.ModelSerializer):
is_expired = serializers.SerializerMethodField()
members_expiry_date = serializers.SerializerMethodField()
class Meta:
model = PackageDetails
exclude = ['id']
extra_fields = ['is_expired', 'members_expiry_date']
def get_is_expired(self, instance):
return instance.is_expired
def get_members_expiry_date(self, instance):
return instance.members_expiry_date
class PhysicalDetailSerializer(serializers.ModelSerializer):
class Meta:
model = PhysicalDetail
exclude = ['id']
class MemberSerializer(serializers.ModelSerializer):
physical_details = PhysicalDetailSerializer(many=True)
package_details = PackageDetailsSerializer(many=True)
class Meta:
model = Member
fields = '__all__'
extra_fields = ['physical_details', 'package_details']
def create(self, validated_data):
physical_detail_data = validated_data.pop("physical_details")
package_detail_data = validated_data.pop("package_details")
member = Member.objects.create(**validated_data)
PhysicalDetail.objects.create(member=member, **physical_detail_data)
PackageDetails.objects.create(member=member, **package_detail_data)
return member
views.py :
class MemberViewset(viewsets.ModelViewSet):
queryset = Member.objects.all()
serializer_class = MemberSerializer
class PackageDetailViewset(viewsets.ModelViewSet):
queryset = PackageDetails.objects.all()
serializer_class = PackageDetailsSerializer
class PhysicalDetailViewset(viewsets.ModelViewSet):
queryset = PhysicalDetail.objects.all()
serializer_class = PhysicalDetailSerializer
In GET request it worked well.. but in POST request with the same json format it responses the following:
{
"physical_details": [
"This field is required."
],
"package_details": [
"This field is required."
]
}
I've provided the fields.. so why this happening..
You removed those from dict using pop()
The pop() method removes and returns an element from a dictionary having the given key.
Try using get() instead
The get() method returns the value for the specified key if the key is in the dictionary.
Related
I am trying to do a fairly simple GET request that leads to a query with DRF:
def get(self, request, character):
char_entry = Dictionary.objects.filter(Q(simplified=character) | Q(traditional=character))
serializer = DictionarySerializer(char_entry)
return Response({"character": serializer.data})
My DictionarySerializer looks like this:
from rest_framework import serializers
from .models import Dictionary
class DictionarySerializer(serializers.ModelSerializer):
class Meta:
model = Dictionary
fields = ["id", "simplified", "pinyin_numbers", "pinyin_marks", "translation", "level", "traditional", ]
And my Dictionary model looks like this:
class Dictionary(models.Model):
traditional = models.CharField(max_length=50)
simplified = models.CharField(max_length=50)
pinyin_numbers = models.CharField(max_length=50)
pinyin_marks = models.CharField(max_length=50)
translation = models.TextField()
level = models.IntegerField()
class Meta:
db_table = 'dictionary'
indexes = [
models.Index(fields=['simplified', ]),
models.Index(fields=['traditional', ]),
]
As far as I can tell, this should serialize all the fields from the Dictionary table, including simplified.
Why can't Django find the attribute? What am I missing?
A QuerySet is a collection of items. It can thus contain zero, one or more items. YOu need to serialize with the many=True parameter:
def get(self, request, character):
char_entry = Dictionary.objects.filter(
Q(simplified=character) | Q(traditional=character)
)
serializer = DictionarySerializer(char_entry, many=True)
return Response({'character': serializer.data})
or if we know there is only one item, we should retrieve that single item:
from django.shortcuts import get_object_or_404
def get(self, request, character):
char_entry = get_object_or_404(
Dictionary,
Q(simplified=character) | Q(traditional=character)
)
serializer = DictionarySerializer(char_entry, many=True)
return Response({'character': serializer.data})
For example i have a few models:
class Parent(Model):
api_key = CharField(max_length=250)
class Child(Model):
parent = ForeignKey(Parent, related_name='children')
status = CharField(max_length=250)
I wrote view based on ListAPIView, and serializer:
class ChildSerializer(ModelSerializer):
class Meta:
fields = ['id']
I need to take all Children with parent by find Parent by api_key and return this as:
{
children:[
{'id':1},
{'id':2}
]}
But i take this:
[
{'id':1},
{'id':2}
]
Well, you can define a ParentSerializer which will also return the child objects data with it:
class ParentSerializer(ModelSerializer):
child = ChildSerializer(many=True)
class Meta:
fields = ['api_key', 'child']
Then you need to find the parent by api_key and pass the Parent object to this serializer. Like this:
class ParentView(RetrieveAPIView):
queryset = Parent.objects.all()
lookup_field = 'api_key'
serializer_class = ParentSerializer
lookup_url_kwarg = 'api_key'
Finally set the url to:
path('/parent/<str:api_key>/', ParentView.as_view(), name='parent-detail')
Update
If you need to get the data from request.GET(url querystring), then it much simpler. Try like this:
class ParentView(ListAPIView):
queryset = Parent.objects.all()
serializer_class = ParentSerializer
def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs)
api_key = request.GET.get('api_key')
if api_key:
return queryset.filter(api_key=api_key)
return queryset
I'm writing a Django REST Framework API.
My models have default Django PK for internal use AND uuid field for external reference.
class BaseModel(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, editable=False)
class Event(BaseModel):
title = models.TextField()
location = models.ForeignKey('Location', null=True, on_delete=models.SET_NULL)
class Location(BaseModel):
latitude = models.FloatField()
longitude = models.FloatField()
And my serializers:
class BaseSerializer(serializers.ModelSerializer):
default_fields = ('uuid',)
class EventSerializer(BaseSerializer):
class Meta:
model = Event
lookup_field = 'uuid' # This does not work
fields = BaseSerializer.default_fields + ('title', 'location',)
class LocationSerializer(BaseSerializer):
class Meta:
model = Location
lookup_field = 'uuid' # This does not work
fields = BaseSerializer.default_fields + ('latitude', 'longitude',)
This works fine, here is what I got when I retrieve an Event:
{
"uuid": "ef33db27-e98b-4c26-8817-9784dfd546c6",
"title": "UCI Worldcup #1 Salzburg",
"location": 1 # Note here I have the PK, not UUID
}
But what I would like is:
{
"uuid": "ef33db27-e98b-4c26-8817-9784dfd546c6",
"title": "UCI Worldcup #1 Salzburg",
"location": "2454abe7-7cde-4bcb-bf6d-aaff91c107bf" # I want UUID here
}
And of course I want this behavior to work for all my ForeignKeys and ManyToMany fields.
Is there a way to customize the field used by DRF for nested models ?
Thanks !
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ObjectDoesNotExist
from rest_framework.relations import RelatedField
from django.utils.encoding import smart_text
class UUIDRelatedField(RelatedField):
"""
A read-write field that represents the target of the relationship
by a unique 'slug' attribute.
"""
default_error_messages = {
'does_not_exist': _('Object with {uuid_field}={value} does not exist.'),
'invalid': _('Invalid value.'),
}
def __init__(self, uuid_field=None, **kwargs):
assert uuid_field is not None, 'The `uuid_field` argument is required.'
self.uuid_field = uuid_field
super().__init__(**kwargs)
def to_internal_value(self, data):
try:
return self.get_queryset().get(**{self.uuid_field: data})
except ObjectDoesNotExist:
self.fail('does_not_exist', uuid_field=self.uuid_field, value=smart_text(data))
except (TypeError, ValueError):
self.fail('invalid')
def to_representation(self, obj):
return getattr(obj, self.uuid_field)
Sample Usage:
class ProductSerializer(serializers.ModelSerializer):
category = UUIDRelatedField(
queryset=Category.objects.all(),
uuid_field='alias'
)
class Meta:
model = Product
fields = (
'id',
'alias',
'name',
'category',
)
read_only_fields = (
'id',
'alias',
)
Note that as of Django version 4, smart_text and ugettext_lazy were removed, use smart_str and gettext_lazy instead of them:
from django.utils.encoding import gettext_lazy
from django.utils.encoding import smart_str
A friend of mine send me this solution:
It works with all my related objects.
from rest_framework import serializers
from rest_framework.relations import SlugRelatedField
class UuidRelatedField(SlugRelatedField):
def __init__(self, slug_field=None, **kwargs):
slug_field = 'uuid'
super().__init__(slug_field, **kwargs)
class BaseSerializer(serializers.ModelSerializer):
default_fields = ('uuid',)
serializer_related_field = UuidRelatedField
class Meta:
pass
For nested model fields you can use the source argument in a serializer like this
class EventSerializer(BaseSerializer):
location = serializers.CharField(source='location.uuid')
class Meta:
model = Event
lookup_field = 'uuid' # This does not work
fields = BaseSerializer.default_fields + ('title', 'location',)
This is my serializers.py,
class MalbSerializer(serializers.ModelSerializer):
class Meta:
model = malb
fields = ('zoning', 'zoningdesc', )
class MasrSerializer(serializers.ModelSerializer):
class Meta:
model = masr
fields = ('solddate', 'soldprice', )
class MataSerializer(serializers.ModelSerializer):
class Meta:
model = mata
fields = ('assessyear', 'landvalue', )
class TotalSerializer(serializers.ModelSerializer):
LandBuilding = serializers.SerializerMethodField()
SalesRecord = serializers.SerializerMethodField()
TaxAssessment = serializers.SerializerMethodField()
def get_LandBuilding(self, number):
queryset_lb = malb.objects.filter(maid=number)
serializer = MalbSerializer(queryset_lb, many=True)
return serializer.data
def get_SalesRecord(self, number):
queryset_sr = masr.objects.filter(maid=number)
serializer = MasrSerializer(queryset_sr, many=True)
return serializer.data
def get_TaxAssessment(self, number):
queryset_ta = mata.objects.filter(maid=number)
serializer = MataSerializer(queryset_ta, many=True)
return serializer.data
class Meta:
fields = ('LandBuilding', 'SalesRecord', 'TaxAssessment', )
I want to assemble these three serializers to one serializer in TotalSerializer, But it has an error:
Class TotalSerializer missing "Meta.model" attribute
I don't know add which models to here, because I have already add models in MalbSerializer, MasrSerializer, MataSerializer.
So How can I do to show MalbSerializer, MasrSerializer, MataSerializer together in TotalSerializer?
TotalSerializer should subclass serializers.Serializer, not serializers.ModelSerializer.
I have two serializers, one of which is nested:
class PaperSerializer(serializers.ModelSerializer):
class Meta:
model = Paper
class AuthorSerializer(serializers.ModelSerializer):
papers = PaperSerializer(
many=True,
read_only=True,
source='paper_set'
)
class Meta:
model = Author
I want to get a list of Authors which shows only their published Papers (Boolean field exists on the model).
I would like to call the API like /api/v1/authors/?show_published_only=true.
After some digging around, I discovered that you can pass the context from the ViewSet to the Serializer:
views.py
class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
filter_fields = (
'show_published_only',
)
def get_serializer_context(self):
return {'request': self.request}
Now, create a new serializer FilteredPaperSerializer which inherits from serializers.ListSerializer, then override the to_representation() method to filter the queryset:
serializers.py
class FilteredPaperSerializer(serializers.ListSerializer):
def to_representation(self, data):
# Get the parameter from the URL
show_published_only = self.context['request'].query_params['show_published_only']
data = data.filter(is_published=show_published_only)
return super(FilteredPaperSerializer, self).to_representation(data)
class AuthorSerializer(serializers.ModelSerializer):
papers = FilteredPaperSerializer(
many=True,
read_only=True,
source='paper_set'
)
class Meta:
model = Author
NB: Don't forget to convert the fetched URL parameter to a Boolean or relevant data type for your model, I neglected to do it in the write-up above.