Skip to content

Commit

Permalink
allow default to be a callable and review improvement
Browse files Browse the repository at this point in the history
  • Loading branch information
AMontagu committed Mar 11, 2024
1 parent b9a6ce5 commit 2716fed
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 73 deletions.
31 changes: 16 additions & 15 deletions django_socio_grpc/proto_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@
LIST_PROTO_SERIALIZER_KWARGS = (*LIST_SERIALIZER_KWARGS, LIST_ATTR_MESSAGE_NAME, "message")


def get_default_value(field_default):
if callable(field_default):
return field_default()
else:
return field_default


class BaseProtoSerializer(BaseSerializer):
def __init__(self, *args, **kwargs):
message = kwargs.pop("message", None)
Expand Down Expand Up @@ -68,7 +75,9 @@ def populate_dict_with_none_if_not_required(self, data_dict, message=None):
code="missing_partial_message_attribute",
)

is_update_process = bool(data_dict.get(self.Meta.model._meta.pk.name, ""))
is_update_process = (
hasattr(self.Meta, "model") and self.Meta.model._meta.pk.name in data_dict
)
for field in self.fields.values():
# INFO - AM - 04/01/2024 - If we are in a partial serializer we only need to have field specified in PARTIAL_UPDATE_FIELD_NAME attribute in the data. Meaning deleting fields that should not be here and not adding None to allow_null field that are not specified
if self.partial and field.field_name not in data_dict.get(
Expand All @@ -81,28 +90,22 @@ def populate_dict_with_none_if_not_required(self, data_dict, message=None):
if field.field_name in data_dict:
continue

# INFO - AM - 04/01/2024 - if field is not in the data_dict but in PARTIAL_UPDATE_FIELD_NAME we need to set the default value if existing or raise exception to avoid having default grpc value by mistake
if self.partial and field.field_name in data_dict.get(
PARTIAL_UPDATE_FIELD_NAME, {}
):
if field.allow_null:
data_dict[field.field_name] = None
continue
if field.default not in [None, empty]:
# TODO - AM - 12/01/2024 - is field.default a possible callable here ?
data_dict[field.field_name] = field.default
data_dict[field.field_name] = get_default_value(field.default)
continue

# INFO - AM - 11/03/2024 - Here we set the default value especially for the blank authorized data. We debated about raising a ValidaitonError but prefered this behavior. Can be changed if it create issue with users
data_dict[field.field_name] = message.DESCRIPTOR.fields_by_name[
field.field_name
].default_value

# INFO - AM - 12/01/2024 - if field name is not in data_dict but is required that mean that it's a default value deleted when transforming proto to dict
if field.required and field.field_name in message.DESCRIPTOR.fields_by_name:
data_dict[field.field_name] = message.DESCRIPTOR.fields_by_name[
field.field_name
].default_value
continue

if field.allow_null or (field.default in [None, empty] and field.required is True):
if is_update_process:
data_dict[field.field_name] = None
Expand All @@ -119,14 +122,12 @@ def populate_dict_with_none_if_not_required(self, data_dict, message=None):
):
deferred_attribute = getattr(self.Meta.model, field.field_name)
if deferred_attribute.field.default != NOT_PROVIDED:
data_dict[field.field_name] = deferred_attribute.field.default
data_dict[field.field_name] = get_default_value(
deferred_attribute.field.default
)
continue

data_dict[field.field_name] = None
continue

# elif field.required and field.field_name in message.DESCRIPTOR.fields_by_name:
# data_dict[field.field_name] = message.DESCRIPTOR.fields_by_name[field.field_name].default_value
return data_dict

def data_to_message(self, data):
Expand Down
2 changes: 1 addition & 1 deletion django_socio_grpc/protobuf/json_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def message_to_dict(message, **kwargs):
Uses the default `google.protobuf.json_format.MessageToDict` function.
Adds None values for optional fields that are not set.
"""

kwargs.setdefault("including_default_value_fields", True)
kwargs.setdefault("preserving_proto_field_name", True)

return MessageToDict(message, **kwargs)
Expand Down
12 changes: 6 additions & 6 deletions django_socio_grpc/tests/fakeapp/grpc/fakeapp.proto
Original file line number Diff line number Diff line change
Expand Up @@ -642,20 +642,20 @@ message UnitTestModelWithStructFilterListResponse {
}

message UnitTestModelWithStructFilterPartialUpdateRequest {
int32 id = 1;
string title = 2;
optional string text = 3;
repeated string _partial_update_fields = 4;
optional int32 id = 1;
repeated string _partial_update_fields = 2;
string title = 3;
optional string text = 4;
}

message UnitTestModelWithStructFilterRequest {
int32 id = 1;
optional int32 id = 1;
string title = 2;
optional string text = 3;
}

message UnitTestModelWithStructFilterResponse {
int32 id = 1;
optional int32 id = 1;
string title = 2;
optional string text = 3;
}
Expand Down
70 changes: 45 additions & 25 deletions django_socio_grpc/tests/fakeapp/grpc/fakeapp_pb2.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ message UnitTestModelStreamRequest {
}

message UnitTestModelWithStructFilter {
int32 id = 1;
optional int32 id = 1;
string title = 2;
optional string text = 3;
}
Expand All @@ -465,10 +465,10 @@ message UnitTestModelWithStructFilterList {
}

message UnitTestModelWithStructFilterPartialUpdateRequest {
int32 id = 1;
string title = 2;
optional string text = 3;
repeated string _partial_update_fields = 4;
optional int32 id = 1;
repeated string _partial_update_fields = 2;
string title = 3;
optional string text = 4;
}

message UnitTestModelWithStructFilterRetrieveRequest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -642,20 +642,20 @@ message UnitTestModelWithStructFilterListResponse {
}

message UnitTestModelWithStructFilterPartialUpdateRequest {
int32 id = 1;
string title = 2;
optional string text = 3;
repeated string _partial_update_fields = 4;
optional int32 id = 1;
repeated string _partial_update_fields = 2;
string title = 3;
optional string text = 4;
}

message UnitTestModelWithStructFilterRequest {
int32 id = 1;
optional int32 id = 1;
string title = 2;
optional string text = 3;
}

message UnitTestModelWithStructFilterResponse {
int32 id = 1;
optional int32 id = 1;
string title = 2;
optional string text = 3;
}
Expand Down
30 changes: 15 additions & 15 deletions test_utils/generateproto.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@
call_command("generateproto", *args, **opts)

# INFO - AM - 29/12/2023 - This for loop is used to generate proto file used in proto tests to avoid changing them by hand
# for name, grpc_settings in OVERRIDEN_SETTINGS.items():
# RegistrySingleton.clean_all()
# settings = {}
# if grpc_settings:
# settings = {
# "GRPC_FRAMEWORK": grpc_settings,
# }
# with override_settings(**settings):
# call_command(
# "generateproto",
# no_generate_pb2=True,
# directory=f"./django_socio_grpc/tests/protos/{name}",
# project="myproject",
# override_fields_number=override_fields_number,
# )
for name, grpc_settings in OVERRIDEN_SETTINGS.items():
RegistrySingleton.clean_all()
settings = {}
if grpc_settings:
settings = {
"GRPC_FRAMEWORK": grpc_settings,
}
with override_settings(**settings):
call_command(
"generateproto",
no_generate_pb2=True,
directory=f"./django_socio_grpc/tests/protos/{name}",
project="myproject",
override_fields_number=override_fields_number,
)

0 comments on commit 2716fed

Please sign in to comment.