{"id":17034448,"url":"https://github.com/luckydonald/pony_up","last_synced_at":"2025-04-12T13:02:01.798Z","repository":{"id":68105101,"uuid":"89294562","full_name":"luckydonald/pony_up","owner":"luckydonald","description":"Migrations for ponyorm","archived":false,"fork":false,"pushed_at":"2022-12-22T04:32:02.000Z","size":46,"stargazers_count":24,"open_issues_count":2,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-26T07:42:24.768Z","etag":null,"topics":["migration","migrations","orm","pony","pony-up","ponyorm","ponyup","postgres","python","sql"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/luckydonald.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-04-24T23:00:55.000Z","updated_at":"2024-12-13T08:20:52.000Z","dependencies_parsed_at":null,"dependency_job_id":"f2b87d8f-921a-42a9-8149-941c38d22cd1","html_url":"https://github.com/luckydonald/pony_up","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luckydonald%2Fpony_up","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luckydonald%2Fpony_up/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luckydonald%2Fpony_up/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luckydonald%2Fpony_up/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/luckydonald","download_url":"https://codeload.github.com/luckydonald/pony_up/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248571920,"owners_count":21126522,"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":["migration","migrations","orm","pony","pony-up","ponyorm","ponyup","postgres","python","sql"],"created_at":"2024-10-14T08:43:40.946Z","updated_at":"2025-04-12T13:02:01.720Z","avatar_url":"https://github.com/luckydonald.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# PonyUp!\n###### Version `0.1.1`\nMigrations for ponyorm\n\n## Very simple migrations.\n\n### Install\n\nThis still a first beta version, but you can test it already with the following shell command:\n```sh\n$ pip install pony_up\n```\n\n### Getting started\n\n```python\nfrom pony_up import migrate\n\n# to be able to bind the database with your information,\n# just create a function for it:\ndef bind_func(db):\n    db.bind('database type', host='localhost', user='root', passwd='1234secure', db='test1')\n    # https://docs.ponyorm.com/api_reference.html#Database.bind\n    db.generate_mapping(create_tables=True)\n    # https://docs.ponyorm.com/api_reference.html#Database.generate_mapping\n# end def\n\n\ndb = migrate(bind_func, folder_path=\"examples/migrations, python_import=\"examples.migrations\")\n```\n\u003e In case the import in line 1 does not work, see [#1](https://github.com/luckydonald/pony_up/issues/1#issuecomment-448175018) for a workaround.\n\nThe updates are applied as soon as `migrate` is called. It will return `db`, being the latest schema.\n\n### Your File schema\n- `migrations/`: The migrations are in here\n    - `v{number}/`: for example \"v0\"\n        - `__init__.py`: needs to import the model and also migrate if present.\n        - `model.py`: model of version `{number}`\n        - `migrate.py`: the script which updates from the model in this folder (`{number}`) to the next version (`{number+1}`).\n    - `v{number}.py`:    \n        A file is possible too, if it has the attribute `model` with a function `register_database` (calling `model.register_database(db)`)    \n        and optionally a `migrate` attribute with function `do_update` (it will call `migrate.do_update(db)`).\n        \n    However use only one of those, either the folder or the single file.\n\n### The required functions\n\n###### `model.py`\n\n```python\ndef register_database(db):\n```         \n\u003e In this function should be your `orm.Entity` subclasses.\n\nArguments:\n- `db` - The database to register entities to.\n\n###### `migrate.py`\n\n```python\ndef do_update(db, old_db=None):\n```\n\u003e Here you write code which changes stuff in the database to get from the current version/folder to the next one.\n \nArguments:\n- `db` - The latest schema. \n- `old_db` - This can have 3 different types:   \n    - `pony.orm.Database` A database schema of the previous migration step (Would be **v0** if we are at **v1**. See _Fig 1_),    \n    - `True` if the provided `db` database is old, and this version didn't introduced a new schema. (See **v2** in _Fig 1_)    \n    - `None` if there was no previous step (The first migration, e.g. **v0**)    \n\n\n\n### Info graphic\n![migrations](https://cloud.githubusercontent.com/assets/2737108/25397889/3a75eca2-29ea-11e7-9527-0bb3cc1412ef.png)    \n_Fig 1. Migrations_\n\n### FAQ\n#### How to use\n\u003e See above, or have a look at the example folder.\n\n#### Can I contribute?\n\u003e Please do!    \n\u003e Report issues, suggest features, or even submit code!\n\n#### I don't like using `db.{EntityName}`.\nBefore I have used the file `database.py`, to include all my objects,\nand still like to use the existing import statements. I imported it like the following:\n```python\nfrom database import {EntityName}\n```\nor I have imported all the database entities with the wildcard import like:\n```python\nfrom database import *\n```\n\n\u003e You should move the entity definitions in `database.py` into a migrations step (`v0.model` perhaps),\n\u003e and replace the file content with `db = migrate(...)`, like seen above.    \n\u003e Now you can add the following lines after said `db = migrate(...)` part:    \n\u003e ```python\n\u003e # register the tables to this module\n\u003e __all__ = [\"db\"]\n\u003e for t_name, t_clazz in db.entities.items():\n\u003e     globals()[t_name] = t_clazz\n\u003e     __all__.append(t_name)\n\u003e # end for\n\u003e ```\n\n#### My application with the migration will run multible times at the same time.\n\u003e You need to deploy some sort of locking, because else two clients trying to modify the same tables would end in a disaster.    \n\u003e If you use postgres, you can use [Advisory Locks](https://www.postgresql.org/docs/9.1/static/explicit-locking.html#ADVISORY-LOCKS). (Also see this [blog post with examples](https://hashrocket.com/blog/posts/advisory-locks-in-postgres)).    \n\u003e Request a lock before the `db = migrate(...)`, and release it afterwards:\n\u003e ```python\n\u003e import psycopg2\n\u003e con = psycopg2.connect(host=POSTGRES_HOST, user=POSTGRES_USER, password=POSTGRES_PASSWORD, database=POSTGRES_DB)\n\u003e cur = con.cursor()\n\u003e # requesting database update lock\n\u003e cur.execute(\"SELECT pg_advisory_lock(85,80);\")  # update lock (ascii: 85,80 = UP)\n\u003e \n\u003e\n\u003e # run the migration\n\u003e db = migrate(...)\n\u003e \n\u003e \n\u003e # releasing lock after database update\n\u003e cur.execute(\"SELECT pg_advisory_unlock(85,80);\")  # update lock (ascii: 85,80 = UP)\n\u003e res = cur.fetchone()\n\u003e if not isinstance(res[0], bool) or not res[0]:\n\u003e     # True = success\n\u003e     # Fail =\u003e false or no bool\n\u003e     raise ValueError(\"Could not release update lock, lock was not held (Advisory Lock 85,80)\")\n\u003e # end if\n\u003e ```\n  \n#### I like the script above, but it should just terminate instead of waiting,\n\n\u003e Replace the `cur.execute(\"SELECT pg_advisory_lock(85,80);\")` part above with: \n\u003e ```python\n\u003e # requesting database update lock\n\u003e cur.execute(\"SELECT pg_try_advisory_lock(85,80);\")  # update lock (ascii: 85,80 = UP)\n\u003e res = cur.fetchone()\n\u003e if not isinstance(res[0], bool) or not res[0]:\n\u003e     # True = success\n\u003e     # Fail =\u003e false or no bool\n\u003e     raise ValueError(\"Currently already upgrading. (Advisory Lock 85,80)\")\n\u003e # end if\n\u003e ```\n\u003e With that your script will raise an exception (and probably terminate) if the database is already being upgraded somewhere else.    \n\u003e Note: in a webserver (flask, django, ...) environment this is probably not wanted.\n\u003e Like, a Nginx server would keep running, and uWSGI would spam the log with `no python application found, check your startup logs for errors`.\n\n#### Where does the name come from?\n\u003e Because of the library `Pony ORM`, and this tool doing `updates`!    \n\u003e Got it? Yeah, what a sick joke! Tell your Grandma, too!\n\u003e Also there is the verb `to pony up`, which is not really related.\n\n#### Who is best pony?\n\u003e Definitely **Littlepip**! (see [Fallout: Equestria](http://falloutequestria.wikia.com/wiki/Fallout:_Equestria))\n\n#### Why is this FAQ getting stupid now?\n\u003e lel.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluckydonald%2Fpony_up","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fluckydonald%2Fpony_up","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluckydonald%2Fpony_up/lists"}