Hi I am using a Field Serializer to be able to serialize a PK field then deserialize it as object. Inside the serializer is a SerializerMethodField to build a custom url. It works when I use the itself from serializing its own record. However when I use it to a different serializer as a FieldSerializer, the request object is not passed.
class TelemetryFileSerializer(serializers.ModelSerializer):
telemetry_type = serializers.SlugRelatedField(
slug_field='name', queryset=TelemetryFileType.objects.all())
receiving_station = serializers.SlugRelatedField(
required=False, slug_field='name', queryset=ReceivingStation.objects.all())
link = serializers.SerializerMethodField()
class Meta:
model = TelemetryFile
fields = '__all__'
def get_link(self, object):
request = self.context.get('request')
print(self.context) # request is not passed here from RawImageSerializer/TelemetryFileField
return request.build_absolute_uri('/data_management/telemetry_files/{}'.format(object.id))
class TelemetryFileField(serializers.PrimaryKeyRelatedField):
def to_representation(self, value):
pk = super(TelemetryFileField, self).to_representation(value)
item = TelemetryFile.objects.get(pk=pk)
serializer = TelemetryFileSerializer(item)
return serializer.data
class RawImageSerializer(serializers.ModelSerializer):
from_telemetry_file = TelemetryFileField(queryset=TelemetryFile.objects.all())
link = serializers.SerializerMethodField()
I want to pass a request of itself to be able to create a url of it.
This is the returned when I use the RawImageSerializer:
AttributeError: 'NoneType' object has no attribute
'build_absolute_uri'
There must be a way to pass request from serializer to another...
I am not sure if this is the correct solution but adding this solved my problem...
class TelemetryFileField(serializers.PrimaryKeyRelatedField):
def to_representation(self, value):
print("value", self.context)
pk = super(TelemetryFileField, self).to_representation(value)
item = TelemetryFile.objects.get(pk=pk)
serializer = TelemetryFileSerializer(item, context=self.context) # context was added
return serializer.data
Related
I'm using django rest and I'm trying to update my django model with the changed values from a form POST.I place the model to be updated and new data in the serializer instance.
model_serializer = serializer.PersonSerializer(queryset, request.POST)
When performing model_serializer.is_valid() I get the error AttributeError: 'Person' object has no attribute '_default_manager'
I've looked at similar questions to this. I haven't found anything regarding a model object not having a '_default_manager'
In these questions they suggest changing the name of method/model due to conflicts. This hasn't worked for me.
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = models.Person()
fields = '__all__'
url(r'^api/personview/', views.PersonView.as_view()),
class PersonView(APIView):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'rest_person_form.html'
def get(self, request):
queryset = models.Person.objects.all().last()
model_serializer = serializer.PersonSerializer(queryset)
return Response({'serializer': model_serializer, 'queryset': queryset})
def post(self, request):
queryset = models.Person.objects.all().last()
model_serializer = serializer.PersonSerializer(queryset, request.POST)
model_serializer.is_valid()
model_serializer.save()
return Response({'serializer':model_serializer})
I'm expecting the is_valid() method to pass without errors to allow me to save my updated details into my model.
Attribute model is waiting for a class, not an instance.
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = models.Person # No () at the end
fields = '__all__'
Tip: If you aren't using the return from is_valid(), you could use is_valid(raise_exception=True) to raise error 400 automatically.
I create some dotted source field in my serializer. I did it cause have to display name value of foreign key not pk value. But when I trying to POST from frontend djang throws this : AssertionError at /api/my-api/
The .create() method does not support writable dotted-source fields by default.
Write an explicit .create() method for serializer MySerializer, or set read_only=True on dotted-source serializer fields.
So, when I set read_only = True my POST from frontend to request null for every field from dotted-source serializer fields.
This is my serializer:
class FcaWorksSerializer(serializers.ModelSerializer):
fell_form = serializers.CharField(source="fell_form.name" )
#...
main_type = serializers.CharField(source="main_type.name")
class Meta:
model = FcaWorks
fields = ('id_fca','wkod', 'main_type','fell_form','fell_type','kind',\
'sortiment','vol_drew','use_type','fca_res','ed_izm','vol_les','act_name',\
'obj_type','use_area','indicator','comment','date_report')
How I can to solve this problem?
Override the __init__() method of the serializer to adjust the serializer condition
class FcaWorksSerializer(serializers.ModelSerializer):
fell_form = serializers.CharField()
# ...
main_type = serializers.CharField()
class Meta:
model = FcaWorks
fields = ('id_fca', 'wkod', 'main_type', 'fell_form', 'fell_type', 'kind',
'sortiment', 'vol_drew', 'use_type', 'fca_res', 'ed_izm', 'vol_les', 'act_name',
'obj_type', 'use_area', 'indicator', 'comment', 'date_report')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.context['request'].method == 'GET':
self.fields['fell_form'].source = "fell_form.name"
self.fields['main_type'].source = "main_type.name"
def create(self, validated_data):
# here you will get the data
fell_form = validated_data['fell_form']
main_type = validated_data['main_type']
From the docs, there are multiple ways to deal with ForeignKey relations. You don't have to make your own create method if the Foreignkey relations are not "many-to-many".
In your case you can use one of the following:
SlugRelatedField
PrimaryKeyRelatedField
class FcaWorksSerializer(serializers.ModelSerializer):
fell_form = serializers.SlugRelatedField(slug_field="name", queryset = ***"""The queryset that fell_form came from"""*** )
#...
main_type = serializers.SlugRelatedField(slug_field="name", queryset = ***"""The queryset main_type came from"""***)
class Meta:
model = FcaWorks
fields = ('id_fca','wkod', 'main_type','fell_form','fell_type','kind',\
'sortiment','vol_drew','use_type','fca_res','ed_izm','vol_les','act_name',\
'obj_type','use_area','indicator','comment','date_report')
Then PrimaryKeyRelated Field usage:
class FcaWorksSerializer(serializers.ModelSerializer):
fell_form = serializers.PrimaryKeyRelatedField(source ="fell_form.name", queryset = ***"""The queryset that fell_form came from"""*** )
#...
main_type = serializers.PrimaryKeyRelatedField(source="main_type.name", queryset = ***"""The queryset main_type came from"""***)
This has worked for me when I had the same problem, however like previously stated for "Many-to-Many" field you have to explicitly write the create and update methods.
I have a nested serializer and I want to pass Parent serializer data to the child. But I don't understand how can I do this. I want to do something like this:
class BookingSerializer(serializers.ModelSerializer):
use_additional_fields = serializers.BooleanField()
persons = PersonSerializer(many=True)
class PersonSerializer(serializers.ModelSerializer):
def validate_date_of_birth(self, value):
if parent.use_additional_fields and not value:
raise serializers.ValidationError(_('Date of birth is required'))
return value
class Meta:
model = Person
exclude = ('phone', 'date_of_birth')
So if user select use_additional_fields in parent serializer, then some of my fields in child serializers should be required
You can get data from request object directly:
class PersonSerializer(serializers.ModelSerializer):
def validate_date_of_birth(self, value):
if self.context['request'].data.get('use_additional_fields') and not value:
raise serializers.ValidationError(_('Date of birth is required'))
return value
class Meta:
model = Person
exclude = ('phone', 'date_of_birth')
Note if you initiate serializer instance manually in your view, you should pass request to the serializer's context:
serializer = BookingSerializer(data=data, context={'request': request})
There are examples how to create a writable nested serializer like this and then how to serialize a generic foreign key (here).
But I cannot find how to do both at the same time, i.e how to create a nested writable serializer for a generic foreign key field.
In my models there is a Meeting model with a GenericForeignKey which can be either DailyMeeting or WeeklyMeeting like:
class Meeting(models.Model):
# More fields above
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
recurring_meeting = GenericForeignKey('content_type', 'object_id')
class DailyMeeting(models.Model):
meeting = GenericRelation(Meeting)
# more fields
class WeeklyMeeting(models.Model):
meeting = GenericRelation(Meeting)
# more fields
Then I created a custom field in my serializers.py:
class RecurringMeetingRelatedField(serializers.RelatedField):
def to_representation(self, value):
if isinstance(value, DailyMeeting):
serializer = DailyMeetingSerializer(value)
elif isinstance(value, WeeklyMeeting):
serializer = WeeklyMeetingSerializer(value)
else:
raise Exception('Unexpected type of tagged object')
return serializer.data
class MeetingSerializer(serializers.ModelSerializer):
recurring_meeting = RecurringMeetingRelatedField()
class Meta:
model = Meeting
fields = '__all__'
I am passing a JSON which looks like:
{
"start_time": "2017-11-27T18:50:00",
"end_time": "2017-11-27T21:30:00",
"subject": "Test now",
"moderators": [41],
"recurring_meeting":{
"interval":"daily",
"repetitions": 10,
"weekdays_only": "True"
}
}
But the problem is that I am getting the following error:
AssertionError: Relational field must provide a queryset argument, override get_queryset, or set read_only=True.
Why does the Relational field has to be read_only? If I set it as read_only then it is not passed in the data in the serializer.
And what type of queryset do I have to provide?
You need to implement to_internal_value as well, and you can use just plain Field class.
from rest_framework.fields import Field
class RecurringMeetingRelatedField(Field):
def to_representation(self, value):
if isinstance(value, DailyMeeting):
serializer = DailyMeetingSerializer(value)
elif isinstance(value, WeeklyMeeting):
serializer = WeeklyMeetingSerializer(value)
else:
raise Exception('Unexpected type of tagged object')
return serializer.data
def to_internal_value(self, data):
# you need to pass some identity to figure out which serializer to use
# supose you'll add 'meeting_type' key to your json
meeting_type = data.pop('meeting_type')
if meeting_type == 'daily':
serializer = DailyMeetingSerializer(data)
elif meeting_type == 'weekly':
serializer = WeeklyMeetingSerializer(data)
else:
raise serializers.ValidationError('no meeting_type provided')
if serializer.is_valid():
obj = serializer.save()
else:
raise serializers.ValidationError(serializer.errors)
return obj
If validation went well then you'll get created object in the MeetingSerializer validated data in other case RecurringMeetingRelatedField will raise an exception.
In this case instead of using a RecurringMeetingRelatedField in the Meeting serializer, you could define a nested serializer like this.
class RecurringMeetingSerializer(serializers.Serializer):
interval = serializers.CharField()
repetitions = serializers.IntegerField()
weekdays_only = serializers.BooleanField()
class Meta:
fields = '__all__'
class MeetingSerializer(serializers.ModelSerializer):
recurring_meeting = RecurringMeetingSerializer()
class Meta:
model = Meeting
exclude = ['object_id', 'content_type']
def create(self, validated_data):
recurring_meeting = validated_data.pop('recurring_meeting')
if recurring_meeting['interval'] == 'daily':
instance = DailyMeeting.objects.create(**recurring_meeting)
type = ContentType.objects.get_for_model(instance)
else:
instance = WeeklyMeeting.objects.create(**recurring_meeting)
type = ContentType.objects.get_for_model(instance)
meeting = Meeting.objects.create(content_type=type,
object_id=instance.id)
return meeting
I'm wondering which is the best way to solve this, I have a nested serializer like this:
serializers.py:
class PaymentMethodSerializer(serializers.ModelSerializer):
data = serializers.JSONField()
payment_type = PaymentTypeSerializer(required=False)
Then, the view looks something like this:
class PaymentMethodView(APIView):
def put(self, request, id):
try:
payment_method = PaymentMethod.objects.get(id=id)
except ObjectDoesNotExist:
return Response("No PaymentMethod with that id", status=status.HTTP_404_NOT_FOUND)
payment_method_serialized = PaymentMethodSerializer(instance=payment_method, data=request.data)
payment_type = request.data.get("payment_type", None)
if payment_type:
try:
payment_method_type = PaymentType.objects.get(id=payment_type)
except ObjectDoesNotExist:
payment_method_type = None
else:
payment_method_type = None
# Now, call is_valid()
if payment_method_serialized.is_valid():
payment_method_serialized.save(payment_type=payment_method_type)
return Response(payment_method_serialized.data, status=status.HTTP_200_OK)
is_valid() returns False and this error:
{"payment_type":{"non_field_errors":["Invalid data. Expected a dictionary, but got int."]}}
I understand it, I'm giving the serializer a pk. However I don't want to create a new serializer with a PrimaryKeyRelatedField instead of the nested relationship just for this. How can I get the PaymentType that corresponds to that pk and then add that object to the request.data dictionary so that is_valid doesn't fail? is it the best way to solve this?
Suppose payment_type is ForeignKey field in a PaymentMethod model, with null=True because of required=False.
If you provide only a pk so you don't need serializer for it field, you can just write in fields, like all other fields.
class PaymentMethodSerializer(serializers.ModelSerializer):
data = serializers.JSONField()
class Meta:
model = PaymentMethod
fields = ('data', 'payment_type')
It will accept pk or None. If you want to provide special representation for your PaymentType model, you can override a to_representation() method.
class PaymentMethodSerializer(serializers.ModelSerializer):
...
def to_representation(self, instance):
representation = super(PaymentMethodSerializer, self).to_representation(instance)
representation['payment_type'] = PaymentTypeSerializer(instance.payment_type).data
return representation
Now it will use PaymentTypeSerializer for representation of related PaymentType model.
You can move you payment_type validation in a serializer though. Override to_internal_value, check provided PaymentType pk and throw an exception if it wrong and then catch that exception in a view.