{ "info": { "author": "myyang", "author_email": "ymy1019@gmail.com", "bugtrack_url": null, "classifiers": [ "Environment :: Web Environment", "Framework :: Django", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: Dynamic Content" ], "description": "django-pb-model\n=========================\n\n.. image:: https://travis-ci.org/myyang/django-pb-model.svg?branch=master\n :target: https://travis-ci.org/myyang/django-pb-model\n\n.. image:: https://img.shields.io/pypi/v/django-pb-model.svg\n :target: https://pypi.python.org/pypi/django-pb-model\n\n\nDjango-pb-model provides model mixin mapping/converting protobuf message.\nAutomatic model generation from protobuf message definitions is supported.\nCurrently support basic value fields and naive relation conversion, including:\n\n* Integer, String, Float, Boolean\n* Choices field\n* Datetime\n* Foreign Key and Many-to-Many relation\n* `Custom fields`_, ex: JSON\n\nYou could examine testcases_ for more details\n\n.. _testcases: https://github.com/myyang/django-pb-model/tree/master/pb_model/tests\n.. _Custom fields: https://github.com/myyang/django-pb-model#custom-fields\n\nAnd PRs are always welcome :))\n\n\nCompatibility\n-------------\n\nCurrently tested with metrics:\n\n* Python2.7, 3.4, 3.5, 3.6, 3.7\n* Django1.11, 2.0, 2.1, 2.2\n\nInstall\n-------\n\n1. pip install\n\n.. code:: shell\n\n pip install django-pb-model\n\n2. Add django-pb to django ``settings.py``\n\n.. code:: python\n\n INSTALLED_APPS = [\n ....,\n pb_model,\n ...\n ]\n\n3. Run python/django essential commands:\n\n.. code:: shell\n\n python manage.py makemigrations\n python manage.py migrate\n python manage.py collectstatic -l\n\n4. Start hacking or using app.\n\nUsage\n-----\n\nDeclare your protobuf message file, such as ``account.proto``, and compile it. For example:\n\n.. code:: protobuf\n\n message Account {\n int id = 1;\n string email = 2;\n string nickname = 3;\n }\n\nThen compile it with:\n\n.. code:: shell\n\n $ protoc --python_out=. account.proto\n\nYou will get ``account_pb2.py``.\n\nNow you can interact with your protobuf model, add ``ProtoBufMixin`` to your model like:\n\n.. code:: python\n\n from django.db import models\n from pb_model.models import ProtoBufMixin\n from . import account_pb2\n\n class Account(ProtoBufMixin, models.Model):\n pb_model = account_pb2.Account\n\n email = models.EmailField(max_length=64)\n nickname = models.CharField(max_length=64)\n\n def __str__(self):\n return \"Username: {a.email}, nickname: {a.nickname}\".format(a=self)\n\n\nBy above settings, you can covert between django model and protobuf easily. For example:\n\n.. code:: python\n\n >>> account = Account.objects.create(email='user@email.com', nickname='moonmoon')\n >>> account.to_pb()\n email: \"user@email.com\"\n nickname: \"moonmoon\"\n\n >>> account2 = Account()\n >>> account2.from_pb(account.to_pb())\n \n\n\nAutomatic field generation\n--------------------------\n\nTo automatically generate django model fields based on protobuf field types.\n\nIf you don't want to manually specify fields in your django model, you can list names of desired fields under ``pb_2_dj_fields`` attribute to have those generated and added to your model automatically.\n\n.. code:: python\n\n class Account(ProtoBufMixin, models.Model):\n pb_model = account_pb2.Account\n pb_2_dj_fields = ['email', 'nickname']\n\n\nAlternatively if you want all protobuf fields to be mapped you can do ``pb_2_dj_fields = '__all__'``.\n\nFields listed in ``pb_2_dj_fields`` can be overwritten using manuall definition.\n\n.. code:: python\n\n class Account(ProtoBufMixin, models.Model):\n pb_model = account_pb2.Account\n pb_2_dj_fields = '__all__'\n\n email = models.EmailField(max_length=64)\n\n\nType of generated field depends on corresponding protobuf field type. If you want to change default field type mappings you can overwrite those using ``pb_auto_field_type_mapping`` attribute.\n\nFollowing protobuf field types are supported:\n\n* uint32, int32, uint64, int64, float, double, bool, Enum\n* string, bytes\n* google.protobuf.Timestamp\n* Messages\n* oneof fields\n* repeated scalar and Message fields\n* map fields with scalar as key and scalar or Message as value\n\nField details\n-------------\n\nThere are several special field types while converting, read following sections.\n\nField name mapping\n~~~~~~~~~~~~~~~~~~~~~\n\nTo adapt schema migration, field mapping are expected.\n\nFor example, the ``email`` field in previous session is altered to ``username``, but we don't want to break the consistence of protobuf protocol. You may add ``pb_2_dj_field_map`` attribute to solve this problem. Such as:\n\n.. code:: python\n\n class Account(ProtoBufMixin, models.Model):\n pb_model = account_pb2.Account\n pb_2_dj_field_map = {\n \"email\": \"username\", # protobuf field as key and django field as value\n }\n\n username = models.CharField(max_length=64)\n nickname = models.CharField(max_length=64)\n\nForeign Key\n~~~~~~~~~~~\n\nForeign key is a connect to another model in Django. According to this property, the foreign key could and should be converted to nested singular message in Protobuf. For example:\n\n.. code:: Protobuf\n\n message Relation {\n int32 id = 1;\n }\n\n message Main {\n int32 id = 1;\n Relation fk = 2;\n }\n\nDjango model:\n\n.. code:: python\n\n class Relation(ProtoBufMixin, models.Model):\n pb_model = models_pb2.Relation\n\n\n class Main(ProtoBufMixin, models.Model):\n pb_model = models_pb2.Main\n\n fk = models.ForeignKey(Relation)\n\n\nWith above settings, pb_model would recursively serialize and de-serialize between Django and ProtoBuf.\n\n.. code:: python\n\n >>> m = Main.objects.create(fk=Relation.objects.create())\n >>> m.to_pb()\n id: 1\n fk {\n id: 1\n }\n\n >>> m2 = Main()\n >>> m2.from_pb(m.to_pb())\n >>> m2.fk.id\n 1\n\n\n\nMany-to-Many field\n~~~~~~~~~~~~~~~~~~\n\nM2M field is a QuerySet Relation in Django. \nBy default, we assume target message field is \"repeated\" nested message, ex:\n\n.. code:: protobuf\n\n message M2M {\n int32 id = 1;\n }\n\n message Main {\n int32 id = 1;\n\n repeated M2M m2m = 2;\n }\n\nDjango model would be:\n\n.. code:: python \n\n class M2M(models.Model):\n pass\n\n class Main(models.Model):\n\n m2m = models.ManyToManyField(M2M)\n\n\nDjango to Protobuf\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nIf this is not the format you expected, overwrite ``_m2m_to_protobuf()`` of Django model by yourself.\n\n\nProtobuf to Django\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nSame as previous section, we assume m2m field is repeated value in protobuf.\nBy default, **NO** operation is performed, which means\nyou may query current relation if your converted django model instance has a valid primary key.\n\nIf you want to modify your database while converting on-the-fly, overwrite\nlogics such as:\n\n.. code:: python\n\n from django.db import transaction\n\n ...\n\n class PBCompatibleModel(ProtoBufMixin, models.Model):\n\n def _repeated_to_m2m(self, dj_field, _pb_repeated_set):\n with transaction.atomic():\n for item in _pb_repeated_set:\n dj_field.get_or_create(pk=item.pk, defaults={....})\n\n ...\n\nAlso, you should write your converting policy if m2m is not nested repeated message in ``_repeated_to_m2m`` method\n\nDatetime Field\n~~~~~~~~~~~~~~\n\nDatetime is a special singular value.\n\nWe currently convert between ``datetime.datetime`` (Python) and ``google.protobuf.timestamp_pb2.Timestamp`` (ProboBuf),\nfor example:\n\nProtoBuf message:\n\n.. code:: protobuf\n\n package models;\n\n import \"google/protobuf/timestamp.proto\";\n\n message WithDatetime {\n int32 id = 1;\n google.protobuf.Timestamp datetime_field = 2;\n }\n\nDjango Model:\n\n.. code:: python\n\n class WithDatetime(ProtoBufMixin, models.Model):\n pb_model = models_pb2.WithDatetime\n\n datetime_field = models.DatetimeField(default=timezone.now())\n\n\n.. code:: python\n\n >>> WithDatetime.objects.create().to_pb()\n datetime_field {\n seconds: 1495119614\n nanos: 282705000\n }\n\n\nCustom Fields\n~~~~~~~~~~~~~\n\nYou can write your own field serializers, to convert between ``django.contrib.postgres.fields.JSONField`` (Python)\nand `string` (Protobuf) for example:\n\nProtoBuf message:\n\n.. code:: protobuf\n\n package models;\n\n message WithJSONBlob {\n int32 id = 1;\n string json_blob = 2;\n }\n\nDjango Model:\n\n.. code:: python\n\n def json_serializer(pb_obj, pb_field, dj_value):\n setattr(pb_obj, pb_field.name, json.dumps(value))\n\n def json_deserializer(instance, dj_field_name, pb_field, pb_value):\n setattr(instance, dj_field_name, json.loads(pb_value))\n\n class WithJSONField(ProtoBufMixin, models.Model):\n pb_model = models_pb2.WithJSONBlob\n\n pb_2_dj_field_serializers = {\n 'JSONField': (json_serializer, json_deserializer),\n }\n\n json_field = models.JSONField()\n\n\nTimezone\n\"\"\"\"\"\"\"\"\n\nNote that if you use ``USE_TZ`` in Django settings, all datetime would be converted to UTC timezone while storing in protobuf message.\nAnd coverted to default timezone in django according to settings.\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/myyang/django-pb-model", "keywords": "", "license": "MIT License", "maintainer": "", "maintainer_email": "", "name": "django-pb-model", "package_url": "https://pypi.org/project/django-pb-model/", "platform": "", "project_url": "https://pypi.org/project/django-pb-model/", "project_urls": { "Homepage": "https://github.com/myyang/django-pb-model" }, "release_url": "https://pypi.org/project/django-pb-model/0.1.8/", "requires_dist": [ "django (<=2.2,>=1.11)", "protobuf (>=3.1)" ], "requires_python": "", "summary": "Protobuf mixin for Django model", "version": "0.1.8" }, "last_serial": 5275165, "releases": { "0.1.2": [ { "comment_text": "", "digests": { "md5": "c4552c878072c198b19329c82a8b5877", "sha256": "8c217ac27575c03843546f2aae1f224f5bce40e447817162bcc8fa3b3b73e47e" }, "downloads": -1, "filename": "django-pb-model-0.1.2.tar.gz", "has_sig": false, "md5_digest": "c4552c878072c198b19329c82a8b5877", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11700, "upload_time": "2017-05-18T13:58:58", "url": "https://files.pythonhosted.org/packages/d2/f8/56b440a185802f9133abe7c1d0fcb70bd6172f3d9721da2be7bd4f026b3f/django-pb-model-0.1.2.tar.gz" } ], "0.1.3": [ { "comment_text": "", "digests": { "md5": "3333975e177de3879592cdf0ead87535", "sha256": "8fd188672d08e929567af49c3cbe868c6c701ea59555bb5f4ff4f9def901a253" }, "downloads": -1, "filename": "django-pb-model-0.1.3.tar.gz", "has_sig": false, "md5_digest": "3333975e177de3879592cdf0ead87535", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12584, "upload_time": "2017-05-20T16:19:15", "url": "https://files.pythonhosted.org/packages/d2/de/8bb984ef529cd96ec794f9fef4df76d41dcbdb1c4af6a649f6fbe3c1ed0b/django-pb-model-0.1.3.tar.gz" } ], "0.1.4": [ { "comment_text": "", "digests": { "md5": "5a7d583e3ed361c0a1eda2f50880d363", "sha256": "7364c6d32b2c1acdaadc5b57b893c7cdb786acc0af5d6ad54d9f2fd5ec201ac9" }, "downloads": -1, "filename": "django_pb_model-0.1.4-py3-none-any.whl", "has_sig": false, "md5_digest": "5a7d583e3ed361c0a1eda2f50880d363", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 15907, "upload_time": "2017-11-28T14:57:12", "url": "https://files.pythonhosted.org/packages/00/3e/d129e67f484b28b7e7c777479a7b348322c09b1bf567983cd4cb3a1f7779/django_pb_model-0.1.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f0a6ba47af7869df0304169fbf0aba2f", "sha256": "8302c6c8f8877c4a3d0339b8062690dd305f0690279c9a71dc6691409da8538e" }, "downloads": -1, "filename": "django-pb-model-0.1.4.tar.gz", "has_sig": false, "md5_digest": "f0a6ba47af7869df0304169fbf0aba2f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13840, "upload_time": "2017-11-28T14:57:14", "url": "https://files.pythonhosted.org/packages/1e/8f/2727870f4af20720cb88663eb7bc594591339e47b4cf45a8c0e6a0002d3b/django-pb-model-0.1.4.tar.gz" } ], "0.1.5": [ { "comment_text": "", "digests": { "md5": "194e00cba326778f6d5ad3f5ca1f9008", "sha256": "5d398de24b38c523f66fd1a66a2f3a49e7f10cd7e04b8d50cf11f77dd95f4ffb" }, "downloads": -1, "filename": "django-pb-model-0.1.5.tar.gz", "has_sig": false, "md5_digest": "194e00cba326778f6d5ad3f5ca1f9008", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13786, "upload_time": "2018-06-07T02:39:06", "url": "https://files.pythonhosted.org/packages/bd/fe/91d5a69c9af88cf04a704f7a493d8ef0ed5da0d905cae4ca41daff2baa76/django-pb-model-0.1.5.tar.gz" } ], "0.1.6": [ { "comment_text": "", "digests": { "md5": "19ce2641fe113d074a0bf4ac921917d7", "sha256": "0e60e5511e35e659e7cf5da1a90c07587ee4318fca1153850a77ebd85c221d06" }, "downloads": -1, "filename": "django_pb_model-0.1.6-py3-none-any.whl", "has_sig": false, "md5_digest": "19ce2641fe113d074a0bf4ac921917d7", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 13097, "upload_time": "2019-04-23T09:06:01", "url": "https://files.pythonhosted.org/packages/03/8d/950c2f76bb69e2ec448459de16019467f7441e4d0134f91eb47dbcf5d883/django_pb_model-0.1.6-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "586236073aa66f75c33cf5e40fd27544", "sha256": "632d3bbeff0bac70499c5b8b7d3dee3cab5f798d88d21ed9ea000f134fa9838b" }, "downloads": -1, "filename": "django-pb-model-0.1.6.tar.gz", "has_sig": false, "md5_digest": "586236073aa66f75c33cf5e40fd27544", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13745, "upload_time": "2018-06-22T08:16:41", "url": "https://files.pythonhosted.org/packages/36/cd/2d419ea4e825f6a3f7f71907b1c438712f0bc3f55efd3b9dcd81a39cc500/django-pb-model-0.1.6.tar.gz" } ], "0.1.7": [ { "comment_text": "", "digests": { "md5": "462390aee276e8ae57a679382fc5aff3", "sha256": "042dbcf9b1e2ad7c916667e35e787ee0fd39987ec2ffbface8509400e7793914" }, "downloads": -1, "filename": "django_pb_model-0.1.7-py3-none-any.whl", "has_sig": false, "md5_digest": "462390aee276e8ae57a679382fc5aff3", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 13089, "upload_time": "2019-04-23T09:12:56", "url": "https://files.pythonhosted.org/packages/23/0b/85dddb8e04e020fadcd1cda35249c323d874817ae2192202731510f55d89/django_pb_model-0.1.7-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "4b0023013122a477ba12e2809b073e95", "sha256": "47dc4153136200e53ddde856a23210f5320866f8eff49f5d54e517715d1282f1" }, "downloads": -1, "filename": "django-pb-model-0.1.7.tar.gz", "has_sig": false, "md5_digest": "4b0023013122a477ba12e2809b073e95", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11234, "upload_time": "2019-04-23T09:12:58", "url": "https://files.pythonhosted.org/packages/2d/4e/dcfbe570dcd268d184deed0a7168a83c64f84634aa5a972579b99e3a0007/django-pb-model-0.1.7.tar.gz" } ], "0.1.8": [ { "comment_text": "", "digests": { "md5": "df487c6cc6cdb5a810756d8785f78386", "sha256": "5f58ef8915bbe901fe053b5738e3e5fcac84fd4c836c0144231a041e580d8ccd" }, "downloads": -1, "filename": "django_pb_model-0.1.8-py3-none-any.whl", "has_sig": false, "md5_digest": "df487c6cc6cdb5a810756d8785f78386", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 19563, "upload_time": "2019-05-16T03:13:50", "url": "https://files.pythonhosted.org/packages/72/78/19660e66c58c83739a9a5ce672a7760ba5cc88224e50305f54ae379ccc14/django_pb_model-0.1.8-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "6ddfe62358bc215edc660dcf2e565e96", "sha256": "52a306cb885c30b7f16993c0befa651740aedb81089d4d0c98a62c34e0828764" }, "downloads": -1, "filename": "django-pb-model-0.1.8.tar.gz", "has_sig": false, "md5_digest": "6ddfe62358bc215edc660dcf2e565e96", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17077, "upload_time": "2019-05-16T03:13:52", "url": "https://files.pythonhosted.org/packages/b8/38/eee803d9cabe3c50731a9709a20fbfc1018e40c7a750571478dfe8a259ee/django-pb-model-0.1.8.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "df487c6cc6cdb5a810756d8785f78386", "sha256": "5f58ef8915bbe901fe053b5738e3e5fcac84fd4c836c0144231a041e580d8ccd" }, "downloads": -1, "filename": "django_pb_model-0.1.8-py3-none-any.whl", "has_sig": false, "md5_digest": "df487c6cc6cdb5a810756d8785f78386", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 19563, "upload_time": "2019-05-16T03:13:50", "url": "https://files.pythonhosted.org/packages/72/78/19660e66c58c83739a9a5ce672a7760ba5cc88224e50305f54ae379ccc14/django_pb_model-0.1.8-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "6ddfe62358bc215edc660dcf2e565e96", "sha256": "52a306cb885c30b7f16993c0befa651740aedb81089d4d0c98a62c34e0828764" }, "downloads": -1, "filename": "django-pb-model-0.1.8.tar.gz", "has_sig": false, "md5_digest": "6ddfe62358bc215edc660dcf2e565e96", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17077, "upload_time": "2019-05-16T03:13:52", "url": "https://files.pythonhosted.org/packages/b8/38/eee803d9cabe3c50731a9709a20fbfc1018e40c7a750571478dfe8a259ee/django-pb-model-0.1.8.tar.gz" } ] }