{ "info": { "author": "S. Andrew Sheppard", "author_email": "andrew@wq.io", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Framework :: Django", "Framework :: Django :: 1.11", "Framework :: Django :: 1.8", "Framework :: Django :: 2.0", "Framework :: Django :: 2.1", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Database" ], "description": "# Django Natural Keys\n\nEnhanced support for [natural keys] in Django and [Django REST Framework]. Extracted from [wq.db] for general use.\n\n*Django Natural Keys* provides a number of useful model methods (e.g. `get_or_create_by_natural_key()`) that speed up working with natural keys in Django. The module also provides a couple of serializer classes that streamline creating REST API support for models with natural keys.\n\n[](https://pypi.org/project/natural-keys/)\n[](https://github.com/wq/django-natural-keys/releases)\n[](https://github.com/wq/django-natural-keys/blob/master/LICENSE)\n[](https://github.com/wq/django-natural-keys/stargazers)\n[](https://github.com/wq/django-natural-keys/network)\n[](https://github.com/wq/django-natural-keys/issues)\n\n[](https://travis-ci.org/wq/django-natural-keys)\n[](https://pypi.org/project/natural-keys/)\n[](https://pypi.org/project/natural-keys/)\n\n\n## Usage\n\n*Django Natural Keys* is available via PyPI:\n\n```bash\n# Recommended: create virtual environment\n# python3 -m venv venv\n# . venv/bin/activate\npip install natural-keys\n```\n\n### Model API\n\nTo use [natural keys] in vanilla Django, you need to define a `natural_key()` method on your Model class and a `get_natural_key()` method on the Manager class. With *Django Natural Keys*, you can instead extend `NaturalKeyModel` and define a `unique_together` property on your Model's `Meta` class or use a field with `unique=True`. The first `unique_together` entry or the first `unique` field (except an AutoField) will be treated as the natural key for the model, and all of the necessary functions for working with natural keys will automatically work.\n\n```python\nfrom natural_keys import NaturalKeyModel\n\nclass Event(NaturalKeyModel):\n name = models.CharField(max_length=255)\n date = models.DateField()\n class Meta:\n unique_together = (('name','date'),)\n\nclass Note(models.Model):\n event = models.ForeignKey(Event)\n note = models.TextField()\n```\nor\n```python\nfrom natural_keys import NaturalKeyModel\n\nclass Event(NaturalKeyModel):\n name = models.CharField(unique=True)\n```\n\nThe following methods will then be available on your Model and its Manager:\n\n```python\n# Default Django methods\ninstance = Event.objects.get_by_natural_key('ABC123', date(2016, 1, 1))\ninstance.natural_key == ('ABC123', date(2016, 1, 1))\n\n# get_or_create + natural keys\ninstance, is_new = Event.objects.get_or_create_by_natural_key('ABC123', date(2016, 1, 1))\n\n# Like get_or_create_by_natural_key, but discards is_new\n# Useful for quick lookup/creation when you don't care whether the object exists already\ninstance = Event.objects.find('ABC123', date(2016, 1, 1))\nnote = Note.objects.create(\n event=Event.objects.find('ABC123', date(2016, 1, 1)),\n note=\"This is a note\"\n)\ninstance == note.event\n\n# Inspect natural key fields on a model without instantiating it\nEvent.get_natural_key_fields() == ('name', 'date')\n```\n\n#### Nested Natural Keys\nOne key feature of *Django Natural Keys* is that it will automatically traverse `ForeignKey`s to related models (which should also be `NaturalKeyModel` classes). This makes it possible to define complex, arbitrarily nested natural keys with minimal effort.\n\n```python\nclass Place(NaturalKeyModel):\n name = models.CharField(max_length=255, unique=True)\n\nclass Event(NaturalKeyModel):\n place = models.ForeignKey(Place)\n date = models.DateField()\n class Meta:\n unique_together = (('place', 'date'),)\n```\n\n```python\nEvent.get_natural_key_fields() == ('place__name', 'date')\ninstance = Event.find('ABC123', date(2016, 1, 1))\ninstance.place.name == 'ABC123'\n```\n\n### REST Framework Support\n*Django Natural Keys* provides several integrations with [Django REST Framework], primarily through custom Serializer classes. In most cases, you will want to use either:\n * `NaturalKeyModelSerializer`, or\n * The `natural_key_slug` pseudo-field (see below)\n\nIf you have only a single model with a single char field for its natural key, you probably do not need to use either of these integrations. In your view, you can just use Django REST Framework's built in `lookup_field` to point directly to your natural key.\n\n#### `NaturalKeyModelSerializer`\n`NaturalKeyModelSerializer` facilitates handling complex natural keys in your rest API. It can be used with a `NaturalKeyModel`, or (more commonly) a model that has a foreign key to a `NaturalKeyModel` but is not a `NaturalKeyModel` itself. (One concrete example of this is the [vera.Report] model, which has a ForeignKey to [vera.Event], which is a `NaturalKeyModel`).\n\n`NaturalKeyModelSerializer` extends DRF's [ModelSerializer], but uses `NaturalKeySerializer` for each foreign key that points to a `NaturalKeyModel`. When `update()` or `create()`ing the primary model, the nested `NaturalKeySerializer`s will automatically create instances of referenced models if they do not exist already (via the `find()` method described above). Note that `NaturalKeyModelSerializer` does not override DRF's default behavior for other fields, whether or not they form part of the primary model's natural key.\n\n`NaturalKeySerializer` can technically be used as a top level serializer, though this is not recommended. `NaturalKeySerializer` is designed for dealing with nested natural keys and does not support updates or non-natural key fields. Even when used together with `NaturalKeyModelSerializer`, `NaturalKeySerializer` never updates an existing related model instance. Instead, it will repoint the foreign key to another (potentially new) instance of the related model. It may help to think of `NaturalKeySerializer` as a special [RelatedField] class rather than as a `Serializer` per se.\n\n\nYou can use `NaturalKeyModelSerializer` with [Django REST Framework] and/or [wq.db] just like any other serializer:\n```python\n# Django REST Framework usage example\nfrom rest_framework import viewsets\nfrom rest_framework import routers\nfrom natural_keys import NaturalKeyModelSerializer\nfrom .models import Event, Note\n\nclass EventSerializer(NaturalKeyModelSerializer):\n class Meta:\n model = Event\n\nclass NoteSerializer(NaturalKeyModelSerializer):\n class Meta:\n model = Note\n\nclass EventViewSet(viewsets.ModelViewSet):\n queryset = Event.objects.all()\n serializer_class = EventSerializer\n\nclass NoteViewSet(viewsets.ModelViewSet):\n queryset = Note.objects.all()\n serializer_class = NoteSerializer\n\nrouter = routers.DefaultRouter()\nrouter.register(r'events', EventViewSet)\nrouter.register(r'notes', NoteViewSet)\n\n# wq.db usage example\nfrom wq.db import rest\nfrom natural_keys import NaturalKeyModelSerializer\nfrom .models import Event, Note\n\nrest.router.register_model(Note, serializer=NaturalKeyModelSerializer)\nrest.router.register_model(Event, serializer=NaturalKeyModelSerializer)\n```\n\nOnce this is set up, you can use your REST API to create and view your `NaturalKeyModel` instances and related data. To facilitate integration with regular HTML Forms, *Django Natural Keys* is integrated with the [HTML JSON Forms] package, which supports nested keys via an array naming convention, as the examples below demonstrate.\n\n```html\n
\n```\n```js\n// /events.json\n[\n {\n \"id\": 123,\n \"place\": {\"name\": \"ABC123\"},\n \"date\": \"2016-01-01\"\n }\n]\n```\n```html\n\n```\n```js\n// /notes.json\n[\n {\n \"id\": 12345,\n \"event\": {\n \"place\": {\"name\": \"ABC123\"},\n \"date\": \"2016-01-01\"\n },\n \"note\": \"This is a note\"\n }\n]\n```\n\n### Natural Key Slugs\nAs an alternative to using `NaturalKeyModelSerializer` / `NaturalKeySerializer`, you can also use a single slug-like field for lookup and serialization. `NaturalKeyModel` (and its associated queryset) defines a pseudo-field, `natural_key_slug`, for this purpose.\n\n```python\nclass Place(NaturalKeyModel):\n name = models.CharField(max_length=255, unique=True)\n\nclass Room(NaturalKeyModel)\n place = models.ForeignKey(Place, models.ON_DELETE)\n name = models.CharField(max_length=255)\n\n class Meta:\n unique_together = (('place', 'name'),)\n```\n```python\nroom = Room.objects.find(\"ABC123\", \"MainHall\")\nassert(room.natural_key_slug == \"ABC123-MainHall\")\nassert(room == Room.objects.get(natural_key_slug=\"ABC123-MainHall\"))\n```\n\nYou can expose this functionality in your REST API to expose natural keys instead of database-generated ids. To do this, you will likely want to do the following:\n\n 1. Create a regular serializer with `id = serializers.ReadOnlyField(source='natural_key_slug')`\n 2. Set `lookup_field = 'natural_key_slug'` on your `ModelViewSet` (or similar generic class) and update the URL registration accordingly\n 3. Ensure foreign keys on any related models are serialized with `serializers.SlugRelatedField(slug_field='natural_key_slug')`\n\nIn [wq.db], all three of the above can be achieved by setting the `\"lookup\"` attribute when registering with the [router]:\n\n```python\n# myapp/rest.py\nfrom wq.db import rest\nfrom .models import Room\n\nrest.router.register_model(\n Room,\n fields='__all__',\n lookup='natural_key_slug',\n)\n```\n\nNote that the `natural_key_slug` may not behave as expected if any of the component values contain the delimiter character (`-` by default). To mitigate this, you can set `natural_key_separator` on the model class to another character.\n\n[natural keys]: https://docs.djangoproject.com/en/2.0/topics/serialization/#natural-keys\n[wq.db]: https://wq.io/wq.db\n[Django REST Framework]: http://www.django-rest-framework.org/\n[vera.Report]:https://github.com/wq/vera#report\n[vera.Event]: https://github.com/wq/vera#event\n[ModelSerializer]: https://www.django-rest-framework.org/api-guide/serializers/#modelserializer\n[RelatedField]: https://www.django-rest-framework.org/api-guide/relations/\n[HTML JSON Forms]: https://github.com/wq/html-json-forms\n[router]: https://wq.io/docs/router\n\n\n", "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/wq/django-natural-keys", "keywords": "", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "natural-keys", "package_url": "https://pypi.org/project/natural-keys/", "platform": "", "project_url": "https://pypi.org/project/natural-keys/", "project_urls": { "Homepage": "https://github.com/wq/django-natural-keys" }, "release_url": "https://pypi.org/project/natural-keys/1.5.1/", "requires_dist": [ "html-json-forms (>=1.0.0)" ], "requires_python": "", "summary": "Enhanced support for natural keys in Django and Django REST Framework", "version": "1.5.1" }, "last_serial": 5768562, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "952a0059441036d302b0a07eb454cfe2", "sha256": "00b45353f0b668a174427d0067c06646cb83e2d9d4dc0eddce7eef063ee6bb3f" }, "downloads": -1, "filename": "natural-keys-0.1.0.tar.gz", "has_sig": false, "md5_digest": "952a0059441036d302b0a07eb454cfe2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8290, "upload_time": "2016-03-07T22:45:19", "url": "https://files.pythonhosted.org/packages/cb/4d/ebf8dec68efd42bec3811769a66bd00b03f6dcf8786ce827f31555aff720/natural-keys-0.1.0.tar.gz" } ], "1.0.0": [ { "comment_text": "", "digests": { "md5": "d7153fbaa43f9cee54748583bff4726f", "sha256": "94c3d36359edf32aeaad0bd4d3a57bf5f7b3b458ccdbe89c737ac21dbcfce8c0" }, "downloads": -1, "filename": "natural-keys-1.0.0.tar.gz", "has_sig": false, "md5_digest": "d7153fbaa43f9cee54748583bff4726f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8322, "upload_time": "2016-08-23T16:00:29", "url": "https://files.pythonhosted.org/packages/31/0a/b8e2736e57c984ce4ee4ce1236ab82200b721a9afe6e49358a1e82099de9/natural-keys-1.0.0.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "20d783519faf70e50d4503449f5c3cf0", "sha256": "1a9ff4f922e1fbb2fb9ad292aefd75ffb2556040bd87daad1bc9f0f47a8c1e12" }, "downloads": -1, "filename": "natural-keys-1.1.0.tar.gz", "has_sig": false, "md5_digest": "20d783519faf70e50d4503449f5c3cf0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8417, "upload_time": "2016-11-07T22:16:42", "url": "https://files.pythonhosted.org/packages/f7/7d/9c8809156b795657d222da5abe340e92a4bc6f1dba1dcd2cd24b63b11552/natural-keys-1.1.0.tar.gz" } ], "1.2.0": [ { "comment_text": "", "digests": { "md5": "4a5f833bc5ee616bda29e94341995496", "sha256": "60c8873bd7b5148e3df50fafce59a76f33744009c007c88f69b0ddedb18665c9" }, "downloads": -1, "filename": "natural-keys-1.2.0.tar.gz", "has_sig": false, "md5_digest": "4a5f833bc5ee616bda29e94341995496", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8577, "upload_time": "2017-05-02T16:32:10", "url": "https://files.pythonhosted.org/packages/77/f5/c5e1171bc22a97f7ac09272a41f90c81c32baf285fdbb90d2470f17d1960/natural-keys-1.2.0.tar.gz" } ], "1.2.1": [ { "comment_text": "", "digests": { "md5": "83996c0494ef69b26ee610394629aea8", "sha256": "acc720bef34a11abc30f71e9f8a8d4cb999b8f17315307893446736986d82b66" }, "downloads": -1, "filename": "natural-keys-1.2.1.tar.gz", "has_sig": false, "md5_digest": "83996c0494ef69b26ee610394629aea8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8799, "upload_time": "2017-09-13T14:24:57", "url": "https://files.pythonhosted.org/packages/6d/7d/595a9800b4af47f75dadf7ed06a4efd8095267d49e67567e8f5d436ada8a/natural-keys-1.2.1.tar.gz" } ], "1.3.0": [ { "comment_text": "", "digests": { "md5": "4bfe8b11299c9012292199de69487def", "sha256": "57b1af61443453741791fb4d68f6460ec7811bd7ee4484711d31d06e88b2ef5f" }, "downloads": -1, "filename": "natural-keys-1.3.0.tar.gz", "has_sig": false, "md5_digest": "4bfe8b11299c9012292199de69487def", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8845, "upload_time": "2018-04-06T15:16:18", "url": "https://files.pythonhosted.org/packages/a5/b4/311f593a8e3cc04134580af434f31e67134f808f02154883d91600d63661/natural-keys-1.3.0.tar.gz" } ], "1.4.0": [ { "comment_text": "", "digests": { "md5": "9c2bb61e77f67c13825e2c021da5b6df", "sha256": "10da994ff405e470088d5568ae66be85d2208491dbe72c6b2789aa75bbe44d7d" }, "downloads": -1, "filename": "natural-keys-1.4.0.tar.gz", "has_sig": false, "md5_digest": "9c2bb61e77f67c13825e2c021da5b6df", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10481, "upload_time": "2018-04-12T16:40:26", "url": "https://files.pythonhosted.org/packages/a7/82/11cba802b92bb6aa0e80cd157a7742d6d9e8ec841b14579ab9bb291d7daf/natural-keys-1.4.0.tar.gz" } ], "1.5.0": [ { "comment_text": "", "digests": { "md5": "df98f198b3a2ae59a26a250332e4e022", "sha256": "4007e4297131ee5477e556218a4b66fa0f36fd00bb56a4b13ec678917b96e846" }, "downloads": -1, "filename": "natural_keys-1.5.0-py3-none-any.whl", "has_sig": false, "md5_digest": "df98f198b3a2ae59a26a250332e4e022", "packagetype": "bdist_wheel", "python_version": "3.5", "requires_python": null, "size": 10055, "upload_time": "2019-03-01T03:43:54", "url": "https://files.pythonhosted.org/packages/85/f7/0b616767a89a56693ce8284ab6a971009556e1f65b025d96b0a397cc5de6/natural_keys-1.5.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "75717504cdafe8939fdb5c764dae34bd", "sha256": "e4667e604f0d6ed9f77208573708cb439f0c7d09e20ab8e8d65418652aac0f94" }, "downloads": -1, "filename": "natural-keys-1.5.0.tar.gz", "has_sig": false, "md5_digest": "75717504cdafe8939fdb5c764dae34bd", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16804, "upload_time": "2019-03-01T03:43:53", "url": "https://files.pythonhosted.org/packages/ca/0e/bb024b2e61dea32de6500336adc3e90f7ad4379f544105c13c39a9121f3b/natural-keys-1.5.0.tar.gz" } ], "1.5.0.post1": [ { "comment_text": "", "digests": { "md5": "713bb59f4b2a3387395ba3f77e7c040e", "sha256": "fa2ca04dbb15b18dee39403c9273615f4830c8e392bdfefea408c9e6c1ac0270" }, "downloads": -1, "filename": "natural_keys-1.5.0.post1-py3-none-any.whl", "has_sig": false, "md5_digest": "713bb59f4b2a3387395ba3f77e7c040e", "packagetype": "bdist_wheel", "python_version": "3.5", "requires_python": null, "size": 10127, "upload_time": "2019-03-01T03:50:51", "url": "https://files.pythonhosted.org/packages/eb/55/0b6b7399e983c662c7b48195aa7fc8f00bcc2d457cf962b38faa0b09c6b6/natural_keys-1.5.0.post1-py3-none-any.whl" } ], "1.5.0.post2": [ { "comment_text": "", "digests": { "md5": "c8add3e09c2303f0064d409322cad6a6", "sha256": "b7cd15bbeed002d3682da547c059bb15d3fcfcbc82501a79dcc9d89cbf696f12" }, "downloads": -1, "filename": "natural_keys-1.5.0.post2-py3-none-any.whl", "has_sig": false, "md5_digest": "c8add3e09c2303f0064d409322cad6a6", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 10123, "upload_time": "2019-03-01T03:53:31", "url": "https://files.pythonhosted.org/packages/75/c5/83875a60f13311c40473f0a28680e59f70707d98b2892add80eca4a7765d/natural_keys-1.5.0.post2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "3dcfacfc124f6ce63b0883397f45e104", "sha256": "2a83ce1790c5616e27368321998d23c29653c3e09aa8a41a03545348f5ddbc1d" }, "downloads": -1, "filename": "natural-keys-1.5.0.post2.tar.gz", "has_sig": false, "md5_digest": "3dcfacfc124f6ce63b0883397f45e104", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16811, "upload_time": "2019-03-01T03:57:59", "url": "https://files.pythonhosted.org/packages/8b/a1/9cecd4e41593cf5ae0ac340ce10481473dae2cbffa3091526389e455bb29/natural-keys-1.5.0.post2.tar.gz" } ], "1.5.1": [ { "comment_text": "", "digests": { "md5": "b894a63a6eaeda660ee534916e315bde", "sha256": "ea4a19e3ce67a52e2f2745443113913bab748449ab345467064819e25c26d156" }, "downloads": -1, "filename": "natural_keys-1.5.1-py3-none-any.whl", "has_sig": false, "md5_digest": "b894a63a6eaeda660ee534916e315bde", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 10314, "upload_time": "2019-09-02T01:25:29", "url": "https://files.pythonhosted.org/packages/af/8c/de54ee11fcc068386426aba93a0879545586878b8dac4f837f4a3eec85b2/natural_keys-1.5.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "72c217efc19887ae4c2bab1522c197ab", "sha256": "b0bc3f17baf557fa2aed3bb94728adcfaef3dc068f5eb14d2d00c76169ed6fd3" }, "downloads": -1, "filename": "natural-keys-1.5.1.tar.gz", "has_sig": false, "md5_digest": "72c217efc19887ae4c2bab1522c197ab", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17177, "upload_time": "2019-09-02T01:25:31", "url": "https://files.pythonhosted.org/packages/cd/bd/a2db13159e4e3613a46eba1a59e98fbf8a4a699efab4aadda433c9e43635/natural-keys-1.5.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "b894a63a6eaeda660ee534916e315bde", "sha256": "ea4a19e3ce67a52e2f2745443113913bab748449ab345467064819e25c26d156" }, "downloads": -1, "filename": "natural_keys-1.5.1-py3-none-any.whl", "has_sig": false, "md5_digest": "b894a63a6eaeda660ee534916e315bde", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 10314, "upload_time": "2019-09-02T01:25:29", "url": "https://files.pythonhosted.org/packages/af/8c/de54ee11fcc068386426aba93a0879545586878b8dac4f837f4a3eec85b2/natural_keys-1.5.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "72c217efc19887ae4c2bab1522c197ab", "sha256": "b0bc3f17baf557fa2aed3bb94728adcfaef3dc068f5eb14d2d00c76169ed6fd3" }, "downloads": -1, "filename": "natural-keys-1.5.1.tar.gz", "has_sig": false, "md5_digest": "72c217efc19887ae4c2bab1522c197ab", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17177, "upload_time": "2019-09-02T01:25:31", "url": "https://files.pythonhosted.org/packages/cd/bd/a2db13159e4e3613a46eba1a59e98fbf8a4a699efab4aadda433c9e43635/natural-keys-1.5.1.tar.gz" } ] }