1
votes

Let's say I have an Appointment model. An appointment has a Doctor and a Patient. When creating an Appointment, the user picks a doctor and a patient. Which means only the actual Appointment is created. Not the Doctor and Patient.

When retrieving an appointment from the database, I want the representation to look like this:

{
  id: 1,
  doctor: {
    id: 8,
    name: 'Dr James Brown',
    phone: '107-102-304',
  },
  patient: {
    id: 4,
    name: 'Mr Elvis',
    phone: '123-456'
  }
}

So my serializer looks like:

class AppointmentSerializer(serializers.ModelSerializer):

  id            = serializers.IntegerField(read_only=True)
  doctor        = ContactSerializer()
  patient       = ContactSerializer()

  class Meta:
    model  = Appointment
    fields = (
      'id',
      'doctor',
      'patient',
    )

I want to be able to create an appointment using the same serializer. Which means the JSON that I will POST to the server looks like this (it's basically the same as above, without the id field, since it's a new record):

{
  doctor: {
    id: 9,
    name: 'Dr Otis Redding',
    phone: '107-102-304'
  },
  patient: {
    id: 4,
    name: 'Mr Ray Charles',
    phone: '123-456'
  }
}

I added the option read_only=False to my serializer (which should make the fields writable):

  doctor        = ContactSerializer(read_only=False)
  patient       = ContactSerializer(read_only=False)

But this raises an error:

Cannot assign "OrderedDict([(u'id', 14), (u'name', u'Josephine'), (u'phone', u'0123-456')])": "Appointment.doctor" must be a "Contact" instance.

I'd like to be able to tell Django-Rest-Framework: "Don't create the doctor and patient, but select them by id". I know that it's not necessary to send the whole representation when creating an appointment. Only the actual id's are necessary. But again, I'm doing this because i'd like to use the same serializer and keep the code cleaner.

Does that make sense? Is this the DRF way to go?

Ps: I noticed that it works when the relation is a many=True. Bu in my case, an appointment only has one doctor and one patient.

Thanks in advance.

1

1 Answers

2
votes

Ok here's what I ended up doing in case it is useful to someone:

class ContactField(serializers.Field):

  def to_internal_value(self, data)
    return Contact.objects.get(pk=data.get('id'))

  def to_representation(self, obj):
    return ContactSerializer(obj).data

And

class AppointmentSerializer(serializers.ModelSerializer):

  id            = serializers.IntegerField(read_only=True)
  doctor        = ContactField()
  patient       = ContactField()

  class Meta:
    model  = Appointment
    fields = (
      'id',
      'doctor',
      'patient',
    )