diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..0ec5a67 --- /dev/null +++ b/TODO.md @@ -0,0 +1,69 @@ +# TODO +remaining tasks for the `0.2` release, in order of priority + +## major +* [ ] move behaviours (getting, updating, etc.) to library methods +* [ ] validation (schema, column, relationship) +* [ ] declarative entity definitions +* [ ] schema validation +* [ ] documentation +* [ ] callbacks +* [ ] pagination +* [ ] hateoas-style support + +### move behaviours +at the moment, when you generate your code, a lot of functionality +gets put in the restplus endpoint. this results in huge diffs between +versions and is tiresome to test. + +it is proposed that this code moves into a library function which then +gets imported by the endpoint. this will make the code easier to test, +and will result in a smaller generated codebase. + +### validation +there's loads that could be done to validate a schema. the most interesting +part is probably the relationships - to do this we would need a two-pass +implementation where the first pass builds up an idea of what entities are +present and how they relate to one another, and the second pass +verifies that these relationships are possible. + +error reporting should be as approachable and helpful as possible. see +[elm](https://elm-lang.org/) for inspiration. + +### declarative entity definitions +writing entities declaratively is much more natural to python developers +already familiar with sqlalchemy, which is a dependency of genyrator. +it is proposed to use decorators in the style of +[attr.s](http://www.attrs.org/en/stable/) to configure the behaviour of +entities. + +### schema validation +we currently validate provided data with a really old version +of marhsmallow-sqlalchemy. pros: it gives nice errors and will persist +the data if it's valid. cons: it's not easily configurable from the standpoint +of someone writing an entity. explore going with a different library, writing our +own or something different? there could be the opportunity to allow the option +to specify column-level validation - how would this look? + + +### callbacks +provide a mechanism for intercepting the data at different points of processing. +perhaps as a first pass, just allow a user-provided method to be called at the start +of the endpoint method, and another before the response gets serialized? + + +### documentation + +it would be wonderful to have documentation which takes the time to explain +some of the core concepts of columns, entities and schemas, as well as +giving examples of how to configure different kinds of relationships. + +### pagination +configurable pagination on a per-endpoint basis. + +### hateoas-style support +location headers. explorable links. + +## minor + +* [ ] stop using `.format`, start using `f` strings everywhere diff --git a/bookshop/__init__.py b/bookshop/__init__.py index 02f6d8e..a5f9478 100644 --- a/bookshop/__init__.py +++ b/bookshop/__init__.py @@ -1,7 +1,7 @@ from flask_marshmallow import Marshmallow from bookshop.resources import api from bookshop.config import config -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db app = config() db.init_app(app) diff --git a/bookshop/resources/Author.py b/bookshop/resources/Author.py index bf38ea7..f9cf79b 100644 --- a/bookshop/resources/Author.py +++ b/bookshop/resources/Author.py @@ -9,7 +9,7 @@ from bookshop.core.convert_dict import ( python_dict_to_json_dict, json_dict_to_python_dict ) -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.sqlalchemy.model import Author from bookshop.sqlalchemy.convert_properties import ( convert_properties_to_sqlalchemy_properties, convert_sqlalchemy_properties_to_dict_properties diff --git a/bookshop/resources/Book.py b/bookshop/resources/Book.py index 6a9951d..cd5f84f 100644 --- a/bookshop/resources/Book.py +++ b/bookshop/resources/Book.py @@ -9,7 +9,7 @@ from bookshop.core.convert_dict import ( python_dict_to_json_dict, json_dict_to_python_dict ) -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.sqlalchemy.model import Book from bookshop.sqlalchemy.convert_properties import ( convert_properties_to_sqlalchemy_properties, convert_sqlalchemy_properties_to_dict_properties diff --git a/bookshop/resources/BookGenre.py b/bookshop/resources/BookGenre.py index eced0c4..86a61b2 100644 --- a/bookshop/resources/BookGenre.py +++ b/bookshop/resources/BookGenre.py @@ -9,7 +9,7 @@ from bookshop.core.convert_dict import ( python_dict_to_json_dict, json_dict_to_python_dict ) -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.sqlalchemy.model import BookGenre from bookshop.sqlalchemy.convert_properties import ( convert_properties_to_sqlalchemy_properties, convert_sqlalchemy_properties_to_dict_properties diff --git a/bookshop/resources/Genre.py b/bookshop/resources/Genre.py index 14f85ee..45ce3fa 100644 --- a/bookshop/resources/Genre.py +++ b/bookshop/resources/Genre.py @@ -9,7 +9,7 @@ from bookshop.core.convert_dict import ( python_dict_to_json_dict, json_dict_to_python_dict ) -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.sqlalchemy.model import Genre from bookshop.sqlalchemy.convert_properties import ( convert_properties_to_sqlalchemy_properties, convert_sqlalchemy_properties_to_dict_properties diff --git a/bookshop/resources/RelatedBook.py b/bookshop/resources/RelatedBook.py index c26d73b..e451e07 100644 --- a/bookshop/resources/RelatedBook.py +++ b/bookshop/resources/RelatedBook.py @@ -9,7 +9,7 @@ from bookshop.core.convert_dict import ( python_dict_to_json_dict, json_dict_to_python_dict ) -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.sqlalchemy.model import RelatedBook from bookshop.sqlalchemy.convert_properties import ( convert_properties_to_sqlalchemy_properties, convert_sqlalchemy_properties_to_dict_properties diff --git a/bookshop/resources/Review.py b/bookshop/resources/Review.py index 2699119..22959bc 100644 --- a/bookshop/resources/Review.py +++ b/bookshop/resources/Review.py @@ -9,7 +9,7 @@ from bookshop.core.convert_dict import ( python_dict_to_json_dict, json_dict_to_python_dict ) -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.sqlalchemy.model import Review from bookshop.sqlalchemy.convert_properties import ( convert_properties_to_sqlalchemy_properties, convert_sqlalchemy_properties_to_dict_properties diff --git a/bookshop/sqlalchemy/convert_dict_to_marshmallow_result.py b/bookshop/sqlalchemy/convert_dict_to_marshmallow_result.py index 18cf42d..2a4745d 100644 --- a/bookshop/sqlalchemy/convert_dict_to_marshmallow_result.py +++ b/bookshop/sqlalchemy/convert_dict_to_marshmallow_result.py @@ -4,7 +4,7 @@ from sqlalchemy.ext.declarative import DeclarativeMeta from sqlalchemy.orm import noload -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.core.convert_dict import json_dict_to_python_dict from bookshop.domain.types import DomainModel, UserJson from bookshop.sqlalchemy.convert_properties import convert_properties_to_sqlalchemy_properties diff --git a/bookshop/sqlalchemy/fixture/Author.py b/bookshop/sqlalchemy/fixture/Author.py index ec17e11..687837f 100644 --- a/bookshop/sqlalchemy/fixture/Author.py +++ b/bookshop/sqlalchemy/fixture/Author.py @@ -1,6 +1,6 @@ import factory -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.sqlalchemy.model.Author import Author diff --git a/bookshop/sqlalchemy/fixture/Book.py b/bookshop/sqlalchemy/fixture/Book.py index f3855e7..2acdb85 100644 --- a/bookshop/sqlalchemy/fixture/Book.py +++ b/bookshop/sqlalchemy/fixture/Book.py @@ -1,6 +1,6 @@ import factory -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.sqlalchemy.model.Book import Book diff --git a/bookshop/sqlalchemy/fixture/BookGenre.py b/bookshop/sqlalchemy/fixture/BookGenre.py index 4c04994..4862c15 100644 --- a/bookshop/sqlalchemy/fixture/BookGenre.py +++ b/bookshop/sqlalchemy/fixture/BookGenre.py @@ -1,6 +1,6 @@ import factory -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.sqlalchemy.model.BookGenre import BookGenre diff --git a/bookshop/sqlalchemy/fixture/Genre.py b/bookshop/sqlalchemy/fixture/Genre.py index 3452fa7..574833d 100644 --- a/bookshop/sqlalchemy/fixture/Genre.py +++ b/bookshop/sqlalchemy/fixture/Genre.py @@ -1,6 +1,6 @@ import factory -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.sqlalchemy.model.Genre import Genre diff --git a/bookshop/sqlalchemy/fixture/RelatedBook.py b/bookshop/sqlalchemy/fixture/RelatedBook.py index 9f6c0f7..e0cb3f6 100644 --- a/bookshop/sqlalchemy/fixture/RelatedBook.py +++ b/bookshop/sqlalchemy/fixture/RelatedBook.py @@ -1,6 +1,6 @@ import factory -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.sqlalchemy.model.RelatedBook import RelatedBook diff --git a/bookshop/sqlalchemy/fixture/Review.py b/bookshop/sqlalchemy/fixture/Review.py index abacd37..bd10557 100644 --- a/bookshop/sqlalchemy/fixture/Review.py +++ b/bookshop/sqlalchemy/fixture/Review.py @@ -1,6 +1,6 @@ import factory -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.sqlalchemy.model.Review import Review diff --git a/bookshop/sqlalchemy/model/Author.py b/bookshop/sqlalchemy/model/Author.py index 65cf674..30814fb 100644 --- a/bookshop/sqlalchemy/model/Author.py +++ b/bookshop/sqlalchemy/model/Author.py @@ -2,7 +2,7 @@ from sqlalchemy import UniqueConstraint from sqlalchemy.types import JSON as JSONType -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.sqlalchemy.model.types import BigIntegerVariantType diff --git a/bookshop/sqlalchemy/model/Book.py b/bookshop/sqlalchemy/model/Book.py index b5cf7c1..c290701 100644 --- a/bookshop/sqlalchemy/model/Book.py +++ b/bookshop/sqlalchemy/model/Book.py @@ -2,7 +2,7 @@ from sqlalchemy import UniqueConstraint from sqlalchemy.types import JSON as JSONType -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.sqlalchemy.model.types import BigIntegerVariantType diff --git a/bookshop/sqlalchemy/model/BookGenre.py b/bookshop/sqlalchemy/model/BookGenre.py index 5699a9b..99f6c0c 100644 --- a/bookshop/sqlalchemy/model/BookGenre.py +++ b/bookshop/sqlalchemy/model/BookGenre.py @@ -2,7 +2,7 @@ from sqlalchemy import UniqueConstraint from sqlalchemy.types import JSON as JSONType -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.sqlalchemy.model.types import BigIntegerVariantType diff --git a/bookshop/sqlalchemy/model/Genre.py b/bookshop/sqlalchemy/model/Genre.py index 9c70394..96f6a7e 100644 --- a/bookshop/sqlalchemy/model/Genre.py +++ b/bookshop/sqlalchemy/model/Genre.py @@ -2,7 +2,7 @@ from sqlalchemy import UniqueConstraint from sqlalchemy.types import JSON as JSONType -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.sqlalchemy.model.types import BigIntegerVariantType diff --git a/bookshop/sqlalchemy/model/RelatedBook.py b/bookshop/sqlalchemy/model/RelatedBook.py index 9b18dd1..2f098b5 100644 --- a/bookshop/sqlalchemy/model/RelatedBook.py +++ b/bookshop/sqlalchemy/model/RelatedBook.py @@ -2,7 +2,7 @@ from sqlalchemy import UniqueConstraint from sqlalchemy.types import JSON as JSONType -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.sqlalchemy.model.types import BigIntegerVariantType diff --git a/bookshop/sqlalchemy/model/Review.py b/bookshop/sqlalchemy/model/Review.py index 2e37ea8..285c64e 100644 --- a/bookshop/sqlalchemy/model/Review.py +++ b/bookshop/sqlalchemy/model/Review.py @@ -2,7 +2,7 @@ from sqlalchemy import UniqueConstraint from sqlalchemy.types import JSON as JSONType -from bookshop.sqlalchemy import db +from bookshop.sqlalchemy import db as db from bookshop.sqlalchemy.model.types import BigIntegerVariantType diff --git a/genyrator/behaviour/__init__.py b/genyrator/behaviour/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/genyrator/behaviour/sqlalchemy/__init__.py b/genyrator/behaviour/sqlalchemy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/genyrator/behaviour/sqlalchemy/db.py b/genyrator/behaviour/sqlalchemy/db.py new file mode 100644 index 0000000..7560562 --- /dev/null +++ b/genyrator/behaviour/sqlalchemy/db.py @@ -0,0 +1,22 @@ +from functools import lru_cache + +from flask_sqlalchemy import SQLAlchemy + + +class DBNotInitialisedException(Exception): + message = "It looks like you're trying to use genyrator without having " + "initialised the db object first! Call `init_genyrator` with your SQLAlchemy " + "db instance before trying to use any of the generated code." + + +def init_genyrator( + sqlalchemy_instance: SQLAlchemy, +): + get_db_instance._db_instance = sqlalchemy_instance + + +def get_db_instance(): + if not hasattr(get_db_instance, '_db_instance'): + raise DBNotInitialisedException(DBNotInitialisedException.message) + else: + return get_db_instance._db_instance diff --git a/genyrator/entities/File.py b/genyrator/entities/File.py index 7a4cb86..c5810ec 100644 --- a/genyrator/entities/File.py +++ b/genyrator/entities/File.py @@ -45,10 +45,10 @@ def create_files_from_template_config( def create_file_from_template(file_path: List[str], template: Template) -> File: if template.out_path: file_path = file_path + template.out_path[0] - file_name = '{}.py'.format(template.out_path[1]) + file_name = f'{template.out_path[1]}.py' else: file_path = file_path + template.relative_path - file_name = '{}.py'.format(template.template_name) + file_name = f'{template.template_name}.py' return File( file_name=file_name, file_path=file_path, diff --git a/genyrator/entities/Schema.py b/genyrator/entities/Schema.py index ef2b7de..e29b3f3 100644 --- a/genyrator/entities/Schema.py +++ b/genyrator/entities/Schema.py @@ -1,20 +1,19 @@ -from typing import List, Optional +from typing import List, Optional, NamedTuple, Tuple import attr from genyrator.entities.Entity import Entity from genyrator.entities.File import create_files_from_template_config, FileList -from genyrator.entities.Template import Template -from genyrator.template_config import create_template_config +from genyrator.template_config import create_template_config, TemplateConfig @attr.s class Schema(object): - module_name: str = attr.ib() - entities: List[Entity] = attr.ib() - templates: List[Template] = attr.ib() - files: FileList = attr.ib() - api_name: str = attr.ib() - api_description: str = attr.ib() + module_name: str = attr.ib() + entities: List[Entity] = attr.ib() + templates: TemplateConfig = attr.ib() + files: FileList = attr.ib() + api_name: str = attr.ib() + api_description: str = attr.ib() def write_files(self) -> None: for file_list in self.files: @@ -48,18 +47,35 @@ def write_domain_models(self): def create_schema( module_name: str, entities: List[Entity], - db_import_path: Optional[str] = None, + db_import: Optional[str] = None, api_name: Optional[str] = None, api_description: Optional[str] = None, file_path: Optional[List[str]] = None, ) -> Schema: - db_import_path = db_import_path if db_import_path else '{}.sqlalchemy'.format(module_name) + """Create a Genyrator Schema. + + A Schema represents a collection of entities, as well as details on how to + write the generated code. + + Args: + module_name: The name used for the generated module. Gets used in import statements + entities: A list of Entities to include in the Schema + db_import: A string containing a statement which, when evaluated, will import an SQLAlchemy + db object. Defaults to "from {module_name}.sqlalchemy import db". You should + make this point to your db object if you wish to use a non-generated db + initialization. + api_name: Used in the generation of the RESTPLUS API. Shows up in the generated Swagger + api_description: Used in the generated RESTPLUS API + file_path: The path to where to write the files to. Defaults to the module name - if + the module_name is "bookshop", the app will be written to "bookshop/" + """ + db_import = db_import if db_import else f'from {module_name}.sqlalchemy import db' file_path = file_path if file_path else [module_name] api_name = api_name if api_name else module_name api_description = api_description if api_description else '' template_config = create_template_config( module_name=module_name, - db_import_path=db_import_path, + db_import_statement=db_import, entities=entities, api_name=api_name, api_description=api_description, diff --git a/genyrator/entities/Template.py b/genyrator/entities/Template.py index d9e62b2..05c6c9f 100644 --- a/genyrator/entities/Template.py +++ b/genyrator/entities/Template.py @@ -52,8 +52,8 @@ def create_template( @attr.s class RootInit(Template): - db_import_path: str = attr.ib() - module_name: str = attr.ib() + db_import_statement: str = attr.ib() + module_name: str = attr.ib() @attr.s @@ -69,9 +69,9 @@ class ConvertDict(Template): @attr.s class SQLAlchemyModel(Template): - module_name: str = attr.ib() - db_import_path: str = attr.ib() - entity: Entity = attr.ib() + module_name: str = attr.ib() + db_import_statement: str = attr.ib() + entity: Entity = attr.ib() @attr.s @@ -86,8 +86,8 @@ class Config(Template): @attr.s class SQLAlchemyModelInit(Template): - module_name: str = attr.ib() - db_import_path: str = attr.ib() + module_name: str = attr.ib() + db_import_statement: str = attr.ib() imports: List[Import] = attr.ib() @@ -98,11 +98,11 @@ class RestplusModel(Template): @attr.s class Resource(Template): - module_name: str = attr.ib() - db_import_path: str = attr.ib() - entity: Entity = attr.ib() - restplus_template: str = attr.ib() - TypeOption: Type = attr.ib() + module_name: str = attr.ib() + db_import_statement: str = attr.ib() + entity: Entity = attr.ib() + restplus_template: str = attr.ib() + TypeOption: Type = attr.ib() @attr.s @@ -142,12 +142,12 @@ class JoinEntities(Template): @attr.s class ConvertDictToMarshmallow(Template): - module_name: str = attr.ib() - db_import_path: str = attr.ib() + module_name: str = attr.ib() + db_import_statement: str = attr.ib() @attr.s class Fixture(Template): - db_import_path: str = attr.ib() - module_name: str = attr.ib() - entity: Entity = attr.ib() + db_import_statement: str = attr.ib() + module_name: str = attr.ib() + entity: Entity = attr.ib() diff --git a/genyrator/template_config.py b/genyrator/template_config.py index d4cc26a..4a00773 100644 --- a/genyrator/template_config.py +++ b/genyrator/template_config.py @@ -15,15 +15,15 @@ def create_template_config( - module_name: str, - db_import_path: str, - entities: List[Entity], - api_name: str, - api_description: str, + module_name: str, + db_import_statement: str, + entities: List[Entity], + api_name: str, + api_description: str, ) -> TemplateConfig: root_files = [ create_template( - Template.RootInit, ['__init__'], module_name=module_name, db_import_path=db_import_path, + Template.RootInit, ['__init__'], module_name=module_name, db_import_statement=db_import_statement, ), create_template(Template.Config, ['config'], module_name=module_name), ] @@ -37,11 +37,11 @@ def create_template_config( db_models = [ *[create_template( Template.SQLAlchemyModel, ['sqlalchemy', 'model', 'sqlalchemy_model'], - db_import_path=db_import_path, entity=e, module_name=module_name, + db_import_statement=db_import_statement, entity=e, module_name=module_name, out_path=Template.OutPath((['sqlalchemy', 'model'], e.class_name)) ) for e in entities], create_template( - Template.SQLAlchemyModelInit, ['sqlalchemy', 'model', '__init__'], db_import_path=db_import_path, + Template.SQLAlchemyModelInit, ['sqlalchemy', 'model', '__init__'], db_import_statement=db_import_statement, imports=[Template.Import(e.class_name, [e.class_name]) for e in entities], module_name=module_name, ), create_template(Template.ModelToDict, ['sqlalchemy', 'model_to_dict'], module_name=module_name), @@ -52,14 +52,14 @@ def create_template_config( create_template( Template.ConvertDictToMarshmallow, ['sqlalchemy', 'convert_dict_to_marshmallow_result'], - module_name=module_name, db_import_path=db_import_path, + module_name=module_name, db_import_statement=db_import_statement, ), ] fixtures = [ create_template(Template.Template, ['sqlalchemy', 'fixture', '__init__']), *[create_template( Template.Fixture, ['sqlalchemy', 'fixture', 'fixture'], module_name=module_name, - db_import_path=db_import_path, out_path=Template.OutPath((['sqlalchemy', 'fixture'], entity.class_name)), + db_import_statement=db_import_statement, out_path=Template.OutPath((['sqlalchemy', 'fixture'], entity.class_name)), entity=entity, ) for entity in entities] ] @@ -77,7 +77,7 @@ def create_template_config( *[create_template( Template.Resource, ['resources', 'resource'], entity=entity, out_path=Template.OutPath((['resources'], entity.class_name)), - db_import_path=db_import_path, module_name=module_name, + db_import_statement=db_import_statement, module_name=module_name, restplus_template=create_template( Template.RestplusModel, ['resources', 'restplus_model'], entity=entity ).render(), diff --git a/genyrator/templates/__init__.j2 b/genyrator/templates/__init__.j2 index 1cd5455..6f7b96b 100644 --- a/genyrator/templates/__init__.j2 +++ b/genyrator/templates/__init__.j2 @@ -1,7 +1,7 @@ from flask_marshmallow import Marshmallow from {{ template.module_name }}.resources import api from {{ template.module_name }}.config import config -from {{ template.db_import_path }} import db +{{ template.db_import_statement }} app = config() db.init_app(app) diff --git a/genyrator/templates/resources/resource.j2 b/genyrator/templates/resources/resource.j2 index e2e94d4..e87fa49 100644 --- a/genyrator/templates/resources/resource.j2 +++ b/genyrator/templates/resources/resource.j2 @@ -24,7 +24,7 @@ from sqlalchemy.orm import noload from {{ template.module_name }}.core.convert_dict import ( python_dict_to_json_dict, json_dict_to_python_dict ) -from {{ template.db_import_path }} import db +{{ template.db_import_statement }} {{ model_import }} from {{ template.module_name }}.sqlalchemy.convert_properties import ( convert_properties_to_sqlalchemy_properties, convert_sqlalchemy_properties_to_dict_properties diff --git a/genyrator/templates/sqlalchemy/convert_dict_to_marshmallow_result.j2 b/genyrator/templates/sqlalchemy/convert_dict_to_marshmallow_result.j2 index 3ffd492..814537f 100644 --- a/genyrator/templates/sqlalchemy/convert_dict_to_marshmallow_result.j2 +++ b/genyrator/templates/sqlalchemy/convert_dict_to_marshmallow_result.j2 @@ -4,7 +4,7 @@ from flask_marshmallow.sqla import ModelSchema from sqlalchemy.ext.declarative import DeclarativeMeta from sqlalchemy.orm import noload -from {{ template.db_import_path }} import db +{{ template.db_import_statement }} from {{ template.module_name }}.core.convert_dict import json_dict_to_python_dict from {{ template.module_name }}.domain.types import DomainModel, UserJson from {{ template.module_name }}.sqlalchemy.convert_properties import convert_properties_to_sqlalchemy_properties diff --git a/genyrator/templates/sqlalchemy/fixture/fixture.j2 b/genyrator/templates/sqlalchemy/fixture/fixture.j2 index 69acac1..6e0f778 100644 --- a/genyrator/templates/sqlalchemy/fixture/fixture.j2 +++ b/genyrator/templates/sqlalchemy/fixture/fixture.j2 @@ -1,7 +1,7 @@ {%- set entity = template.entity -%} import factory -from {{ template.db_import_path }} import db +{{ template.db_import_statement }} from {{ template.module_name }}.sqlalchemy.model.{{ entity.class_name }} import {{ entity.class_name }} diff --git a/genyrator/templates/sqlalchemy/model/sqlalchemy_model.j2 b/genyrator/templates/sqlalchemy/model/sqlalchemy_model.j2 index 74165c7..914bb98 100644 --- a/genyrator/templates/sqlalchemy/model/sqlalchemy_model.j2 +++ b/genyrator/templates/sqlalchemy/model/sqlalchemy_model.j2 @@ -2,7 +2,7 @@ from sqlalchemy_utils import UUIDType from sqlalchemy import UniqueConstraint from sqlalchemy.types import JSON as JSONType -from {{ template.db_import_path }} import db +{{ template.db_import_statement }} from {{ template.module_name }}.sqlalchemy.model.types import BigIntegerVariantType {%- macro pad(name) -%} diff --git a/requirements.txt b/requirements.txt index 86f19fa..6ad73cb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -29,7 +29,7 @@ pyhamcrest==1.9.0 python-dateutil==2.7.3 pytz==2018.5 requests-toolbelt==0.8.0 -requests==2.19.1 +requests==2.20.0 six==1.11.0 sqlalchemy-utils==0.33.3 sqlalchemy==1.2.10 diff --git a/test/spec/genyrator/behaviour/sqlalchemy/db_spec.py b/test/spec/genyrator/behaviour/sqlalchemy/db_spec.py new file mode 100644 index 0000000..922eeb3 --- /dev/null +++ b/test/spec/genyrator/behaviour/sqlalchemy/db_spec.py @@ -0,0 +1,25 @@ +from expects import be_a, expect, be_none +from flask_sqlalchemy import SQLAlchemy +from mamba import description, it + +from genyrator.behaviour.sqlalchemy.db import ( + init_genyrator, get_db_instance, + DBNotInitialisedException) + +from bookshop.sqlalchemy import db as bookshop_db + + +with description('using the db instance'): + with it('throws if the db has not been initialized'): + exception = None + try: + instance = get_db_instance() + except DBNotInitialisedException as e: + exception = e + expect(exception).to_not(be_none) + + + with it('provides access to the db instance'): + init_genyrator(sqlalchemy_instance=bookshop_db) + instance = get_db_instance() + expect(instance).to(be_a(SQLAlchemy)) diff --git a/test/spec/genyrator/entities/Schema_spec.py b/test/spec/genyrator/entities/Schema_spec.py new file mode 100644 index 0000000..8039934 --- /dev/null +++ b/test/spec/genyrator/entities/Schema_spec.py @@ -0,0 +1,15 @@ +from expects import expect, equal +from mamba import description, it + +from genyrator import create_schema + +with description('create_schema'): + with it('converts a DBImport to a import string'): + schema = create_schema( + module_name='module_name', + entities=[], + db_import='from db_module import db_variable as db', + ) + + expect(schema.templates.root_files[0].db_import_statement)\ + .to(equal('from db_module import db_variable as db'))