{"id":16476971,"url":"https://github.com/agilgur5/django-serializable-model","last_synced_at":"2025-09-29T02:31:03.161Z","repository":{"id":57421931,"uuid":"121683774","full_name":"agilgur5/django-serializable-model","owner":"agilgur5","description":"Django classes to make your models, managers, and querysets serializable, with built-in support for related objects in ~150 LoC","archived":false,"fork":false,"pushed_at":"2019-11-03T03:35:21.000Z","size":44,"stargazers_count":15,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-14T00:23:19.541Z","etag":null,"topics":["dict","django","full","json","model","model-to-dict","modelmanager","python","queryset","serialize","serializer","serializer-django","serializers","wadofstuff"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/agilgur5.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-02-15T21:16:20.000Z","updated_at":"2024-04-16T00:49:34.000Z","dependencies_parsed_at":"2022-09-06T05:11:41.171Z","dependency_job_id":null,"html_url":"https://github.com/agilgur5/django-serializable-model","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agilgur5%2Fdjango-serializable-model","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agilgur5%2Fdjango-serializable-model/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agilgur5%2Fdjango-serializable-model/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agilgur5%2Fdjango-serializable-model/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/agilgur5","download_url":"https://codeload.github.com/agilgur5/django-serializable-model/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234579633,"owners_count":18855639,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dict","django","full","json","model","model-to-dict","modelmanager","python","queryset","serialize","serializer","serializer-django","serializers","wadofstuff"],"created_at":"2024-10-11T12:44:08.025Z","updated_at":"2025-09-29T02:30:57.870Z","avatar_url":"https://github.com/agilgur5.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# django-serializable-model\n\n\u003c!-- releases / versioning --\u003e\n[![PyPI version](https://img.shields.io/pypi/v/django-serializable-model.svg)](https://pypi.org/project/django-serializable-model/)\n[![releases](https://img.shields.io/github/tag-pre/agilgur5/django-serializable-model.svg)](https://github.com/agilgur5/django-serializable-model/releases)\n[![commits](https://img.shields.io/github/commits-since/agilgur5/django-serializable-model/latest.svg)](https://github.com/agilgur5/django-serializable-model/commits/master)\n\u003cbr\u003e\u003c!-- downloads --\u003e\n[![dm](https://img.shields.io/pypi/dm/django-serializable-model.svg)](https://pypi.org/project/django-serializable-model/)\n[![dw](https://img.shields.io/pypi/dw/django-serializable-model.svg)](https://pypi.org/project/django-serializable-model/)\n\u003cbr\u003e\u003c!-- status / activity --\u003e\n[![build status](https://img.shields.io/travis/agilgur5/django-serializable-model/master.svg)](https://travis-ci.org/agilgur5/django-serializable-model)\n[![code coverage](https://img.shields.io/codecov/c/gh/agilgur5/django-serializable-model/master.svg)](https://codecov.io/gh/agilgur5/django-serializable-model)\n\u003cbr\u003e\nDjango classes to make your models, managers, and querysets serializable, with built-in support for related objects in ~100 LoC (shorter than this README!)\n\n## Table of Contents\n\nI. [Installation](#installation) \u003cbr /\u003e\nII. [Usage](#usage) \u003cbr /\u003e\nIII. [How it Works](#how-it-works) \u003cbr /\u003e\nIV. [Related Libraries](#related-libraries) \u003cbr /\u003e\nV. [Backstory](#backstory)\n\n## Installation\n\n```shell\npip install django-serializable-model\n```\n\nIt is expected that you already have Django installed\n\n### Compatibility\n\n[![Python versions](https://img.shields.io/pypi/pyversions/django-serializable-model.svg)](https://pypi.org/project/django-serializable-model/)\n[![Django versions](https://img.shields.io/pypi/djversions/django-serializable-model.svg)](https://pypi.org/project/django-serializable-model/)\n\n[Tested](https://travis-ci.org/agilgur5/django-serializable-model) on Django 2.2, 1.11, 1.9, and 1.5 as well as Python 3.5, 3.4, and 2.7\n\n- Should work with Django 1.4-2.x and Python 2.7-3.x\n  - Has several Django backward \u0026 forward compatibility fixes built-in\n- Likely works with Django 0.95-1.3 as well\n  - Pre 1.3 does not support the [`on_delete` argument](https://django.readthedocs.io/en/1.3.X/releases/1.3.html#configurable-delete-cascade) on relations.\n  This only affects the usage and examples below; the internals are unaffected.\n  - Pre 0.95, the Manager API didn't exist, so some functionality may be limited in those versions, or it may just error on import\n- Have not confirmed if this works with earlier versions of Python.\n\nPlease submit a PR or file an issue if you have a compatibility problem or have confirmed compatibility on versions.\n\n\u003cbr\u003e\n\n## Usage\n\nSimplest use case, just implements the `.serialize()` function on a model:\n\n```python\nfrom django.db import models\nfrom django_serializable_model import SerializableModel\n\n\nclass User(SerializableModel):\n    email = models.CharField(max_length=765, blank=True)\n    name = models.CharField(max_length=100)\n\n\nnew_user = User.objects.create(\n    name='John Doe',\n    email='john@doe.com',\n)\n\nprint new_user.serialize()\n# {'id': 1, 'email': 'john@doe.com', 'name': 'John Doe'}\n```\n\n\u003cbr\u003e\n\nWith an override of the default `.serialize()` function to only include whitelisted fields in the serialized dictionary:\n\n```python\nfrom django.db import models\nfrom django_serializable_model import SerializableModel\n\n\nclass User(SerializableModel):\n    email = models.CharField(max_length=765, blank=True)\n    name = models.CharField(max_length=100)\n    # whitelisted fields that are allowed to be seen\n    WHITELISTED_FIELDS = set([\n        'name',\n    ])\n\n\n    def serialize(self, *args, **kwargs):\n        \"\"\"Override serialize method to only serialize whitelisted fields\"\"\"\n        fields = kwargs.pop('fields', self.WHITELISTED_FIELDS)\n        return super(User, self).serialize(*args, fields=fields)\n\n\nnew_user = User.objects.create(\n    name='John Doe',\n    email='john@doe.com',\n)\n\nprint new_user.serialize()\n# {'name': 'John Doe'}\n```\n\n\u003cbr\u003e\n\nWith a simple, one-to-one relation:\n\n```python\nfrom django.db import models\nfrom django_serializable_model import SerializableModel\n\n\nclass User(SerializableModel):\n    email = models.CharField(max_length=765, blank=True)\n    name = models.CharField(max_length=100)\n\n\nclass Settings(SerializableModel):\n    user = models.OneToOneField(User, primary_key=True,\n        on_delete=models.CASCADE)\n    email_notifications = models.BooleanField(default=False)\n\n    def serialize(self, *args):\n        \"\"\"Override serialize method to not serialize the user field\"\"\"\n        return super(Settings, self).serialize(*args, exclude=['user'])\n\n\nnew_user = User.objects.create(\n    name='John Doe',\n    email='john@doe.com',\n)\nSettings.objects.create(user=new_user)\n\nnew_user_refreshed = User.objects.select_related('settings').get(pk=new_user.pk)\n\nprint new_user_refreshed.serialize()\n# {'id': 1, 'email': 'john@doe.com', 'name': 'John Doe'}\n\n# recursively serialize Settings object by passing the join in\nprint new_user_refreshed.serialize('settings')\n# {'id': 1, 'email': 'john@doe.com', 'settings': {'email_notifications': False}, 'name': 'John Doe'}\n```\n\n\u003cbr\u003e\n\nWith a foreign key relation:\n\n```python\nfrom django.db import models\nfrom django_serializable_model import SerializableModel\n\n\nclass User(SerializableModel):\n    email = models.CharField(max_length=765, blank=True)\n    name = models.CharField(max_length=100)\n\n\nclass Post(SerializableModel):\n    user = models.ForeignKey(User, on_delete=models.CASCADE)\n    text = models.TextField()\n\n\nnew_user = User.objects.create(\n    name='John Doe',\n    email='john@doe.com',\n)\nPost.objects.create(user=new_user, text='wat a nice post')\nPost.objects.create(user=new_user, text='another nice post')\n\n# called on QuerySet\nprint Post.objects.all().serialize()\n# [{'id': 1, 'text': 'wat a nice post', 'user_id': 1}, {'id': 2, 'text': 'another nice post', 'user_id': 1}]\n# adds an _id to the foreign key name, just like when using `.values()`\n\n# called on Manager\nuser1 = User.objects.get(pk=new_user.pk)\nprint user1.post_set.serialize()\n# [{'id': 1, 'text': 'wat a nice post', 'user_id': 1}, {'id': 2, 'text': 'another nice post', 'user_id': 1}]\n\n# recursively serialize Post objects by passing the join in\nprint User.objects.prefetch_related('post_set').get(pk=new_user.pk).serialize('post_set')\n\"\"\"\n{\n    'id': 1,\n    'email': 'john@doe.com',\n    'name': 'John Doe',\n    'post_set': [{'id': 1, 'text': 'wat a nice post', 'user_id': 1}, {'id': 2, 'text': 'another nice post', 'user_id': 1}]\n}\n\"\"\"\n```\n\n\u003cbr\u003e\n\n`.serialize` takes in any number of joins as its `*args` and they can be of any depth, using the same `__` syntax as [`prefetch_related`](https://docs.djangoproject.com/en/2.0/ref/models/querysets/#prefetch-related). This means if your `Post` object also had `Comment` objects, you could write:\n\n```python\nUser.objects.prefetch_related('post_set__comment_set').serialize('post_set__comment_set')\n```\n\nand get an array of `Comment` dictionaries within each `Post` dictionary. If your `Post` object also had `Like` objects:\n\n```python\njoins = ['post_set__comment_set', 'post_set__like_set']\nUser.objects.prefetch_related(*joins).serialize(*joins)\n```\n\n\u003cbr\u003e\n\n### JSON and APIs\n\nSince `.serialize` outputs a dictionary, one can turn it into JSON simply by using `json.dumps` on the dictionary.\n\nIf you're building an API, you can use `JSONResponse` on the dictionary as well.\n\n\u003cbr\u003e\n\n## How it works\n\nImplementing a `.serialize` method on Models, Managers, and QuerySets allows for easily customizable whitelists and blacklists (among other things) on a per Model basis.\nThis type of behavior was not possible a simple recursive version of `model_to_dict`, but is often necessary for various security measures and overrides.\nIn order to recurse over relations / joins, it accepts the same arguments as the familiar `prefetch_related`, which, in my use cases, often immediately precedes the `.serialize` calls.\n`.serialize` also uses a custom `model_to_dict` function that behaves a bit differently than the built-in one in a variety of ways that are more expected when building an API (see the docstring).\n\nI'd encourage you to read the source code, since it's shorter than this README :)\n\n## Related Libraries\n\n- [django-api-decorators](https://github.com/agilgur5/django-api-decorators)\n  - `Tiny decorator functions to make it easier to build an API using Django in ~100 LoC`\n\n\u003cbr\u003e\n\n## Backstory\n\nThis library was built while I was working on [Yorango](https://github.com/Yorango)'s ad-hoc API. Writing code to serialize various models was complex and quite tedious, resulting in messy spaghetti code for many of our API methods. The only solutions I could find online were the [Django Full Serializers](http://code.google.com/p/wadofstuff/wiki/DjangoFullSerializers) from [wadofstuff](https://github.com/mattimustang/wadofstuff) as well as some recursive `model_to_dict` snippets online -- none of which gave the option for customizable whitelists and blacklists on a per Model basis.\nLater on, I found that [Django REST Framework's ModelSerializers](http://www.django-rest-framework.org/api-guide/serializers#modelserializer) do offer similar functionality to what I was looking for (and _without_ requiring buy-in to the rest of the framework), albeit with some added complexity and robustness.\n\nI ended up writing my own solution in ~100 LoC that handled basically all of my needs and replaced a ton of messy serialiazation code from all around the codebase. It was used in production with fantastic results, including on queries with quite the complexity and depth, such as:\n\n```python\n\n    joins = ['unit_set', 'unit_set__listing_set',\n        'unit_set__listing_set__tenants', 'unit_set__listing_set__bill_set',\n        'unit_set__listing_set__payment_set__payer',\n        'unit_set__listing_set__contract']\n    s_props = (user.property_set.all().prefetch_related(*joins)\n        .serialize(*joins))\n\n```\n\nHad been meaning to extract and open source this as well as other various useful utility libraries I had made at Yorango and finally got the chance!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagilgur5%2Fdjango-serializable-model","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fagilgur5%2Fdjango-serializable-model","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagilgur5%2Fdjango-serializable-model/lists"}