{ "info": { "author": "Guillaume Chorn", "author_email": "guillaume.chorn@gmail.com", "bugtrack_url": null, "classifiers": [], "description": "# golden-marshmallows\nA better integration between SQLAlchemy and Marshmallow. A little (SQL)alchemy to turn `marshmallow`s into gold.\n\n# Installation\nSimply install with `pip`:\n```\n$ pip install golden-marshmallows\n```\n# Usage\n## Serialization\nTake these SQLAlchemy models as examples:\n```python\nclass WizardCollege(Base):\n __tablename__ = 'wizard_college'\n id = Column(Integer, primary_key=True)\n name = Column(String)\n alchemists = relationship('Alchemist')\n\n def __repr__(self):\n return ''.format(self=self)\n\nclass Alchemist(Base):\n __tablename__ = 'alchemists'\n id = Column(Integer, primary_key=True)\n name = Column(String)\n school_id = Column(Integer, ForeignKey('wizard_college.id'))\n formulae = relationship('Formula')\n \n def __repr__(self):\n return ''.format(self=self)\n\nclass Formula(Base):\n __tablename__ = 'forumulae'\n id = Column(Integer, primary_key=True)\n title = Column(String)\n author_id = Column(Integer, ForeignKey('alchemists.id'))\n\n def __repr__(self):\n return ''.format(self=self)\n```\nThe `GoldenSchema` class allows quick and easy generation of `marshmallow` schemas that can be used for SQLAlchemy object serialization/deserialization. Simply pass the model class on initialization and you're ready to go:\n```python\nimport json\nfrom golden_marshmallow.schema import GoldenSchema\nfrom models import Alchemist, Formula, WizardCollege\n\nalchemist = Alchemist(name='Albertus Magnus', school_id=1)\nsession.add(alchemist)\nsession.flush()\n\nschema = GoldenSchema(Alchemist)\n\nserialized = schema.dump(alchemist).data\n\nprint(json.dump(serialized, indent=4))\n# { \n# \"id\": 1,\n# \"name\": \"Albertus Magnus\",\n# \"school_id\": 1\n# }\n```\nThat's it! No need to define your own `Schema` subclass, unless you really want to (more on that below).\n\n## Nested objects\nBut what about this alchemist's formulae? Nested objects can easily be added to the mix by passing in a dictionary mapping each field that contains a nested object (or objects) to the relevant SQLAlchemy class:\n```python\nnested_map = {\n 'formulae': {\n 'class': Formula,\n 'many': True\n }\n}\n\nformula = Formula(title='transmutation')\nalchemist.formulae.append(formula)\nsession.commit()\n\nschema = GoldenSchema(Alchemist, nested_map=nested_map)\n\nserialized = schema.dump(alchemist).data\n\nprint(json.dump(serialized, indent=4))\n# {\n# \"id\": 1,\n# \"name\": \"Albertus Magnus\",\n# \"school_id\": 1,\n# \"formulae\" : [\n# {\n# \"title\": \"transmutation\"\n# }\n# ]\n# }\n```\nIn fact, the `GoldenSchema` class supports arbitrary nesting in this fashion, simply adjust the map as necessary:\n```python\nnested_map = {\n 'alchemists': {\n 'class': Alchemist,\n 'many': True,\n 'nested_map': {\n 'formulae': {\n 'class': Formula,\n 'many': True\n }\n }\n }\n}\n\ncollege = WizardCollege(name='Bogwarts')\ncollege.alchemists.append(alchemist)\nsession.add(college)\nsession.flush()\n\nschema = GoldenSchema(WizardCollege, nested_map=nested_map)\n\nserialized = schema.dump(college).data\n\nprint(json.dump(serialized, indent=4))\n# {\n# \"id\": 1,\n# \"name\": \"Bogwarts\",\n# \"alchemists\": [\n# {\n# \"id\": 1,\n# \"school_id\": 1,\n# \"name\": \"Albertus Magnus\",\n# \"formulae\": [\n# {\n# \"title\": \"transmutation\",\n# \"author_id\": 1,\n# \"id\": 1\n# }\n# ]\n# }\n# ]\n# }\n```\n## Deserialization\nOf course, you can deserialize data into SQLAlchemy objects just as easily:\n```python\n# Start at the end of the last example and work backwards\ndata = {\n \"id\": 1,\n \"name\": \"Bogwarts\",\n \"alchemists\": [\n {\n \"formulae\": [\n {\n \"title\": \"transmutation\",\n \"author_id\": 1,\n \"id\": 1\n }\n ],\n \"school_id\": 1,\n \"id\": 1,\n \"name\": \"Albertus Magnus\"\n }\n ]\n}\n\ncollege = schema.load(data).data\nprint(college)\n# \nprint(college.alchemists)\n# []\nprint(college.alchemists[0].formulae)\n# []\n```\n# Extra Features\n## camelCasing/snake_casing\nThe `snake_to_camel` flag allows serde to/from camelCase, for example when serializing Python data into JSON to send as an API Response:\n```python\n# `Formula.author_id` is easily converted to camelCase\nschema = GoldenSchema(Formula, snake_to_camel=True)\n\nserialized = schema.dump(formula).data\n\nprint(json.dumps(serialized, indent=4))\n# Notice `author_id` has become `authorId`\n# {\n# \"title\": \"transmutation\",\n# \"authorId\": 1,\n# \"id\": 1\n# }\n```\nThe same `GoldenSchema` instance, when used to `load` (deserialize) data, will expect camelCased attributes and load them as snake_cased attributes:\n```python\ndata = {\n \"title\": \"transmutation\",\n \"authorId\": 1,\n \"id\": 1\n}\nformula = schema.load(data).data\n\nprint(formula.author_id)\n# 1\n```\nA flag with the opposite behavior, `camel_to_snake`, is also included.\n\nThis feature also works for manually declared fields; that is, fields you yourself declare when subclassing `GoldenSchema` like so:\n```python\nclass MySchema(GoldenSchema):\n manually_declared = fields.Function(lambda obj: 'my special value')\n \nmy_schema = MySchema(Formula, snake_to_camel=True)\n\nserialized = schema.dump(formula).data\nprint(json.dumps(serialized, indent=4))\n# `manually_declared` has become camelCase\n# {\n# \"title\": \"transmutation\",\n# \"authorId\": 1,\n# \"id\": 1,\n# \"manuallyDeclared\": \"my special value\"\n# }\n```\nIn fact, you can use this feature without involving SQLAlchemy at all; just use `CaseChangingSchema`, the parent class of `GoldenSchema`:\n```python\nfrom golden_marshmallows.schema import CaseChangingSchema\n\nclass SnakeSchema(CaseChangingSchema):\n attr_one = fields.String()\n attr_two = fields.Integer()\n \nclass SnakeObj:\n def __init__(self, attr_one, attr_two):\n self.attr_one = attr_one\n self.attr_two = attr_two\n \nschema = SnakeSchema(snake_to_camel=True)\nobj = SnakeObj('field1', 2)\n\nserialized = schema.dump(obj).data\nprint(json.dumps(serialized, indent=4))\n# {\n# 'attrOne': 'field1',\n# 'attrTwo': 2\n# }\n```\n\n## Copying objects\nAs a minor convenience, you can pass the `new_obj` flag on initialization to indicate that any fields named `id` should be ignored during deserialization:\n```python\nschema = GoldenSchema(Formula, snake_to_camel=True, new_obj=True)\n\ndata = {\n \"title\": \"transmutation\",\n \"authorId\": 1,\n \"id\": 1\n}\n\nnew_formula = schema.load(data).data\nprint(new_formula.title)\n# 'transmutation'\nprint(new_formula.id) # None\n```\nThis allows you to quickly deserialize data representations of existing objects into new copies.", "description_content_type": "text/markdown", "docs_url": null, "download_url": "https://github.com/gchorn/golden-marshmallows/archive/v0.2.0.tar.gz", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/gchorn/golden-marshmallows", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "golden_marshmallows", "package_url": "https://pypi.org/project/golden_marshmallows/", "platform": "", "project_url": "https://pypi.org/project/golden_marshmallows/", "project_urls": { "Download": "https://github.com/gchorn/golden-marshmallows/archive/v0.2.0.tar.gz", "Homepage": "https://github.com/gchorn/golden-marshmallows" }, "release_url": "https://pypi.org/project/golden_marshmallows/0.2.0/", "requires_dist": null, "requires_python": "", "summary": "Marshmallow Schema subclass that auto-defines fields based on SQLAlchemy classes", "version": "0.2.0" }, "last_serial": 3855257, "releases": { "0.1.2": [ { "comment_text": "", "digests": { "md5": "8262dae6cbca6801d269f1142fa4b2a4", "sha256": "0af24553b59661ff87ca3bd3a92bc10f93bceecbb2109c8dec1119adbeb8d915" }, "downloads": -1, "filename": "golden_marshmallows-0.1.2.tar.gz", "has_sig": false, "md5_digest": "8262dae6cbca6801d269f1142fa4b2a4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4757, "upload_time": "2018-04-12T06:00:13", "url": "https://files.pythonhosted.org/packages/60/58/dd6b2dbe4fe5c27a9ea012aa63222b74ef541cfeec2da1a04ac955adef37/golden_marshmallows-0.1.2.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "f659b892b98502eccc58fc425030df06", "sha256": "94cd0a3155d7a1ee4c00cbab3faaafe286f3aaf2e0617044dd4ddc3e81a54891" }, "downloads": -1, "filename": "golden_marshmallows-0.2.0.tar.gz", "has_sig": false, "md5_digest": "f659b892b98502eccc58fc425030df06", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6470, "upload_time": "2018-05-11T19:37:35", "url": "https://files.pythonhosted.org/packages/fb/18/d4cf2f51612e84803e0363e3cc7bce7a352f03a29b857498d153c433d0aa/golden_marshmallows-0.2.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "f659b892b98502eccc58fc425030df06", "sha256": "94cd0a3155d7a1ee4c00cbab3faaafe286f3aaf2e0617044dd4ddc3e81a54891" }, "downloads": -1, "filename": "golden_marshmallows-0.2.0.tar.gz", "has_sig": false, "md5_digest": "f659b892b98502eccc58fc425030df06", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6470, "upload_time": "2018-05-11T19:37:35", "url": "https://files.pythonhosted.org/packages/fb/18/d4cf2f51612e84803e0363e3cc7bce7a352f03a29b857498d153c433d0aa/golden_marshmallows-0.2.0.tar.gz" } ] }