# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['sa2django']

package_data = \
{'': ['*']}

install_requires = \
['SQLAlchemy-Utils>=0.36.8,<0.37.0',
 'django>=3.1.1,<4.0.0',
 'psycopg2>=2.8.6,<3.0.0',
 'sqlalchemy-citext>=1.7.0,<2.0.0',
 'sqlalchemy>=1.3.19,<2.0.0']

setup_kwargs = {
    'name': 'sa2django',
    'version': '0.1.3',
    'description': 'Convert sqlalchemy database schemas to django database models at runtime.',
    'long_description': '# SqlAlchemy to Django Bridge\n\nConvert sqlalchemy database schemas to django database models at runtime.\n\nOriginal url: [https://github.com/mariushelf/sa2django](https://github.com/mariushelf/sa2django)\n\n\nThe SQLAlchemy to Django Bridge allows to specify your data models in SQLAlchemy\nand use them in Django, without manually re-specifying all your models and fields\nfor the Django ORM.\n\n\n# Why this package?\n\nSpecifying a schema in SQLAlchemy and then using it in Django sounds like... counter-\nintuitive. There are a lot of *Why nots* to answer...\n\n\n## Why not specify your models in Django straight away?\n\nSometimes some or all of the data you serve from Django is maintained or created\nby sources that are not part of the Django application. If those sources already specify\na complete SQLAlchemy model, then the SQLAlchemy to Django bridge is useful.\n\n\n## Why not simply use `inspectdb` to dynamically generate the Django model specifications?\n\n[inspectdb](https://docs.djangoproject.com/en/3.1/howto/legacy-databases/) is not\nthat dynamic after all -- it generates a Python file once, which needs to be manually\ntweaked. And each time the data model changes, you need to adjust that Python file.\n\nAlso it is often not possible to automatically derive all relations between models\nfrom the database. With third-party datasources, often relations are not manifested\nas foreign key constraints in the database schema, but just in some documentation\nthat explains the relations in human-, but not machine-readable form.\n\nIf the SQLAlchemy models already contain all those relations then it makes sense to\nconvert the SQLAlchemy models to Django ORM *at runtime*.\n\n\n# Status\n\nThe SQLAlchemy to Django Bridge works well for the author\'s use case.\n\nThere are probably a lot of corner cases and advanced (or not so advanced) features\nof SQLAlchemy that are not (yet) supported.\n\nThe tests run in sqlite, and our production system uses PostgreSQL.\nIt may or may not work with other database systems.\n\n\n# Installation\n\n`pip install sa2django`\n\n\n# Features\n\n* basic data types (int, float, string, varchar, char, date, datetime etc, bytea)\n* foreign keys and many-to-one relationships\n* many-to-many relationships including `through` tables\n* automatic inference of all declared models in a sqlalchemy `Base`\n* alternatively define your Django models as usual, but use the `SA2DModel` as\n  base class. Then all database fields are added from the corresponding sqlalchemy\n  model, but you can still add properties and functions to the Django model\n  \n# Usage\n\n## Define your SQLAlchemy schema\nSay you have the following SQLAlchemy schema:\n\n```python\n# sqlalchemy_schema.py\nfrom sqlalchemy import Column, ForeignKey, Integer, String\nfrom sqlalchemy.ext.declarative import declarative_base\nfrom sqlalchemy.orm import relationship\n\nBase = declarative_base()\n\nclass Parent(Base):\n    __tablename__ = "parent"\n    id = Column(Integer, primary_key=True)\n    name = Column(String)\n    children = relationship("Child", back_populates="parent")\n\n\nclass Child(Base):\n    __tablename__ = "child"\n    id = Column(Integer, primary_key=True)\n    name = Column(String)\n    age = Column(Integer)\n    parent_id = Column(Integer, ForeignKey("parent.id"))\n    parent = relationship(Parent, uselist=False, back_populates="children")\n```\n\n\n## Automatic inference\nYou can use it in your Django application by simply adding the following code to\nyour `models.py`:\n\n```python\n# models.py\nfrom sa2django.core import generate_sa2d_models, inject_models\nfrom sqlalchemy_models import Base  # this is what we have specified in the snippet above\n\n_models = generate_sa2d_models(Base, __name__)\ninject_models(_models, globals())\n```\n\nThis will add the classes `Child` and `Parent` to your `models` module.\nThese classes are the Django models that correspond to the SQLAlchemy models.\n\nYou can\'t see it in the file, but Django will recognize them, and you can import them\nanywhere in the application as if you had declared them manually:\n\n```python\nfrom models import Parent, Child\n\nprint(Child.objects.all())\nchild = Child.objects.get(pk=my_pk)\nprint(child.parents.all())\n```\n\n*Note:* your IDE might complain because it thinks that `Parent` and `Child` do not\nexist. It can\'t know, because the classes are created at runtime. The code will work\nfine.\n\n\n## Manual specification and custom properties\n\nA strength of Django is that it allows to specify additional properties on a model.\nsa2django supports this. To do so you can declare your models explicitly and inherit\nfrom `sa2django.SA2DModel` instead of `django.db.models.Model`, and specify the\ncorresponding SQLAlchemy model in the `Meta` class under the `sa_model` attribute.\n\nYou also need to register *all* Django models that are referenced by any model in the\nSQLAlchemy schema with `register_table()`.\n\nThen you have control over the class name and additional properties and use `sa2django`\nonly to add all fields and relations to your Django model.\n\n\nHere\'s and example:\n```python\nfrom sa2django import register_table, SA2DModel\nfrom sa2django.core import extract_tables_from_base\nimport sqlalchemy_models\n\n# extract all SQLAlchemy tables\n_tables = extract_tables_from_base(sqlalchemy_models.Base)\n\n# register the tables\nfor _tablename, _sa_class in _tables.items():\n    register_table(_tablename, _sa_class.__name__)\n\nclass Child(SA2DModel):\n    class Meta:\n        sa_model = sqlalchemy_models.Child\n        ordering = ["age"]\n    \n    @property\n    def age_next_year(self):\n        return self.age + 1\n\nclass Parent(SA2DModel):\n    class Meta:\n        sa_model = sqlalchemy_models.Parent\n```\n\nAt the moment it is not possible to mix manual and automatic model extraction --\nif you specify one model manually, you cannot use `generate_sa2d_models()` anymore,\nso you need to specify *all* models manually.\n\n\n# Limitations\n\nSQLAlchemy provides a superset of Django\'s functionality. For this reason, there\'s a\nlong list of limitations.\n\nThe list is even longer and probably not exhaustive because sa2django is a young project\ntailored to its author\'s current needs.\n\n* at the moment only declarative base definitions are supported, no pure `Mapper`\n  objects\n* composite foreign keys and primary keys are not supported. Primary keys and foreign\n  keys must contain exactly one column\n* relations that do not use a foreign key are not added to the Django models\n  \n\n## Many to many relationships\n\n* In sqlalchemy, in the mapper of the intermediate table, both foreign keys *and*\n  relationships linking to both tables must be specified.\n  \n  Example:\n  ```python\n  class CarParentAssoc(Base):\n      __tablename__ = "cartoparent"\n      id = Column(Integer, primary_key=True)\n      car = relationship("Car")\n      parent = relationship("Parent")\n      car_id = Column(Integer, ForeignKey("car.car_id"))\n      parent_id = Column(Integer, ForeignKey("parent.id"))\n  ```\n  Note that for both links to the `car` and `parent` table, both foreign keys and\n  relationship attributes are specified.\n\n\n# Changelog\n## 0.1.3\n- set arbitrary `max_length` of 2048 on String fields that do not have a defined length\n  in sqlalchemy. Necessary because Django does not support unlimited String fields, even\n  though some backends (e.g., Postgres) do\n\n\n# Contributing\n\nPull requests are more than welcome! Ideally reach out to us by creating or replying\nto a Github ticket such that we can align our work and ideas.\n\n\n# License\n\n[MIT](LICENSE)\n\n\nAuthor: Marius Helf \n  ([helfsmarius@gmail.com](mailto:helfsmarius@gmail.com))\n',
    'author': 'Marius Helf',
    'author_email': 'helfsmarius@gmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/mariushelf/sa2django',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.6,<4.0',
}


setup(**setup_kwargs)
