Package: django-grpc
django-grpcEasy way to launch gRPC server with access to Django ORM and other handy stuff.
gRPC calls are much faster that traditional HTTP requests because communicate over
persistent connection and are compressed. Underlying gRPC library is written in C which
makes it work faster than any RESTful framework where a lot of time is spent on serialization/deserialization.Note that you need this project only if you want to use Django functionality in gRPC service.
For pure python implementationread thisSupported Python: 3.4+Supported Django: 2.X, 3.X and 4.XInstallationpipinstalldjango-grpcUpdate settings.pyINSTALLED_APPS=[# ...'django_grpc',]GRPCSERVER={'servicers':['dotted.path.to.callback.eg.grpc_hook'],# see `grpc_hook()` below'interceptors':['dotted.path.to.interceptor_class',],# optional, interceprots are similar to middleware in Django'maximum_concurrent_rpcs':None,'options':[("grpc.max_receive_message_length",1024*1024*100)],# optional, list of key-value pairs to configure the channel. The full list of available channel arguments: https://grpc.github.io/grpc/core/group__grpc__arg__keys.html'credentials':[{'private_key':'private_key.pem','certificate_chain':'certificate_chain.pem'}],# required only if SSL/TLS support is required to be enabled'async':False# Default: False, if True then gRPC server will start in ASYNC mode}The callback that initializes "servicer" must look like following:importmy_pb2importmy_pb2_grpcdefgrpc_hook(server):my_pb2_grpc.add_MYServicer_to_server(MYServicer(),server)...classMYServicer(my_pb2_grpc.MYServicer):defGetPage(self,request,context):response=my_pb2.PageResponse(title="Demo object")returnresponseUsagepythonmanage.pygrpcserverFor developer's convenience add--autoreloadflag during development.SignalsThe package uses Django signals to allow decoupled applications get notified when some actions occur:django_grpc.signals.grpc_request_started- sent before gRPC server begins processing a requestdjango_grpc.signals.grpc_request_finished- sent when gRPC server finishes delivering response to the clientdjango_grpc.signals.grpc_got_request_exception- this signal is sent whenever RPC encounters an exception while
processing an incoming request.Note that signal names are similar to Django's built-in signals, but have "grpc_" prefix.SerializersThere is an easy way to serialize django model to gRPC message usingdjango_grpc.serializers.serialize_model.HelpersRatelimitsYou can limit number of requests to your procedures by using decoratordjango_grpc.helpers.ratelimit.ratelimit.fromtests.sampleappimporthelloworld_pb2_grpc,helloworld_pb2fromdjango_grpc.helpersimportratelimitclassGreeter(helloworld_pb2_grpc.GreeterServicer):@ratelimit(max_calls=10,time_period=60)defSayHello(self,request,context):returnhelloworld_pb2.HelloReply(message='Hello,%s!'%request.name)When limit is reached for given time period decorator will abort with statusgrpc.StatusCode.RESOURCE_EXHAUSTEDAs storage for state of callsDjango's cache frameworkis used. By default"default"cache system is used but you can specify any other in settingsRATELIMIT_USE_CACHEAdvanced usageUsing groups@ratelimit(max_calls=10,time_period=60,group="main")deffoo(request,context):...@ratelimit(max_calls=5,time_period=60,group="main")defbar(request,context):...fooandbarwill share the same counter because they are in the same groupUsing keys@ratelimit(max_calls=5,time_period=10,keys=["request:dot.path.to.field"])@ratelimit(max_calls=5,time_period=10,keys=["metadata:user-agent"])@ratelimit(max_calls=5,time_period=10,keys=[lambdarequest,context:context.peer()])Right now 3 type of keys are supported with prefixes"request:","metadata:"and as callable."request:"allows to extract request's field value by doted path"metadata:"allows to extract metadata fromcontext.invocation_metadata()callable function that takes request and context and returns stringNOTE: if value of key is empty string it still will be considered a valid value
and can cause sharing of ratelimits between different RPCs in the same groupTIP: To use the same configuration for different RPCs use dict variableMAIN_GROUP={"max_calls":5,"time_period":60,"group":"main"}@ratelimit(**MAIN_GROUP)deffoo(request,context):...@ratelimit(**MAIN_GROUP)defbar(request,context):...TestingTest your RPCs just like regular python methods which return some
structure or generator. You need to provide them with only 2 parameters:
request (protobuf structure or generator) and context (useFakeServicerContextfrom the example below).Fake ContextYou can pass instance ofdjango_grpc_testtools.context.FakeServicerContextto your gRPC method
to verify how it works with context (aborts, metadata and etc.).importgrpcfromdjango_grpc_testtools.contextimportFakeServicerContextfromtests.sampleapp.servicerimportGreeterfromtests.sampleapp.helloworld_pb2importHelloRequestservicer=Greeter()context=FakeServicerContext()request=HelloRequest(name='Tester')# To check metadata set by RPCresponse=servicer.SayHello(request,context)assertcontext.get_trailing_metadata("Header1")=='...'# To check status codetry:servicer.SayHello(request,context)exceptException:passassertcontext.abort_status==grpc.StatusCode.INVALID_ARGUMENTassertcontext.abort_message=='Cannot say hello to John'In addition to standard gRPC context methods, FakeServicerContext provides:.set_invocation_metadata()allows to simulate metadata from client to server..get_trailing_metadata()to get metadata set by your server.abort_statusand.abort_messageto check if.abort()was called