{ "info": { "author": "Sasha Bermeister", "author_email": "sbermeister@gmail.com", "bugtrack_url": null, "classifiers": [], "description": "# Simple Python Object-Database Serialiser (SPODS)\n\n## Why it rocks\n\nDatabases are annoying. Sometimes, we just want to save persistent data, and not\nhave to worry about SQL injection, transaction issues, and connection problems.\n\nWith SPODS, there is finally an alternative. SPODS is a simple, lightweight, easy to use alternative to heavy serialisation libraries. SPODS also provides a great way to access generic Python objects.\n\nAnd now, SPODS even gives you a _free_ JSON interface to your DB through a CGI script.\n\n## Getting started\n\nFirst, copy spods.py to the directory you want to use it in.\nInclude it using:\n \n```python\n >>> import spods\n```\n\n## Making SPODS objects\n\nSince the Python class constructor is not _really_ useful to use here, we will use a different method of defining a class.\n\nFirst, create a bunch of `Field` objects and store them in a `Table`:\n\n```python\n >>> fields = [\n ... Field('id', int, pk=True),\n ... Field('title', str),\n ... Field('isbn', int),\n ... Field('condition', bool)\n ... ]\n ... \n >>> books_table = Table('book', fields)\n```\n \nNow it's time to link our table to our database, and start making objects. To do this, simply connect to your database and call the `create_linked_class(table, connection)` function:\n \n```python\n >>> import sqlite3\n >>> con = sqlite3.connect(\"database.db\")\n >>> Book = spods.link_table(books_table, con)\n```\n\nCongratulations! You've just created a database, in `database.db`, and are ready to start storing `Book`s in it. SPODS will automatically create the Books table for you if it doesn't exist.\n\nYou can also add the flag `clear_existing=True` to `spods.link_table()` to delete any table already in the database with that name.\n\nTo add your first record, you can run something like:\n\n```python\n >>> book = Book()\n >>> book\n {'id': 1, 'title': None, 'isbn': None, 'condition': None}\n```\n\nNow, when you modify your `book` variable, the changes will be reflected in your database.\n\nYou can also access an existing book object by providing its ID:\n\n```python\n >>> book.title = 'The Notebook'\n >>> book.title\n 'The Notebook'\n >>> existing_book = Book(id=1)\n >>> existing_book\n {'id': 1, 'title': 'The Notebook', 'isbn': None, 'condition': None}\n```\n\nYou can also create new objects by simply not specifying the id:\n\n```python\n >>> book = Book(title='Atlas')\n >>> book.title\n 'Atlas'\n```\n\n## Using SPODS objects\n\nYou can use all the usual getter and setter methods for attributes, such as:\n \n```python\n >>> x.id = 7\n >>> x.id\n 7\n```\n \nAs well as dictionary-like access:\n\n```python\n >>> x['id'] = 7\n >>> x['id']\n 7\n```\n \nOr a mix of both:\n\n```python\n >>> field = 'id'\n >>> x[field] = 7\n >>> x.id\n 7\n```\n \nYou can also reset fields to their default values, using the `del` keyword:\n \n```python\n >>> x['id'] = 5\n >>> del x['id']\n >>> x['id'] == None\n True\n```\n\nOr, equivalently:\n\n```python\n >>> x.id = 5\n >>> del x.id\n >>> x.id == None\n True\n```\n\n### Deleting records\n\nTo delete an item, just delete its primary key:\n\n```python\n >>> del x.id; del x\n```\n\n**Warning:** Do NOT try to access an item after deleting its primary key. The safest thing to do is delete the object straight away!\n \n## Syncing with the DB\n\nAll *updates*, *insertions* and *deletes* are automatically synced with the DB. So don't worry!\n\nIf, for some reason, you need to ensure the row is synced, you can manually call the sync functions:\n\n```python\n >>> x.read_sync() # reads all values out of the DB, replacing X's local copies\n >>> x.write_sync() # writes all values into the DB, replacing the DB's values\n```\n\n## Relations\n\nRelations in SPODS are pretty easy, too. To make a one-to-many relation, use the syntax:\n\n```python\n >>> Book.has_one(Author)\n```\n\nwhere `Author` was created using the `link_table()` method, as before.\n\nSimilarly, the `has_one()` function can take the flag `clear_existing=True` to force creating a new column.\n\nNow, you can relate two objects using their primary key:\n\n```python\n >>> book = Book(title='Harry Potter')\n >>> author = Author(name='J K Rowling')\n >>> author.id\n 1\n >>> b.author_id = 1\n```\n\n... And access the related object through the first one!\n\n```python\n >>> book['author'].name\n 'J K Rowling'\n >>> book['author'].id\n 1\n```\n\n**NOTE: The current version of SPODS does _NOT_ support attribute access (e.g. `b.author`, in the above example) for relationships. We are working on fixing this, but for now, only dictionary access (e.g. `b['author']`) is supported.**\n \n## The JSON API\n\nNow comes the real reason why you'd want to use SPODS. SPODS comes with a jokingly-easy, automatically generated JSON API for use in any web application.\n\nTo serve an API request, just call the `spods.serve_api` function with all the classes you want to make accessible:\n\n```python\n >>> result = spods.serve_api(Book, Author)\n >>> result\n ...\n```\n\n`serve_api` will read the cookies and form data of the user requesting the API access, and return a string to be printed to the webpage.\n\n\n\nThe resultant JSON contains 3 fields:\n\n* `status`, which is 0 on success, or nonzero on failure (positive on general error, negative on authorisation/permissions error)\n* `error`, which contains an error message describing the error\n* `data`, which contains a list of the objects (in dictionary format) affected by the request\n * e.g. `[{\"id\": 2, \"title\": \"Justice is served\", \"author_id\": 1}, {\"id\": 3, \"title\": \"The best day on Earth\", \"author_id\": 2}]`\n \nAPI requests are in the following format:\n\n(`{}` brackets indicate a parameter, `()` brackets indicate a default value, `[]` brackets indicate an optional value):\n\n```\n http://www.yourdomain.com/api.py?\n obj={ }\n [&action={ (view) | add | edit | delete }]\n [&={ }]\n \n if action!=add:\n [&fetch={ (all) | one }]\n \n if fetch=all:\n [&start={ (0) | number >= 0 }]\n [&limit={ ( | number >= 0 }]\n```\n\nAll reserved request values (all but the ones in `<`'s) are case-**in**sensitive. Table names, field names and field values are case-sensitive.\n\nFor the `action=edit` request, fields to _search_ for must _begin (or end) with at least one asterisk \\*_, whereas fields to _change_ to can remain normal.\n\n### Editing records\n\nFor example, to rename all books called 'The Wizard of Oz' to 'The Witch of Oz', you could use:\n\n```\n http://www.yourdomain.com/api.py?\n obj=books\n &fetch=one\n &action=edit\n &*name='The Wizard of Oz'\n &name='The Witch of Oz'\n```\n\nEquivalently, line 5 could be:\n\n```\n &name*='The Wizard of Oz'\n```\n\nor, in fact, any of:\n\n```\n &*name*='The Wizard of Oz'\n &***name**='The Wizard of Oz'\n &*****name****='The Wizard of Oz'\n```\n\nYou get the point.\n\n### Deleting records\n\nTo delete the book 'The Wizard of Oz', you could use:\n\n```\n http://www.yourdomain.com/api.py?\n obj=books\n &fetch=one\n &action=delete\n &name='The Wizard of Oz'\n \n```\n\n### Viewing records\n\nSimilarly, to list all books with author ID 7, you could use:\n\n```\n http://www.yourdomain.com/api.py?\n obj=books\n &fetch=all\n &action=view\n &author_id=7\n```\n\nor just:\n\n```\n http://www.yourdomain.com/api.py?\n obj=books\n &author_id=7\n```\n\nNote that:\n* POST data is also accepted, not just GET data\n * In fact, any CGI data, in general, is accepted\n* Unrecognised parameters are ignored\n \n### Working it in with jQuery\n\nAn AJAX call from jQuery (or any javascript library, really) can be setup pretty easily like so:\n\n```javascript\n $.ajax('http://www.yourdomain.com/api.py', {\n dataType: 'json',\n data: {\n obj: 'books', // the thing we want to get\n fetch: 'all', // how many we want\n action: 'view', // what we want to do with them\n \n // filtering parameters\n author_id: 7\n },\n success: function(json) {\n if (json.status == 0) {\n // success! do some stuff with the json\n $.each(json.data, function(i, e) {\n alert(\"Book #\" + i + \" is called \" + e.title + \".\");\n });\n } else {\n // error: something went wrong on the application side\n // look at the 'error' field for details\n alert(\"Error: \" + json.error);\n }\n },\n error: function() {\n // something went wrong altogether: connection issues, etc\n alert(\"Something went horribly wrong!\");\n }\n });\n \n```\n\n### Advanced options\n\nIn a real application, we often need more than just basic access to fields. What we need is a customisable interface that allows for all kinds of extensions, and doesn't break the internals. Luckily for you, SPODS is great at handling customisations.\n\n#### Custom API functions\n\nSometimes, you may want to use your JSON API for things other than directly accessing database fields. Or, you might want to write your own API functions that still have access to the data, but perform a custom action. For this, you can use **custom API functions**.\n\nIt's easy. First, define your function:\n\n```python\n >>> def credit_checksum(**kw):\n ... if 'credit_card' not in kw or not kw['credit_card'].isdigit():\n ... raise Exception(\"Please enter a valid credit card number.\")\n ... credit_sum = 0\n ... for digit in kw['credit_card']:\n ... credit_sum += int(digit)\n ... return {'sum': credit_sum}\n ... \n```\n\n(Note how the function takes *keyword arguments `**kw`*, and returns a *serialisable Python object*. This is important for the JSON conversion.)\n\nThe `**kw`* argument is a dictionary of all key-value pairs from the CGI input data (e.g. the URL). There are also some special values:\n* `'_cookie'` is the cookie object from the user (that will be sent back to the user if it is non-empty)\n* `'_classes'` is a list of all class objects that were passed into `serve_api()`\n * For example, to check if your API is serving `Person` objects, you can check `if Person in kw['_classes']`\n * Another good use of this is to _not_ serve a particular object through your API, and _force_ access to it through custom functions\n * For example, providing default API access to `User` objects (which may contain passwords and the like) could pose a security risk\n\nNow, when registering your API, simply pass in your function (as well as any classes you'd like to respond to):\n\n```python\n >>> result = spods.serve_api(Book, Author, credit_checksum)\n >>> result\n ...\n```\n\nYou can now call your function by specifying it as the `obj` parameter in the URL:\n\n```\n http://www.yourdomain.com/api.py?\n obj=credit_checksum\n &credit_card=123492384342\n```\n\nAnd the response would look like:\n\n```json\n {\"status\": 0, \"error\": \"\", \"data\": {\"sum\": 45}}\n```\n\nAny `Exception`s raised during the functions execution will be translated into error codes & error messages in the returned JSON.\n\nSimilarly, whatever is returned from your function will be saved in the `data` field of the JSON response.\n\n## That's it!\n\nYou now know all there is to know about SPODS, and integrating it with your application.\n\nGo make some cool software! :-)", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/sbermeister/SPODS/", "keywords": null, "license": "LICENSE.txt", "maintainer": null, "maintainer_email": null, "name": "SPODS", "package_url": "https://pypi.org/project/SPODS/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/SPODS/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/sbermeister/SPODS/" }, "release_url": "https://pypi.org/project/SPODS/0.4/", "requires_dist": null, "requires_python": null, "summary": "A lightweight database object serialiser for Python.", "version": "0.4" }, "last_serial": 785570, "releases": { "0.4": [ { "comment_text": "", "digests": { "md5": "bb7013d5effde4cbfac502ec885aaed2", "sha256": "950c18bc1592944079496efd394cb0208f346f9ffed90dbfa22e27061a2b1284" }, "downloads": -1, "filename": "SPODS-0.4.zip", "has_sig": false, "md5_digest": "bb7013d5effde4cbfac502ec885aaed2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11973, "upload_time": "2012-05-27T06:40:12", "url": "https://files.pythonhosted.org/packages/f7/95/10aa32829e2dd7a2fea2500ecec0334db2555b6d4500d33dc830028d0fee/SPODS-0.4.zip" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "bb7013d5effde4cbfac502ec885aaed2", "sha256": "950c18bc1592944079496efd394cb0208f346f9ffed90dbfa22e27061a2b1284" }, "downloads": -1, "filename": "SPODS-0.4.zip", "has_sig": false, "md5_digest": "bb7013d5effde4cbfac502ec885aaed2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11973, "upload_time": "2012-05-27T06:40:12", "url": "https://files.pythonhosted.org/packages/f7/95/10aa32829e2dd7a2fea2500ecec0334db2555b6d4500d33dc830028d0fee/SPODS-0.4.zip" } ] }