{ "info": { "author": "Maciej G\u0119barski", "author_email": "mgebarski@gmail.com", "bugtrack_url": null, "classifiers": [ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6" ], "description": "## Matchbox\n\n\n\n
\n\n| Details | Matchbox is orm package for Google Firestore. |\n| ------- | ------ |\n| Repository | https://github.com/gameboy86/matchbox |\n| Author | Maciej G\u0119barski (https://github.com/gameboy86) |\n| Contact | mgebarski@gmail.com |\n| License | MIT License |\n| Version | 0.2.4 |\n## Details\n\n`Matchbox` is a Python Object-Relational Mapper for Google Firestore.\nIt is in development.\n\n\n### Installing\n\n```bash\n pip install matchbox-orm\n ```\n\n## Usage\n\n\n### Connect to Firestore\n\nMore info, how to generate JSON file with private key you will find on [Get started with Cloud Firestore](https://firebase.google.com/docs/firestore/quickstart)\n\n```python\nfrom matchbox import database\n\ndatabase.db_initialization('path/to/serviceAccount.json')\n```\n\n### Model\n\n#### Create\n\n```python\nfrom matchbox import models\n\nclass Test(models.Model):\n age = models.IntegerField()\n name = models.TextField()\n\n def __unicode__(self):\n return self.id\n```\n\n```python\n>> t = Test()\n>> print(t)\n\n```\n\nBy default all fields are required (except `IDField`, `ReferenceField`). \nThis behavior can be change using attributes `blank` or `default`. \n\nIf we now save model we get:\n\n```python\n>> t.save()\nAttributeError: Field age required value\n>> t.age = 18\n>> t.save()\nAttributeError: Field name required value\n>> t.name = 'Name'\n>> t.save()\n```\n\nAnother way to create model is use manager `create` method:\n\n```python\n>> Test.objects.create(name='Test', age=29)\n\n```\n\nBy default collection name in DB will be create based on model name. If you \nwant to change it, you can do it using Meta. For example:\n\n```python\nfrom matchbox import models\n\nclass Test(models.Model):\n age = models.IntegerField()\n name = models.TextField()\n \n class Meta:\n collection_name = 'TestCollection'\n \n def __unicode__(self):\n return self.id\n```\n\n```python\n>> Test._meta.collection_name\n'TestCollection'\n```\n\n#### Update\n\n\nDocument can be update by two ways: override or update. \nExample below will override whole document:\n\n```python\n>> t = Test.objects.get(id='eba5fd53244e38aa1b4951f104ec3c')\n>> t.age = 53\n>> t.save()\n```\n\nIf we want update only specific fields, we can use `update_fields` parameter in\n`save` method:\n\n```python\n>> t = Test.objects.get(id='eba5fd53244e38aa1b4951f104ec3c')\n>> t.age = 32\n>> t.save(update_fields=['age'])\n```\n\n#### Fields\n\n\n**Available fields:**\n * IDField\n * IntegerField\n * TextField\n * TimeStampField\n * BooleanField\n * ListField\n * MapField\n * GeoPointField\n * ReferenceField\n\n#### Attributes\n\n\n**Available attributes for all fields:**\n * blank (If True empty fields will save null in DB.)\n * default (If field is empty, on the save, default value will be used)\n * column_name (Name of field in DB. If empty, name of field will be used)\n \n\n`TextField` accept on more attribute `max-length`.\n\n```python\nclass Test2(models.Model):\n age = models.IntegerField(default=25)\n name = models.TextField(blank=True)\n```\n\n```python\n>> t = Test2()\n>> t.save()\n>> t = Test2.objects.get(id=t.id)\n>> print(t.age, t.name)\n25 None\n```\n\n#### IDField\n\n`IDField` is create automatically by orm. We `can't` add own, because Firestore doesn't \nallow for self named id field.\n\n```python\n>> t._meta.fields\n{\n 'age': ,\n 'name': ,\n 'id': \n}\n```\n\nIf you want you can specify your own id:\n\n```python\n>> t = Test(age=33, name='test', id='My OWN ID')\n>> t.save()\n>> t.id\n'My OWN ID'\n```\n\nIf you change id and save, new document will be create in Firestore.\n\n\n#### TimeStampField\n\n```python\nclass TimeStampFieldExample(models.Model):\n datetimestamp = models.TimeStampField()\n \n def __unicode__(self):\n return self.id\n```\n```python\n>> TimeStampFieldExample.objects.create(datetimestamp=datetime.datetime.now())\n\n\n>> list(TimeStampFieldExample.objects.filter(datetimestamp__lte=datetime.datetime.now()))\n[]\n\n>> TimeStampFieldExample.objects.filter(datetimestamp__lte=datetime.datetime.now()).get().datetimestamp\ndatetime.datetime(2019, 5, 4, 16, 42, 34, 583953, tzinfo=datetime.timezone(datetime.timedelta(0), '+00:00'))\n```\n\n#### ListField\n\n```python\nclass ListFieldExample(models.Model):\n list_f = models.ListField()\n\n def __unicode__(self):\n return self.id\n```\n\n```python\n>> ListFieldExample.objects.create(list_f=[1, 2, 3, 4, 5])\n>> list(ListFieldExample.objects.filter(list_f__contains=5))\n[]\n\n>> ListFieldExample.objects.filter(list_f__contains=5).get().list_f\n[1, 2, 3, 4, 5]\n```\n\n#### MapField\n\n```python\nclass MapFieldExample(models.Model):\n map_f = models.MapField()\n\n def __unicode__(self):\n return self.id\n```\n\n```python\n>> MapFieldExample.objects.create(map_f = {'a': 1, 'b': 2, 'c': {'a': 1}})\n\n\n>> list(MapFieldExample.objects.filter(map_f__c__a=1))\n[]\n\n>> list(MapFieldExample.objects.filter(map_f__c__a=1))[0].map_f\n{'b': 2, 'c': {'a': 1}, 'a': 1}\n```\n\n#### GeoPointField\n\n\nTo save GeoPoint data you must use class `GeoPointValue`\n\n```python\nclass GeoPointFieldExample(models.Model):\n geo_point_f = models.GeoPointField()\n \n def __unicode__(self):\n return self.id\n```\n\n```python \n>> gpf = GeoPointFieldExample()\n>> gpf.geo_point_f = GeoPointValue(latitude=52.2297, longitude=21.0122)\n>> gpf.save()\n\n>> list(GeoPointFieldExample.objects.all())[0].geo_point_f\n\n\n>> list(GeoPointFieldExample.objects.all())[0].geo_point_f.latitude\n52.2297\n```\n\n#### ReferenceField\n\n\nOne of field offered by FireStore is Reference. In one document you can store\nreference to another document.\n\n```python\n\nclass User(models.Model):\n name = models.TextField()\n \n def __unicode__(self):\n return self.id\n\nclass Class(models.Model):\n name = models.TextField()\n user = models.ReferenceField(User)\n \n def __unicode__(self):\n return self.id\n```\n\n```python\n>> u = User.objects.create(name='Alex')\n>> c = Class.objects.create(name='A1', user=u)\n>> c.user\n\n\n>> c.user.id, c.user.name\n('cdda43cf3d65413f9eea17349e8222b8', 'Alex')\n\n```\n\n#### Query\n\n\n##### objects.get\n\n```python\n class User(models.Model):\n name = models.TextField()\n \n def __unicode__(self):\n return self.id\n```\n\n```python\n>> u = User.objects.create(name='Alex')\n>> User.objects.get(id=u.id)\n\n```\n\n\n##### objects.all\n\n\nReturn all documents in collection\n\n```python\nclass User(models.Model):\n name = models.TextField()\n \n def __unicode__(self):\n return self.id\n\nclass Class(models.Model):\n name = models.TextField()\n user = models.ReferenceField(User)\n \n def __unicode__(self):\n return self.id\n```\n\n```python\n>> User.objects.create(name='Tom')\n>> User.objects.create(name='Alex')\n>> User.objects.create(name='Michael')\n>> User.objects.all()\n\n\n>> list(User.objects.all())\n[,\n,\n]\n```\n\n##### objects.filter\n\nFilter is based on django filter method. FireStore allow following comparison, \nwith are mapped to:\n\n| FireStore | Matchbox |\n| -------- | -------- |\n| `<` | lt |\n| `<=` | lte |\n| `>` | gt |\n| `>=` | gte |\n| `==` | not need |\n| `array_contains` | contains|\n\n\n```python\nclass User(models.Model):\n name = models.TextField()\n evaluations = models.ListField()\n age = models.IntegerField(default=20)\n \n def __unicode__(self):\n return self.id\n\n```\n\n```python\n>> User.objects.create(name='Tom', evaluations=[1,1,2], age=15)\n>> User.objects.create(name='Michael', evaluations=[2,3,5])\n>> User.objects.create(name='Michael', evaluations=[4,4,2])\n>> User.objects.filter()\n[,\n,\n]\n\n>> User.objects.filter(age__gte=10, age__lte=15)\n[]\n\n>> u = User.objects.filter(age__gte=10, age__lte=15).get()\n>> print(u.age)\n15\n\n>> list(User.objects.filter(name='Michael'))\n[,\n]\n\n>> list(User.objects.filter(name='Michael').filter(evaluations=[4,4,2])) # or list(User.objects.filter(name='Michael', evaluations=[4,4,2]))\n[]\n\n>> u = User.objects.filter(name='Michael', evaluations=[4,4,2]).get()\n>> print(u.id, u.age, u.name, u.evaluations)\n2dce37628c4345b0a9d1a721265984b4 20 Michael [4, 4, 2]\n\n>> list(User.objects.filter(evaluations__contains=3))\n[]\n\n>> u = User.objects.filter(evaluations__contains=3).get()\n>> u.id, u.name, u.evaluations\n('389ac1ca88614d5fa5e53facb1249576', 'Michael', [2, 3, 5])\n```\n\nYou can also filter by ReferenceField\n\n```python\nclass Class(models.Model):\n name = models.TextField()\n user = models.ReferenceField(User)\n \n def __unicode__(self):\n return self.id\n```\n\n```python\n>> c = Class.objects.create(name='A1', user=User.objects.all().get())\n>> c.user.id, c.user.name\n'2dce37628c4345b0a9d1a721265984b4', 'Michael'\n\n>> Class.objects.filter(user=u).get()\n\n```\n\n`order_by` and `limit`\n\n```python\n\n>> [(u.age, u.name) for u in User.objects.all()]\n[(20, 'Michael'), (15, 'Tom'), (20, 'Michael')]\n\n>> [(u.age, u.name) for u in User.objects.all().order_by('age')]\n[(15, 'Tom'), (20, 'Michael'), (20, 'Michael')]\n\n>> [(u.age, u.name) for u in User.objects.all().order_by('-age')]\n[(20, 'Michael'), (20, 'Michael'), (15, 'Tom')]\n\n>> [(u.age, u.name) for u in User.objects.all().order_by('-age').limit(2)]\n[(20, 'Michael'), (20, 'Michael')]\n```\n\n#### Delete\n\n\nWe can delete document by instance or by filter.\n\n```python\n>> u = User.objects.all().get()\n>> u.delete()\n\n>> User.objects.filter(name='Alex').delete()\n```\n\nDelete whole collection:\n```python\n>> User.objects.delete()\nor\n>> User.objects.filter().delete()\n```\n\n\n#### Managers\n\n\nLike in Django we can create own `Managers`. For example:\n\n```python\n\nclass User(models.Model):\n name = models.TextField()\n evaluations = models.ListField()\n age = models.IntegerField(default=20)\n\n def __unicode__(self):\n return self.id\n\nclass AManager(models.Manager):\n def get_queryset(self):\n return super().get_queryset().filter(active=True)\n\n\nclass DManager(models.Manager):\n def get_queryset(self):\n return super().get_queryset().filter(active=False)\n\n\nclass Class(models.Model):\n name = models.TextField()\n user = models.ReferenceField(User)\n active = models.BooleanField()\n \n a_objects = AManager()\n f_objects = DManager()\n \n def __unicode__(self):\n return self.id\n```\n\n```python\n>> c1 = Class.objects.create(active=True, name='DD21')\n>> c2 = Class.objects.create(active=True, name='DD22')\n>> c3 = Class.objects.create(active=False, name='CC22')\n>> c4 = Class.objects.create(active=False, name='CC11')\n>> list(Class.objects.all())\n[,\n ,\n ,\n ]\n\n>> list(Class.f_objects.all())\n[, ]\n\n>> list(Class.a_objects.all())\n[, ]\n```\n\n#### Abstract model\n\nAbstract model useful when you want to put some common information into a number of other models.\nYou must create base class and add abstract = True in the Meta model class.\n\nFor example:\n\n```python\nfrom matchbox import models as fsm, database\n\ndatabase.db_initialization('xxx.json')\n\n\nclass SuffixFsm(fsm.Model):\n createdAt = fsm.TimeStampField()\n createdBy = fsm.TextField(max_length=30, default='')\n\n class Meta:\n abstract = True\n\n\nclass SystemMaster(SuffixFsm):\n systemName = fsm.TextField(max_length=50, default='')\n\n```\n\n```python\n>> master = SystemMaster(\n systemName='name',\n createdAt=datetime.now(),\n createdBy='test',\n )\n>> master.save()\n>> master.__dict__\n\n{'id': '9ZCOPU8KRwUB4rRVF1kZ',\n 'systemName': 'name',\n 'createdAt': datetime.datetime(2019, 7, 4, 21, 36, 56, 472744),\n 'createdBy': 'test'}\n\n\n```\n\n\n### SubCollections\n\nLet say we want store structure like below in firestore\n\n```\n (C) rooms \n (D) roomA \n name : \"my chat room\"\n (C) messages \n (D) message1\n from : \"alex\"\n msg : \"Hello World!\"\n\n (C) message2\n ...\n\n (D) roomB\n ...\n\n(C) -> Collection\n(D) -> Document\n```\n\n\n\n```python\nfrom matchbox import models, database\ndatabase.db_initialization('xxx.json')\n\nclass Message(models.Model):\n by = models.TextField()\n msg = models.TextField()\n\n class Meta:\n collection_name = 'messages'\n\n\nclass Room(models.Model):\n name = models.TextField()\n\n class Meta:\n collection_name = 'rooms'\n\n```\n\nTo create subcollection in document, we must set path in model to document using set_base_path.\n\nModelClass.set_base_path(model_instance) -> model_instance must be stored in firestore before passed to method.\n\n```python\n>> r = Room.objects.create(name='roomA')\n>> Message.set_base_path(r)\n>> # Wrong Room(name='roomA'); Message.set_base_path(r) \n>> Message.objects.create(by='Alex', msg='Hello')\n>> Message.objects.create(by='Alex', msg='How are you ?') \n\n>> r = Room.objects.create(name='roomB')\n>> Message.set_base_path(r)\n>> Message.objects.create(by='Neo', msg='Matrix ?')\n>> Message.objects.create(by='Matrix', msg='Follow the white rabbit')\n\n```\n\n`IMPORTANT`: Default path is '/', so if you don't set path\nyour document will be created in root path. You always can restore default path\nusing ModelClass.reset_base_path().\n\nTo check path instance use model_path\n```python\n>> r = Room.objects.get(name='roomA') # r.id == 'K8imB6eui5ibfSEZon3e'\n>> print(r.model_path)\n('rooms', 'K8imB6eui5ibfSEZon3e')\n\n>> r = Room.objects.get(name='roomB') # r.id == '4QIk9Q5LCrkVz1bWir6w\n>> print(r.model_path)\n('rooms', '4QIk9Q5LCrkVz1bWir6w')\n\n>> Message.set_base_path(r)\n>> m = Message.objects.get(by='Neo') # m.id == 'YypGDFPi5M1NYeWqROSq'\n>> print(m.model_path)\n('rooms', '4QIk9Q5LCrkVz1bWir6w', 'messages', 'YypGDFPi5M1NYeWqROSq')\n\n```\n\nTo check model path use 'path' property\n```python\n>> print(Room.path) # ('rooms', ) \n```\n\n\nTo get all messages from 'roomB' filtered by 'by' field:\n```python\n>> r = Room.objects.get(name='roomB')\n>> Message.set_base_path(r)\n>> neo_messages = Message.objects.filter(by='Neo')\n>> print(len(list(neo_messages)))\n1\n\n>> matrix_messages = Message.objects.filter(by='Matrix')\n>> print(len(list(matrix_messages)))\n1\n```\n \nNow let say, we want to delete all messages in 'roomA':\n\n```python\n>> r = Room.objects.get(name='roomA')\n>> Message.set_base_path(r)\n>> print(len(list(Message.objects.all())))\n2\n\n>> Message.objects.delete()\n>> print(len(list(Message.objects.all())))\n0\n\n>> r = Room.objects.get(name='roomB')\n>> Message.set_base_path(r)\n>> print([x.msg for x in Message.objects.all()])\n['Follow the white rabbit', 'Matrix ?']\n```\n\n`IMPORTANT`: We can't delete room (Room.objects.get(name='roomA').delete()). If we\ndo this in this way, references in firestore to messages will still exist. So before deleting Collection, make sure you delete all subcollections independently from his documents.", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/gameboy86/matchbox", "keywords": "firebase orm firestore google cloud", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "matchbox-orm", "package_url": "https://pypi.org/project/matchbox-orm/", "platform": "", "project_url": "https://pypi.org/project/matchbox-orm/", "project_urls": { "Homepage": "https://github.com/gameboy86/matchbox" }, "release_url": "https://pypi.org/project/matchbox-orm/0.2.5/", "requires_dist": null, "requires_python": "", "summary": "matchbox is orm package for google Cloud Firestore", "version": "0.2.5" }, "last_serial": 5965393, "releases": { "0.1": [ { "comment_text": "", "digests": { "md5": "8f4be2a6c7c150bf667ba2c752cd1473", "sha256": "46578c055ae1c2896dbf90a7b10ca924bf2988ea8de0e82fea1643c4b7c2ff8e" }, "downloads": -1, "filename": "matchbox_orm-0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "8f4be2a6c7c150bf667ba2c752cd1473", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 13948, "upload_time": "2019-05-08T21:55:07", "url": "https://files.pythonhosted.org/packages/55/38/064abdd0a9c6d55a610f4cf0b0d611d8af3b4b1afc16d24a03af543691a5/matchbox_orm-0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "183e696cfca98e5dbebd4b85325b9ac8", "sha256": "b573089792063e0841db09dada01ae28fa1ef8ec5223c52be80c721fe5478657" }, "downloads": -1, "filename": "matchbox-orm-0.1.tar.gz", "has_sig": false, "md5_digest": "183e696cfca98e5dbebd4b85325b9ac8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10598, "upload_time": "2019-05-08T21:55:09", "url": "https://files.pythonhosted.org/packages/99/c0/28ddd9bc86f84aaf03466e42cab5176a690b309a46c2259e24c7bb2e0b44/matchbox-orm-0.1.tar.gz" } ], "0.2": [ { "comment_text": "", "digests": { "md5": "aa7842a29b95282a68e0d7dfdb3308e5", "sha256": "a995246416ccf8bbba7ab44e6a807b05d50f2b6908c0074c96852e4f2f0b7311" }, "downloads": -1, "filename": "matchbox-orm-0.2.tar.gz", "has_sig": false, "md5_digest": "aa7842a29b95282a68e0d7dfdb3308e5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11219, "upload_time": "2019-07-04T20:38:04", "url": "https://files.pythonhosted.org/packages/1f/83/a4f1722869663b95584af22cb020d29da9b053a56df1f6156b104da432cc/matchbox-orm-0.2.tar.gz" } ], "0.2.1": [ { "comment_text": "", "digests": { "md5": "5d9304136c27bd4e0f094116bdca60e2", "sha256": "d574000d7f972b88c337f04f47cabdc831235f06cf1ca51790061276a365c2fb" }, "downloads": -1, "filename": "matchbox_orm-0.2.1-py3-none-any.whl", "has_sig": false, "md5_digest": "5d9304136c27bd4e0f094116bdca60e2", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 14947, "upload_time": "2019-08-17T21:07:01", "url": "https://files.pythonhosted.org/packages/39/de/6e21ca1068d9e375f2ccec7c1bb59ab7d62b786a34bda3323cfc09169a16/matchbox_orm-0.2.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "85b1f298dfc29cba39edcdee108c6e8a", "sha256": "4031ae0304a7344407d2b417a44eb2ed137c335a9975e83b14b199860f83be8b" }, "downloads": -1, "filename": "matchbox-orm-0.2.1.tar.gz", "has_sig": false, "md5_digest": "85b1f298dfc29cba39edcdee108c6e8a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11525, "upload_time": "2019-08-17T21:07:04", "url": "https://files.pythonhosted.org/packages/dc/50/31fd81a3957963d18829687cf61da3828218bc42f8c9a82a697ee6103adb/matchbox-orm-0.2.1.tar.gz" } ], "0.2.2": [ { "comment_text": "", "digests": { "md5": "c3749f72df64e49ce8321e6325b5465f", "sha256": "cdf4cb8de6d4e432a80caa45129bd4e325f8c14244079a8aa14542c98864f32a" }, "downloads": -1, "filename": "matchbox_orm-0.2.2-py3-none-any.whl", "has_sig": false, "md5_digest": "c3749f72df64e49ce8321e6325b5465f", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 16896, "upload_time": "2019-09-22T13:37:28", "url": "https://files.pythonhosted.org/packages/81/75/ef9e226e8b6dd20e87bdeb2e66372f647e06fbfe8fe570ffddb3c10997cf/matchbox_orm-0.2.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "b6b7de6179a008a2cbd5c1e5a463c31c", "sha256": "bbbe431ecc46ec7e4b3671e6839ce8a1769509970d57e52ee05d19f1d9fcc336" }, "downloads": -1, "filename": "matchbox-orm-0.2.2.tar.gz", "has_sig": false, "md5_digest": "b6b7de6179a008a2cbd5c1e5a463c31c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12710, "upload_time": "2019-09-22T13:37:32", "url": "https://files.pythonhosted.org/packages/ab/02/a0ec25900eb71346edd2d92a601a92e3d38fca0d348478a225f0814b60ec/matchbox-orm-0.2.2.tar.gz" } ], "0.2.3": [ { "comment_text": "", "digests": { "md5": "ec930d4d39441d0add04e01ce12977bf", "sha256": "94f4b96f28d3bb3f5b20aefb9762f5ef5fc25df7d86f9ffb6b7dfb66a27bd357" }, "downloads": -1, "filename": "matchbox_orm-0.2.3-py3-none-any.whl", "has_sig": false, "md5_digest": "ec930d4d39441d0add04e01ce12977bf", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 17342, "upload_time": "2019-09-27T10:06:45", "url": "https://files.pythonhosted.org/packages/27/aa/179734a39361a9ebcb7af451d4331b6d59dc2e450347b7c6a68aad01218e/matchbox_orm-0.2.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "81d8929ba723d45e7543be97c0e3725e", "sha256": "66d10e9d2f1ad299bec63908e139dff696561d9bf1c0796b58781dab4b75cf02" }, "downloads": -1, "filename": "matchbox-orm-0.2.3.tar.gz", "has_sig": false, "md5_digest": "81d8929ba723d45e7543be97c0e3725e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13092, "upload_time": "2019-09-27T10:06:49", "url": "https://files.pythonhosted.org/packages/bd/e2/cbe93660be7a8857a59e5e8a3c43189ad901fea3522d7ad53f337a28fa53/matchbox-orm-0.2.3.tar.gz" } ], "0.2.4": [ { "comment_text": "", "digests": { "md5": "02d078ce2207c35099475540422a302e", "sha256": "83b227008a139f251701af5607a0b343a3b219bfb5aeb2509f8f8bf014cec64a" }, "downloads": -1, "filename": "matchbox-orm-0.2.4.tar.gz", "has_sig": false, "md5_digest": "02d078ce2207c35099475540422a302e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14730, "upload_time": "2019-09-28T14:32:58", "url": "https://files.pythonhosted.org/packages/db/91/d8610de308a610c99a15e23a59ab9aa9119dc43f1511d97635bf923ae3a7/matchbox-orm-0.2.4.tar.gz" } ], "0.2.5": [ { "comment_text": "", "digests": { "md5": "2eaa8e348dbe08c9327f561b2c8ca8e2", "sha256": "1c3251888bfb6ca562b39a1d1d705d6740ad6345a733b7c4c1ce6ec001564e10" }, "downloads": -1, "filename": "matchbox-orm-0.2.5.tar.gz", "has_sig": false, "md5_digest": "2eaa8e348dbe08c9327f561b2c8ca8e2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14933, "upload_time": "2019-10-12T21:11:47", "url": "https://files.pythonhosted.org/packages/f3/10/fe35eeb2c8c57909bcec755b5028332839e02f218ebfa8dca3af8a46739f/matchbox-orm-0.2.5.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "2eaa8e348dbe08c9327f561b2c8ca8e2", "sha256": "1c3251888bfb6ca562b39a1d1d705d6740ad6345a733b7c4c1ce6ec001564e10" }, "downloads": -1, "filename": "matchbox-orm-0.2.5.tar.gz", "has_sig": false, "md5_digest": "2eaa8e348dbe08c9327f561b2c8ca8e2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14933, "upload_time": "2019-10-12T21:11:47", "url": "https://files.pythonhosted.org/packages/f3/10/fe35eeb2c8c57909bcec755b5028332839e02f218ebfa8dca3af8a46739f/matchbox-orm-0.2.5.tar.gz" } ] }