Handling Partial Update¶
In proto3:
- All fields are optional
- Singular primitive fields, repeated fields, and map fields are initialized with default values (0, empty list, etc). There’s no way of telling whether a field was explicitly set to the default value (for example whether a boolean was set to false) or just not set at all.
If we want to do a partial update on resources, we need to know whether a field
was set or not set at all. There are different strategies that can be used to
represent unset
, we’ll use a pattern called "Has Pattern"
here.
Singular field absence¶
In proto3, for singular field types, you can use the parent message’s
HasField()
method to check if a message type field value has been set,
but you can’t do it with non-message singular types.
For primitive types if you need HasField
to you could use
"google/protobuf/wrappers.proto"
. Wrappers are useful for places where you
need to distinguish between the absence of a primitive typed field and its
default value:
import "google/protobuf/wrappers.proto";
service PersonController {
rpc PartialUpdate(PersonPartialUpdateRequest) returns (Person) {}
}
message Person {
int32 id = 1;
string name = 2;
string email = 3;
}
message PersonPartialUpdateRequest {
int32 id = 1;
google.protobuf.StringValue name = 2;
google.protobuf.StringValue email = 3;
}
Here is the client usage:
from google.protobuf.wrappers_pb2 import StringValue
with grpc.insecure_channel('localhost:50051') as channel:
stub = hrm_pb2_grpc.PersonControllerStub(channel)
request = hrm_pb2.PersonPartialUpdateRequest(id=1, name=StringValue(value="amy"))
response = stub.PartialUpdate(request)
print(response, end='')
The service implementation:
class PersonService(generics.GenericService):
queryset = Person.objects.all()
serializer_class = PersonProtoSerializer
def PartialUpdate(self, request, context):
instance = self.get_object()
serializer = self.get_serializer(instance, message=request, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return serializer.message
Or you can just use PartialUpdateModelMixin
to get the same behavior:
class PersonService(mixins.PartialUpdateModelMixin,
generics.GenericService):
queryset = Person.objects.all()
serializer_class = PersonProtoSerializer
Repeated and map field absence¶
If you need to check whether repeated fields and map fields are set or not, you need to do it manually:
message PersonPartialUpdateRequest {
int32 id = 1;
google.protobuf.StringValue name = 2;
google.protobuf.StringValue email = 3;
repeated int32 groups = 4;
bool is_groups_set = 5;
}