{ "info": { "author": "Ryan Vinzent", "author_email": "rvinzent217@hotmail.com", "bugtrack_url": null, "classifiers": [ "Framework :: Django", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.7" ], "description": "# django-dynamic-models\n\n\n## Overview\n\nDynamic Django models allow users to define, edit, and populate their own database tables and apply runtime schema changes to the database. `django-dynamic-models` is loosely based on the [runtime dynamic models](https://dynamic-models.readthedocs.io/en/latest/) talk from DjangoCon 2011. The basic concept involves around dynamic class declaration using the built-in `type` function. We use `type` to dynamically declare new Django models at runtime, and it is the goal of this project to provide a simple API to allow developers to get started with runtime dynamic models quickly.\n\nThis package provides abstract models to help Django developers quickly implement dynamic runtime models for their specific use case while the runtime schema changes and Django's model registry are handled automatically. The schema changes are applied in pure Django, *without* the migrations framework, so none of your dynamic models will affect your migrations files at all.\n\n## Installation\n\nInstall `django-dynamic-model` from PyPi with:\n\n```python\npip install django-dynamic-model\n```\n\nThen, add `'dynamic_models'` and `django.contrib.contenttypes` to `INSTALLED_APPS`.\n> **Note**: \n> \n> Django's Content Types app is currently required, although this dependency may possibly removed in the future.\n\n```python\nINSTALLED_APPS = [\n ...\n 'dynamic_models',\n 'django.contrib.conttenttypes'\n]\n```\n\n## Usage\n\nTo begin, simply subclass `AbstractModelSchema` and `AbstractFieldSchema` from `dynamic_models.models`. The abstract models will still work if no additional fields are provided. The simplest way to get started with dynamic models would be to add subclasses to your app's `models.py`, and run migrations. Then, new models can be created dynamically by creating instances of model schema and field schema models.\n\n```python\nfrom dynamic_models.models import AbstractModelSchema, AbstractFieldSchema\n\nclass ModelSchema(AbstractModelSchema):\n pass\n\nclass FieldSchema(AbstractFieldSchema):\n pass\n```\n\nNow, run the migration commands:\n```\n$ python manage.py makemigrations\n> ... making migrations ...\n\n$ python manage.py migrate\n> ... migrating ...\n```\n\n### Creating dynamic models\n\nCreating a dynamic model is as simple as creating a new instance of your concrete model schema class. `AbstractModelSchema` provides a single field. The `name` field will be used to generate the class name and the name of the database table. Once created, the `as_model` method can be used to retreive the dynamic model class.\n\n>**Note**:\n>\n>The `name` field has `unique=True` by default to help enforce unique table names generated by the `AbstracModelSchema` instance. To use a different table naming scheme, the `db_table` (and probably `model_name`) properties should be overridden. Care should be taken that it is not possible to generate the same `db_table` from different instances of `AbstractModelSchema`. \n\nThe default model_name will be `Car` and the default table_name `myapp_car` where \"myapp\" is the app label. The table name and model name are derived from the `name` value.\n\n```python\ncar_schema = ModelSchema.objects.create(name='car')\nCar = car_model_schema.as_model()\nassert issubclass(Car, models.Model)\n\n# The dynamic model can now be used to create Car instances\ninstance = Car.objects.create()\nassert instance.pk is not None\n```\n\n### Adding fields\n\nCreating field schema to add to models is quite similar to creating dynamic models. `AbstractFieldSchema` provides two fields, `name` and `data_type`, and they are responsible for naming the database column and choosing which Django field to add to the dynamic model. Fields are not attached to any dynamic model when they are created, so the same field can be applied to any number of dynamic models. The constraints applied to the field, however, are specific for each model-field pair. Currently supported data types are returned by the `get_data_types` method on subclasses of `AbstractModelSchema`. Currently, supported data types and their corresponding Django field classes are listed below:\n\n> **Note**: The value of `data_type` is not editable while data migrations are not supported.\n\n| Data Type | Django Field |\n|:---------:|:------------:|\n| character | CharField |\n| text | TextField |\n| integer | IntegerField |\n| float | FloatField |\n| boolean | BooleanField |\n| date | DateTimeField|\n\n```python\ncar_model_schema = ModelSchema.objects.create(name='car')\ncolor_field_schema = FieldSchema.objects.create(name='color', data_type='character')\n```\nThe color field is still completely independent of the Car model, and it has not been added to any database tables. Like normal CharFields, a max_length must be defined for the character data type. Add a field to a dynamic model with the `add_field` method\n\n```python\ncolor = car_model_schema.add_field(\n color_field_schema,\n null=False,\n unique=False,\n max_length=16\n)\n```\nField constraints can be added when a field schema is attached to a model schema. Now, the new field can be used as you normally would in Django. Be sure to grab\nthe lastest version of the dynamic model after changing schema or `OutdatedModelError` will be raised on save.\n\n```python\nCar = car_model_schema.as_model()\nred_car = Car.objects.create(color='red')\nassert red_car.color == 'red'\n\n# This will raise an error because the 'color' field is required\nanother_car = Car.objects.create()\n```\n\nChange the schema with 'update_field' to allow null. Null columns cannot currently be changed to not null because a default value will be required to fill the null spaces. This limitation should be removed when default values are implemented.\n\nExisting field schema can be edited or removed with the `update_field` and `remove_field` methods.\n\n```python\ncar_model_schema.update_field(color_field_schema, null=True)\ncar_model_schema.remove_field(color_field_schema)\n```\n\n## Support\n`django-dynamic-models` is tested on Python 3.6+ with Django 2.0+\n\n
\n\n# Reference\n\n## AbstractModelSchema\n\n### Fields\n\n#### `name`\n\nThe value of name deterimines the model name and table name of the dynamic model. It has the `unique` constraint applied by default, but can be removed if a custom naming scheme has been implemented to prevent duplicate table and model names.\n
\n\n### Methods\n\n#### `as_model()`\n\nReturn the dynamic model generated by the model schema instance.\n
\n\n#### `get_fields()`\n\nReturn all `ModelFieldSchema` instances attached to this model.\n
\n\n#### `get_field_for_schema(field_schema)`\n\nReturn the `ModelFieldSchema` instance for this model schema and the provided field schema arugument.\n
\n\n#### `add_field(field_schema, **options)`\n\nAdd a field to this dynamic model by creating a new instance of `ModelFieldSchema`. Takes the field schema instance as the first argument and optionally any constraints applied to this field. Valid constraints are `null`, `unique`, and `max_length` if the field's data type requires.\n
\n\n#### `update_field(field_schema, **options)`\n\nUpdate an existing field with new contraints. Currently, fields cannot be changed from `null=True` to `null=False`.\n\n
\n\n#### `remove_field(field_schema)`\n\nRemove a field from the dynamic model.\n
\n\n#### `is_current_model(model)`\n\nReturns `True` if the provided model is current with the latest schema and `False` if the schema has since changed from when the model was generated. This does not work on unsaved schema instances.\n
\n\n#### `destroy_model()`\n\nPurges the dynamic model from the app registry and the database without deleting the schema instance. There are few use cases for calling this method manually.\n
\n\n\n## AbstractFieldSchema\n\n### Fields\n\n#### `name`\n\nThe name of the field. Used to generate the database column name. Names should not start with an underscore to avoid potentially clashing with private attributes on the generated models.\n
\n\n#### `data_type`\n\nUsed to determine the Django model field to used on the dynamic model. See above for a list of valid data types.\n
\n\n### Methods\n\n#### `get_data_types()`\n\nReturn a list of available data type options.\n
\n\n#### `update_last_modified()`\n\nMarks all related model schema objects as changed, forcing the dynamic model to be redefined the next time `as_model` is called.\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": "http://github.com/rvinzent/django-dynamic-models", "keywords": "", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "django-dynamic-model", "package_url": "https://pypi.org/project/django-dynamic-model/", "platform": "", "project_url": "https://pypi.org/project/django-dynamic-model/", "project_urls": { "Homepage": "http://github.com/rvinzent/django-dynamic-models" }, "release_url": "https://pypi.org/project/django-dynamic-model/0.1.0/", "requires_dist": [ "Django (>=2.0)" ], "requires_python": "", "summary": "Allow dynamic creation and updates to database schema at runtime.", "version": "0.1.0" }, "last_serial": 4647614, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "dcc147bdcc9fa312df96a330b27adb4f", "sha256": "5ceb7da8158a321dc73e4260bca6a3c406d0af493231008a7913880735bb19fd" }, "downloads": -1, "filename": "django_dynamic_model-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "dcc147bdcc9fa312df96a330b27adb4f", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 12136, "upload_time": "2018-12-31T06:49:53", "url": "https://files.pythonhosted.org/packages/08/cc/716ba818bbd7a8beeeb3e9e5bcf71d793b4e7612b1bc5a1fa670c69e0b30/django_dynamic_model-0.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "d59cf3de0111fd2de6b909232204f1d8", "sha256": "05f1a6a3fcb57304a19803039333f8341e2911f270c75b0349373daa2cdc6e27" }, "downloads": -1, "filename": "django-dynamic-model-0.1.0.tar.gz", "has_sig": false, "md5_digest": "d59cf3de0111fd2de6b909232204f1d8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9829, "upload_time": "2018-12-31T06:49:55", "url": "https://files.pythonhosted.org/packages/10/07/6bafddc7bccf5f2588d78a4db1c038a5184acf02d2e6bd38a644ff17cdd8/django-dynamic-model-0.1.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "dcc147bdcc9fa312df96a330b27adb4f", "sha256": "5ceb7da8158a321dc73e4260bca6a3c406d0af493231008a7913880735bb19fd" }, "downloads": -1, "filename": "django_dynamic_model-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "dcc147bdcc9fa312df96a330b27adb4f", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 12136, "upload_time": "2018-12-31T06:49:53", "url": "https://files.pythonhosted.org/packages/08/cc/716ba818bbd7a8beeeb3e9e5bcf71d793b4e7612b1bc5a1fa670c69e0b30/django_dynamic_model-0.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "d59cf3de0111fd2de6b909232204f1d8", "sha256": "05f1a6a3fcb57304a19803039333f8341e2911f270c75b0349373daa2cdc6e27" }, "downloads": -1, "filename": "django-dynamic-model-0.1.0.tar.gz", "has_sig": false, "md5_digest": "d59cf3de0111fd2de6b909232204f1d8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9829, "upload_time": "2018-12-31T06:49:55", "url": "https://files.pythonhosted.org/packages/10/07/6bafddc7bccf5f2588d78a4db1c038a5184acf02d2e6bd38a644ff17cdd8/django-dynamic-model-0.1.0.tar.gz" } ] }