.. _getting_started: Getting Started =============== Quickstart Guide ---------------- We are going to create a simple blog application with a gRPC service. The blog application will have the following models: ``User`` and ``Post``. Prerequisites ~~~~~~~~~~~~~ You will need to install the following packages: - Python (>= 3.8) Installation ~~~~~~~~~~~~ Install the package via pip: .. code-block:: bash pip install django-socio-grpc Creating a New Project ~~~~~~~~~~~~~~~~~~~~~~ Now you can create the project by running the following command : .. code-block:: bash django-admin startproject dsg_tutorial Add now the following lines to the ``INSTALLED_APPS`` section of your ``dsg_tutorial/settings.py`` file: .. code-block:: python INSTALLED_APPS = [ ... 'django_socio_grpc', ] See `Django tutorial `_ for more information Adding a New App ~~~~~~~~~~~~~~~~ Then create a new app. First, cd into the project directory: .. code-block:: bash cd dsg_tutorial Create the new app: .. code-block:: bash python manage.py startapp quickstart This will create a new directory called ``quickstart`` inside your project directory including python files. Add the new app to the ``INSTALLED_APPS`` section of your ``dsg_tutorial/settings.py`` file: .. code-block:: python INSTALLED_APPS = [ ... 'quickstart', ] Finally migrate the database: .. code-block:: bash python manage.py migrate See `Django tutorial `_ for more information .. _getting-started-defining-models: Defining models ~~~~~~~~~~~~~~~~~~~~~~~ Create your models as described in the `Django documentation `_ . Each model is assigned to a table in the database. It inherits from ``django.db.models.Model`` Django class. Each attribute represents a field in the table. For directly working with the database, use the usual Django API (see `Query creation `_). .. code-block:: python #quickstart/models.py from django.db import models class User(models.Model): full_name = models.CharField(max_length=70) class Post(models.Model): # If you are using Django >= 5.0, consider using `db_default` instead of `auto_now_add`: https://docs.djangoproject.com/en/5.1/ref/models/fields/#django.db.models.Field.db_default pub_date = models.DateField(auto_now_add=True) headline = models.CharField(max_length=200) content = models.TextField() user = models.ForeignKey(User, on_delete=models.CASCADE) Defining serializers ~~~~~~~~~~~~~~~~~~~~~~~ Serializers convert the data from the Django database into protobuf format, that can be sent over the network via gRPC, and also back from protobuf into the Django database. In this simple example, our serializers inherit from ModelProtoSerializer, which is simply an inheritance of DRF's ModelSerializer. For more extensive use, you can use all the DRF serializer methods: `Django REST framework serializers `_. See :ref:`ProtoSerialzer doc page ` for more information. .. code-block:: python #quickstart/serializers.py from django_socio_grpc import proto_serializers from rest_framework import serializers from quickstart.models import User, Post class UserProtoSerializer(proto_serializers.ModelProtoSerializer): # This line is written here as an example, # but can be removed as the serializer integrates all the fields in the model full_name = serializers.CharField(allow_blank=True) class Meta: model = User fields = "__all__" class PostProtoSerializer(proto_serializers.ModelProtoSerializer): pub_date = serializers.DateTimeField(read_only=True) class Meta: model = Post fields = "__all__" .. _define-grpc-service: Defining gRPC services ~~~~~~~~~~~~~~~~~~~~~~~ Services define the gRPC actions that can be performed, e.g., on your models. :ref:`Its what is used to generate the protobuf files and gRPC stubs`. So all the gRPC actions that you want in your proto file should be declared or inhetited in your service. The gRPC service is the equivalent of the `DRF APIView `_ and behaves in a similar way (it only contains an additional internal layer). See additional note after example for comparaison. In the the following example we will create 2 services. - ``UserService``, will be a read-only service (:func:`AsyncReadOnlyModelService`), meaning that it will have 2 gRPC actions: `List` and `Retrieve`. - ``PostService``, will be a read-write service (:func:`AsyncModelService`), meaning that it will have 6 gRPC actions: `List`, `Retrieve`, `Create`, `Update`, `PartialUpdate`, `Destroy`. See :ref:`Mixin section ` and :ref:`Proto generation ` documentation to help you understand how actions are declared. .. code-block:: python #quickstart/services.py from django_socio_grpc import generics from quickstart.models import User, Post from quickstart.serializers import UserProtoSerializer, PostProtoSerializer # This service will have only the List and Retrieve actions class UserService(generics.AsyncReadOnlyModelService): queryset = User.objects.all() serializer_class = UserProtoSerializer # This service will have all the CRUD actions class PostService(generics.AsyncModelService): queryset = Post.objects.all() serializer_class = PostProtoSerializer .. warning:: DSG supports both sync and async, but we recommend using async services, since sync services will be deprecated in the future versions of DSG. .. note:: DSG Generic services and mixins are based on DRF Generic views and mixins, so you can create your services in a similar way as you would do with DRF in a class-based services, e.g.: In DSG : .. code-block:: python from django.contrib.auth.models import User from quickstart.serializers import UserProtoSerializer from django_socio_grpc import generics # This is and example of a custom service class MyListService(generics.ListCreateService): queryset = User.objects.all() serializer_class = UserProtoSerializer In DRF : .. code-block:: python from django.contrib.auth.models import User from quickstart.serializers import UserProtoSerializer from rest_framework import generics class MyListService(generics.ListCreateAPIView): queryset = User.objects.all() serializer_class = UserProtoSerializer .. _quickstart-register-services: Register services ~~~~~~~~~~~~~~~~~~~~~~~ You need to register your services in a handler function. This handler will be the entrypoint for your whole app. In this quickstart, we will register our services in the ``quickstart/handlers.py`` file. .. code-block:: python # quickstart/handlers.py from django_socio_grpc.services.app_handler_registry import AppHandlerRegistry from quickstart.services import UserService, PostService def grpc_handlers(server): app_registry = AppHandlerRegistry("quickstart", server) app_registry.register(UserService) app_registry.register(PostService) Set its path as the ``ROOT_HANDLERS_HOOK`` of the ``GRPC_FRAMEWORK`` :ref:`settings ` section of your ``dsg_tutorial/settings.py`` file: .. code-block:: python # dsg_tutorial/settings.py ... GRPC_FRAMEWORK = { "ROOT_HANDLERS_HOOK" : 'quickstart.handlers.grpc_handlers', ... } .. note:: To better understand the register process and have recommandation about the ``handlers.py`` file for more complex project please read the :ref:`Service Registry documentation` .. _quickstart-generate-proto: Generate the app's Protobuf files and gRPC stubs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To generate (and update) the .proto files and gRPC stubs from the services defined in service.py, you need to run the following command: .. code-block:: python python manage.py generateproto See `Proto generation `_ for more information. This will generate a folder called ``grpc`` at the root of your Django app. It contains the three files describing your new gRPC service: - `quickstart_pb2_grpc.py` - `quickstart_pb2.py` - `quickstart.proto` **DSG generate all the file needed by gRPC. Meaning that you don't need to deal with protofile manually.** Assign newly generated classes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In the newly generated ``quickstart/grpc/quickstart.proto`` file, you can find the structure of Service, responses message and requests message. For each serializer of your app attached to a service, you will find the associated protobuf **message**. If the serializer is used as a list you will also find the associated **list message**. To let the DSG magic opere you need to manually (for now) reassign this generated message to the Serializer. This message exit in python class in the generated ``pb2`` file. You only need to import the messages in the ``serializers.py`` file and assign them to the serializers, like in the following example: .. code-block:: python #quickstart/serializers.py ... from quickstart.grpc.quickstart_pb2 import ( UserResponse, UserListResponse, PostResponse, PostListResponse, ) class UserProtoSerializer(proto_serializers.ModelProtoSerializer): ... class Meta: ... proto_class = UserResponse proto_class_list = UserListResponse class PostProtoSerializer(proto_serializers.ModelProtoSerializer): ... class Meta: ... proto_class = PostResponse proto_class_list = PostListResponse Running the Server ~~~~~~~~~~~~~~~~~~ You can now run the gRPC server with the following command: .. code-block:: python python manage.py makemigrations python manage.py migrate python manage.py grpcrunaioserver --dev The server is now running on port `50051` by default. See :ref:`How To Web ` to see how to call this server with web client or :ref:`Python example ` for python client example. To read more about the grpcrunaioserver please :ref:`read the commands documentation ` To continue reading consider read: - :ref:`Unit testing the quickstart app ` - :ref:`Generic Mixins ` - :ref:`gRPC Action ` - :ref:`Proto Serializers ` - :ref:`Proto generation `