diff --git a/README.md b/README.md
index 1aacaab1da..bca741b652 100644
--- a/README.md
+++ b/README.md
@@ -148,6 +148,17 @@ After using this generator, your new project (the directory created) will contai
### Next release
+* PR #14:
+ * Update CRUD utils to use types better.
+ * Simplify Pydantic model names, from `UserInCreate` to `UserCreate`, etc.
+ * Upgrade packages.
+ * Add new generic "Items" models, crud utils, endpoints, and tests. To facilitate re-using them to create new functionality. As they are simple and generic (not like Users), it's easier to copy-paste and adapt them to each use case.
+ * Update endpoints/*path operations* to simplify code and use new utilities, prefix and tags in `include_router`.
+ * Update testing utils.
+ * Update linting rules, relax vulture to reduce false positives.
+ * Update migrations to include new Items.
+ * Update project README.md with tips about how to start with backend.
+
* Upgrade Python to 3.7 as Celery is now compatible too. PR #10 by @ebreton.
### 0.2.2
diff --git a/{{cookiecutter.project_slug}}/README.md b/{{cookiecutter.project_slug}}/README.md
index cfa8cbffb0..bc75538feb 100644
--- a/{{cookiecutter.project_slug}}/README.md
+++ b/{{cookiecutter.project_slug}}/README.md
@@ -53,7 +53,9 @@ If your Docker is not running in `localhost` (the URLs above wouldn't work) chec
### General workflow
-Add and modify SQLAlchemy models in `./backend/app/app/db_models/`, Pydantic models in `./backend/app/app/models` and API endpoints in `./backend/app/app/api/`.
+Open your editor at `./backend/app/` (instead of the project root: `./`), so that you see an `./app/` directory with your code inside. That way, your editor will be able to find all the imports, etc.
+
+Modify or add SQLAlchemy models in `./backend/app/app/db_models/`, Pydantic models in `./backend/app/app/models/`, API endpoints in `./backend/app/app/api/`, CRUD (Create, Read, Update, Delete) utils in `./backend/app/app/crud/`. The easiest might be to copy the ones for Items (models, endpoints, and CRUD utils) and update them to your needs.
Add and modify tasks to the Celery worker in `./backend/app/app/worker.py`.
diff --git a/{{cookiecutter.project_slug}}/backend/app/Pipfile b/{{cookiecutter.project_slug}}/backend/app/Pipfile
index 6dd0374e0f..23090edabf 100644
--- a/{{cookiecutter.project_slug}}/backend/app/Pipfile
+++ b/{{cookiecutter.project_slug}}/backend/app/Pipfile
@@ -20,7 +20,7 @@ pyjwt = "*"
python-multipart = "*"
email-validator = "*"
requests = "*"
-celery = "~=4.3"
+celery = "*"
passlib = {extras = ["bcrypt"],version = "*"}
tenacity = "*"
pydantic = "*"
diff --git a/{{cookiecutter.project_slug}}/backend/app/Pipfile.lock b/{{cookiecutter.project_slug}}/backend/app/Pipfile.lock
deleted file mode 100644
index 3358988a0b..0000000000
--- a/{{cookiecutter.project_slug}}/backend/app/Pipfile.lock
+++ /dev/null
@@ -1,1022 +0,0 @@
-{
- "_meta": {
- "hash": {
- "sha256": "9e6b6eaf001ef1b6097d2ecccae8151ade81f5c4ac0f02791ec2248008ddcddf"
- },
- "pipfile-spec": 6,
- "requires": {
- "python_version": "3.6"
- },
- "sources": [
- {
- "name": "pypi",
- "url": "https://pypi.org/simple",
- "verify_ssl": true
- }
- ]
- },
- "default": {
- "alembic": {
- "hashes": [
- "sha256:16505782b229007ae905ef9e0ae6e880fddafa406f086ac7d442c1aaf712f8c2"
- ],
- "index": "pypi",
- "version": "==1.0.7"
- },
- "amqp": {
- "hashes": [
- "sha256:16056c952e8029ce8db097edf0d7c2fe2ba9de15d30ba08aee2c5221273d8e23",
- "sha256:6816eed27521293ee03aa9ace300a07215b11fee4e845588a9b863a7ba30addb"
- ],
- "version": "==2.4.1"
- },
- "bcrypt": {
- "hashes": [
- "sha256:0ba875eb67b011add6d8c5b76afbd92166e98b1f1efab9433d5dc0fafc76e203",
- "sha256:21ed446054c93e209434148ef0b362432bb82bbdaf7beef70a32c221f3e33d1c",
- "sha256:28a0459381a8021f57230954b9e9a65bb5e3d569d2c253c5cac6cb181d71cf23",
- "sha256:2aed3091eb6f51c26b7c2fad08d6620d1c35839e7a362f706015b41bd991125e",
- "sha256:2fa5d1e438958ea90eaedbf8082c2ceb1a684b4f6c75a3800c6ec1e18ebef96f",
- "sha256:3a73f45484e9874252002793518da060fb11eaa76c30713faa12115db17d1430",
- "sha256:3e489787638a36bb466cd66780e15715494b6d6905ffdbaede94440d6d8e7dba",
- "sha256:44636759d222baa62806bbceb20e96f75a015a6381690d1bc2eda91c01ec02ea",
- "sha256:678c21b2fecaa72a1eded0cf12351b153615520637efcadc09ecf81b871f1596",
- "sha256:75460c2c3786977ea9768d6c9d8957ba31b5fbeb0aae67a5c0e96aab4155f18c",
- "sha256:8ac06fb3e6aacb0a95b56eba735c0b64df49651c6ceb1ad1cf01ba75070d567f",
- "sha256:8fdced50a8b646fff8fa0e4b1c5fd940ecc844b43d1da5a980cb07f2d1b1132f",
- "sha256:9b2c5b640a2da533b0ab5f148d87fb9989bf9bcb2e61eea6a729102a6d36aef9",
- "sha256:a9083e7fa9adb1a4de5ac15f9097eb15b04e2c8f97618f1b881af40abce382e1",
- "sha256:b7e3948b8b1a81c5a99d41da5fb2dc03ddb93b5f96fcd3fd27e643f91efa33e1",
- "sha256:b998b8ca979d906085f6a5d84f7b5459e5e94a13fc27c28a3514437013b6c2f6",
- "sha256:dd08c50bc6f7be69cd7ba0769acca28c846ec46b7a8ddc2acf4b9ac6f8a7457e",
- "sha256:de5badee458544ab8125e63e39afeedfcf3aef6a6e2282ac159c95ae7472d773",
- "sha256:ede2a87333d24f55a4a7338a6ccdccf3eaa9bed081d1737e0db4dbd1a4f7e6b6"
- ],
- "version": "==3.1.6"
- },
- "billiard": {
- "hashes": [
- "sha256:42d9a227401ac4fba892918bba0a0c409def5435c4b483267ebfe821afaaba0e"
- ],
- "version": "==3.5.0.5"
- },
- "cachetools": {
- "hashes": [
- "sha256:219b7dc6024195b6f2bc3d3f884d1fef458745cd323b04165378622dcc823852",
- "sha256:9efcc9fab3b49ab833475702b55edd5ae07af1af7a4c627678980b45e459c460"
- ],
- "version": "==3.1.0"
- },
- "celery": {
- "hashes": [
- "sha256:77dab4677e24dc654d42dfbdfed65fa760455b6bb563a0877ecc35f4cfcfc678",
- "sha256:ad7a7411772b80a4d6c64f2f7f723200e39fb66cf614a7fdfab76d345acc7b13"
- ],
- "index": "pypi",
- "version": "==4.2.1"
- },
- "certifi": {
- "hashes": [
- "sha256:47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7",
- "sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033"
- ],
- "version": "==2018.11.29"
- },
- "cffi": {
- "hashes": [
- "sha256:0b5f895714a7a9905148fc51978c62e8a6cbcace30904d39dcd0d9e2265bb2f6",
- "sha256:27cdc7ba35ee6aa443271d11583b50815c4bb52be89a909d0028e86c21961709",
- "sha256:2d4a38049ea93d5ce3c7659210393524c1efc3efafa151bd85d196fa98fce50a",
- "sha256:3262573d0d60fc6b9d0e0e6e666db0e5045cbe8a531779aa0deb3b425ec5a282",
- "sha256:358e96cfffc185ab8f6e7e425c7bb028931ed08d65402fbcf3f4e1bff6e66556",
- "sha256:37c7db824b5687fbd7ea5519acfd054c905951acc53503547c86be3db0580134",
- "sha256:39b9554dfe60f878e0c6ff8a460708db6e1b1c9cc6da2c74df2955adf83e355d",
- "sha256:42b96a77acf8b2d06821600fa87c208046decc13bd22a4a0e65c5c973443e0da",
- "sha256:5b37dde5035d3c219324cac0e69d96495970977f310b306fa2df5910e1f329a1",
- "sha256:5d35819f5566d0dd254f273d60cf4a2dcdd3ae3003dfd412d40b3fe8ffd87509",
- "sha256:5df73aa465e53549bd03c819c1bc69fb85529a5e1a693b7b6cb64408dd3970d1",
- "sha256:7075b361f7a4d0d4165439992d0b8a3cdfad1f302bf246ed9308a2e33b046bd3",
- "sha256:7678b5a667b0381c173abe530d7bdb0e6e3b98e062490618f04b80ca62686d96",
- "sha256:7dfd996192ff8a535458c17f22ff5eb78b83504c34d10eefac0c77b1322609e2",
- "sha256:8a3be5d31d02c60f84c4fd4c98c5e3a97b49f32e16861367f67c49425f955b28",
- "sha256:9812e53369c469506b123aee9dcb56d50c82fad60c5df87feb5ff59af5b5f55c",
- "sha256:9b6f7ba4e78c52c1a291d0c0c0bd745d19adde1a9e1c03cb899f0c6efd6f8033",
- "sha256:a85bc1d7c3bba89b3d8c892bc0458de504f8b3bcca18892e6ed15b5f7a52ad9d",
- "sha256:aa6b9c843ad645ebb12616de848cc4e25a40f633ccc293c3c9fe34107c02c2ea",
- "sha256:bae1aa56ee00746798beafe486daa7cfb586cd395c6ce822ba3068e48d761bc0",
- "sha256:bae96e26510e4825d5910a196bf6b5a11a18b87d9278db6d08413be8ea799469",
- "sha256:bd78df3b594013b227bf31d0301566dc50ba6f40df38a70ded731d5a8f2cb071",
- "sha256:c2711197154f46d06f73542c539a0ff5411f1951fab391e0a4ac8359badef719",
- "sha256:d998c20e3deed234fca993fd6c8314cb7cbfda05fd170f1bd75bb5d7421c3c5a",
- "sha256:df4f840d77d9e37136f8e6b432fecc9d6b8730f18f896e90628712c793466ce6",
- "sha256:f5653c2581acb038319e6705d4e3593677676df14b112f13e0b5b44b6a18df1a",
- "sha256:f7c7aa485a2e2250d455148470ffd0195eecc3d845122635202d7467d6f7b4cf",
- "sha256:f9e2c66a6493147de835f207f198540a56b26745ce4f272fbc7c2f2cfebeb729"
- ],
- "version": "==1.12.1"
- },
- "chardet": {
- "hashes": [
- "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
- "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
- ],
- "version": "==3.0.4"
- },
- "click": {
- "hashes": [
- "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
- "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
- ],
- "version": "==7.0"
- },
- "cssselect": {
- "hashes": [
- "sha256:066d8bc5229af09617e24b3ca4d52f1f9092d9e061931f4184cd572885c23204",
- "sha256:3b5103e8789da9e936a68d993b70df732d06b8bb9a337a05ed4eb52c17ef7206"
- ],
- "version": "==1.0.3"
- },
- "cssutils": {
- "hashes": [
- "sha256:a2fcf06467553038e98fea9cfe36af2bf14063eb147a70958cfcaa8f5786acaf",
- "sha256:c74dbe19c92f5052774eadb15136263548dd013250f1ed1027988e7fef125c8d"
- ],
- "version": "==1.0.2"
- },
- "dataclasses": {
- "hashes": [
- "sha256:454a69d788c7fda44efd71e259be79577822f5e3f53f029a22d08004e951dc9f",
- "sha256:6988bd2b895eef432d562370bb707d540f32f7360ab13da45340101bc2307d84"
- ],
- "markers": "python_version < '3.7'",
- "version": "==0.6"
- },
- "dnspython": {
- "hashes": [
- "sha256:36c5e8e38d4369a08b6780b7f27d790a292b2b08eea01607865bf0936c558e01",
- "sha256:f69c21288a962f4da86e56c4905b49d11aba7938d3d740e80d9e366ee4f1632d"
- ],
- "version": "==1.16.0"
- },
- "email-validator": {
- "hashes": [
- "sha256:ddc4b5b59fa699bb10127adcf7ad4de78fde4ec539a072b104b8bb16da666ae5"
- ],
- "index": "pypi",
- "version": "==1.0.3"
- },
- "emails": {
- "hashes": [
- "sha256:2d93bb09539d65a16cf1f68db4ffd0f7f45067633e950866e8a4ef89a7c290ec",
- "sha256:fcc02567a528eae6b66d2a5c20ce7a0326e4f6b201bc8ae302f89413164db06a"
- ],
- "index": "pypi",
- "version": "==0.5.15"
- },
- "fastapi": {
- "hashes": [
- "sha256:06225ac528daec555d5d8488828c9adc1570c0627800abc52481696b2a5e4d1f",
- "sha256:b37d74e197e6dbb54e3c397fe6dd270e477daa4b016ebb25366d6c9839aca298"
- ],
- "index": "pypi",
- "version": "==0.6.0"
- },
- "gunicorn": {
- "hashes": [
- "sha256:aa8e0b40b4157b36a5df5e599f45c9c76d6af43845ba3b3b0efe2c70473c2471",
- "sha256:fa2662097c66f920f53f70621c6c58ca4a3c4d3434205e608e121b5b3b71f4f3"
- ],
- "index": "pypi",
- "version": "==19.9.0"
- },
- "h11": {
- "hashes": [
- "sha256:acca6a44cb52a32ab442b1779adf0875c443c689e9e028f8d831a3769f9c5208",
- "sha256:f2b1ca39bfed357d1f19ac732913d5f9faa54a5062eca7d2ec3a916cfb7ae4c7"
- ],
- "version": "==0.8.1"
- },
- "httptools": {
- "hashes": [
- "sha256:04c7703bbef0e8ca28b09811547352b8c7c20549eab70dc24e536bb24fd2b7c5"
- ],
- "version": "==0.0.11"
- },
- "idna": {
- "hashes": [
- "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
- "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
- ],
- "version": "==2.8"
- },
- "jinja2": {
- "hashes": [
- "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
- "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
- ],
- "index": "pypi",
- "version": "==2.10"
- },
- "kombu": {
- "hashes": [
- "sha256:529df9e0ecc0bad9fc2b376c3ce4796c41b482cf697b78b71aea6ebe7ca353c8",
- "sha256:7a2cbed551103db9a4e2efafe9b63222e012a61a18a881160ad797b9d4e1d0a1"
- ],
- "version": "==4.3.0"
- },
- "lxml": {
- "hashes": [
- "sha256:0537eee4902e8bf4f41bfee8133f7edf96533dd175930a12086d6a40d62376b2",
- "sha256:0562ec748abd230ab87d73384e08fa784f9b9cee89e28696087d2d22c052cc27",
- "sha256:09e91831e749fbf0f24608694e4573be0ef51430229450c39c83176cc2e2d353",
- "sha256:1ae4c0722fc70c0d4fba43ae33c2885f705e96dce1db41f75ae14a2d2749b428",
- "sha256:1c630c083d782cbaf1f7f37f6cac87bda9cff643cf2803a5f180f30d97955cef",
- "sha256:2fe74e3836bd8c0fa7467ffae05545233c7f37de1eb765cacfda15ad20c6574a",
- "sha256:37af783c2667ead34a811037bda56a0b142ac8438f7ed29ae93f82ddb812fbd6",
- "sha256:3f2d9eafbb0b24a33f56acd16f39fc935756524dcb3172892721c54713964c70",
- "sha256:47d8365a8ef14097aa4c65730689be51851b4ade677285a3b2daa03b37893e26",
- "sha256:510e904079bc56ea784677348e151e1156040dbfb736f1d8ea4b9e6d0ab2d9f4",
- "sha256:58d0851da422bba31c7f652a7e9335313cf94a641aa6d73b8f3c67602f75b593",
- "sha256:7940d5c2185ffb989203dacbb28e6ae88b4f1bb25d04e17f94b0edd82232bcbd",
- "sha256:7cf39bb3a905579836f7a8f3a45320d9eb22f16ab0c1e112efb940ced4d057a5",
- "sha256:9563a23c1456c0ab550c087833bc13fcc61013a66c6420921d5b70550ea312bf",
- "sha256:95b392952935947e0786a90b75cc33388549dcb19af716b525dae65b186138fc",
- "sha256:983129f3fd3cef5c3cf067adcca56e30a169656c00fcc6c648629dbb850b27fa",
- "sha256:a0b75b1f1854771844c647c464533def3e0a899dd094a85d1d4ed72ecaaee93d",
- "sha256:b5db89cc0ef624f3a81214b7961a99f443b8c91e88188376b6b322fd10d5b118",
- "sha256:c0a7751ba1a4bfbe7831920d98cee3ce748007eab8dfda74593d44079568219a",
- "sha256:c0c5a7d4aafcc30c9b6d8613a362567e32e5f5b708dc41bc3a81dac56f8af8bb",
- "sha256:d4d63d85eacc6cb37b459b16061e1f100d154bee89dc8d8f9a6128a5a538e92e",
- "sha256:da5e7e941d6e71c9c9a717c93725cda0708c2474f532e3680ac5e39ec57d224d",
- "sha256:dccad2b3c583f036f43f80ac99ee212c2fa9a45151358d55f13004d095e683b2",
- "sha256:df46307d39f2aeaafa1d25309b8a8d11738b73e9861f72d4d0a092528f498baa",
- "sha256:e70b5e1cb48828ddd2818f99b1662cb9226dc6f57d07fc75485405c77da17436",
- "sha256:ea825562b8cd057cbc9810d496b8b5dec37a1e2fc7b27bc7c1e72ce94462a09a"
- ],
- "version": "==4.3.1"
- },
- "mako": {
- "hashes": [
- "sha256:4e02fde57bd4abb5ec400181e4c314f56ac3e49ba4fb8b0d50bba18cb27d25ae"
- ],
- "version": "==1.0.7"
- },
- "markupsafe": {
- "hashes": [
- "sha256:048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432",
- "sha256:130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b",
- "sha256:19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9",
- "sha256:1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af",
- "sha256:1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834",
- "sha256:1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd",
- "sha256:1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d",
- "sha256:31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7",
- "sha256:3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b",
- "sha256:4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3",
- "sha256:525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c",
- "sha256:52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2",
- "sha256:52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7",
- "sha256:5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36",
- "sha256:5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1",
- "sha256:5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e",
- "sha256:7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1",
- "sha256:83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c",
- "sha256:857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856",
- "sha256:98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550",
- "sha256:bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492",
- "sha256:d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672",
- "sha256:e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401",
- "sha256:edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6",
- "sha256:efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6",
- "sha256:f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c",
- "sha256:f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd",
- "sha256:fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1"
- ],
- "version": "==1.1.0"
- },
- "passlib": {
- "extras": [
- "bcrypt"
- ],
- "hashes": [
- "sha256:3d948f64138c25633613f303bcc471126eae67c04d5e3f6b7b8ce6242f8653e0",
- "sha256:43526aea08fa32c6b6dbbbe9963c4c767285b78147b7437597f992812f69d280"
- ],
- "index": "pypi",
- "version": "==1.7.1"
- },
- "premailer": {
- "hashes": [
- "sha256:93be4f197e9d2a87a8fe6b5b6a79b64070dbb523108dfaf2a415b4558fc78ec1",
- "sha256:f45eb4a30485aeccc3ff19771d6614346899ec19a301931af4694f737b6035c3"
- ],
- "version": "==3.3.0"
- },
- "psycopg2-binary": {
- "hashes": [
- "sha256:19a2d1f3567b30f6c2bb3baea23f74f69d51f0c06c2e2082d0d9c28b0733a4c2",
- "sha256:2b69cf4b0fa2716fd977aa4e1fd39af6110eb47b2bb30b4e5a469d8fbecfc102",
- "sha256:2e952fa17ba48cbc2dc063ddeec37d7dc4ea0ef7db0ac1eda8906365a8543f31",
- "sha256:348b49dd737ff74cfb5e663e18cb069b44c64f77ec0523b5794efafbfa7df0b8",
- "sha256:3d72a5fdc5f00ca85160915eb9a973cf9a0ab8148f6eda40708bf672c55ac1d1",
- "sha256:4957452f7868f43f32c090dadb4188e9c74a4687323c87a882e943c2bd4780c3",
- "sha256:5138cec2ee1e53a671e11cc519505eb08aaaaf390c508f25b09605763d48de4b",
- "sha256:587098ca4fc46c95736459d171102336af12f0d415b3b865972a79c03f06259f",
- "sha256:5b79368bcdb1da4a05f931b62760bea0955ee2c81531d8e84625df2defd3f709",
- "sha256:5cf43807392247d9bc99737160da32d3fa619e0bfd85ba24d1c78db205f472a4",
- "sha256:676d1a80b1eebc0cacae8dd09b2fde24213173bf65650d22b038c5ed4039f392",
- "sha256:6b0211ecda389101a7d1d3df2eba0cf7ffbdd2480ca6f1d2257c7bd739e84110",
- "sha256:79cde4660de6f0bb523c229763bd8ad9a93ac6760b72c369cf1213955c430934",
- "sha256:7aba9786ac32c2a6d5fb446002ed936b47d5e1f10c466ef7e48f66eb9f9ebe3b",
- "sha256:7c8159352244e11bdd422226aa17651110b600d175220c451a9acf795e7414e0",
- "sha256:945f2eedf4fc6b2432697eb90bb98cc467de5147869e57405bfc31fa0b824741",
- "sha256:96b4e902cde37a7fc6ab306b3ac089a3949e6ce3d824eeca5b19dc0bedb9f6e2",
- "sha256:9a7bccb1212e63f309eb9fab47b6eaef796f59850f169a25695b248ca1bf681b",
- "sha256:a3bfcac727538ec11af304b5eccadbac952d4cca1a551a29b8fe554e3ad535dc",
- "sha256:b19e9f1b85c5d6136f5a0549abdc55dcbd63aba18b4f10d0d063eb65ef2c68b4",
- "sha256:b664011bb14ca1f2287c17185e222f2098f7b4c857961dbcf9badb28786dbbf4",
- "sha256:bde7959ef012b628868d69c474ec4920252656d0800835ed999ba5e4f57e3e2e",
- "sha256:cb095a0657d792c8de9f7c9a0452385a309dfb1bbbb3357d6b1e216353ade6ca",
- "sha256:d16d42a1b9772152c1fe606f679b2316551f7e1a1ce273e7f808e82a136cdb3d",
- "sha256:d444b1545430ffc1e7a24ce5a9be122ccd3b135a7b7e695c5862c5aff0b11159",
- "sha256:d93ccc7bf409ec0a23f2ac70977507e0b8a8d8c54e5ee46109af2f0ec9e411f3",
- "sha256:df6444f952ca849016902662e1a47abf4fa0678d75f92fd9dd27f20525f809cd",
- "sha256:e63850d8c52ba2b502662bf3c02603175c2397a9acc756090e444ce49508d41e",
- "sha256:ec43358c105794bc2b6fd34c68d27f92bea7102393c01889e93f4b6a70975728",
- "sha256:f4c6926d9c03dadce7a3b378b40d2fea912c1344ef9b29869f984fb3d2a2420b"
- ],
- "index": "pypi",
- "version": "==2.7.7"
- },
- "pycparser": {
- "hashes": [
- "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"
- ],
- "version": "==2.19"
- },
- "pydantic": {
- "hashes": [
- "sha256:9f023811b6cefd203c5fd8fd15a4152f04e79e531b8f676ab1244dfe06ce8024",
- "sha256:edbb08b561feda505374c0f25e4b54466a0a0c702ed6b2efaabdc3890d1a82e7"
- ],
- "index": "pypi",
- "version": "==0.18.2"
- },
- "pyjwt": {
- "hashes": [
- "sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e",
- "sha256:8d59a976fb773f3e6a39c85636357c4f0e242707394cadadd9814f5cbaa20e96"
- ],
- "index": "pypi",
- "version": "==1.7.1"
- },
- "python-dateutil": {
- "hashes": [
- "sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb",
- "sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"
- ],
- "version": "==2.8.0"
- },
- "python-editor": {
- "hashes": [
- "sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d",
- "sha256:51fda6bcc5ddbbb7063b2af7509e43bd84bfc32a4ff71349ec7847713882327b",
- "sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8"
- ],
- "version": "==1.0.4"
- },
- "python-multipart": {
- "hashes": [
- "sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"
- ],
- "index": "pypi",
- "version": "==0.0.5"
- },
- "pytz": {
- "hashes": [
- "sha256:32b0891edff07e28efe91284ed9c31e123d84bea3fd98e1f72be2508f43ef8d9",
- "sha256:d5f05e487007e29e03409f9398d074e158d920d36eb82eaf66fb1136b0c5374c"
- ],
- "version": "==2018.9"
- },
- "raven": {
- "hashes": [
- "sha256:3fa6de6efa2493a7c827472e984ce9b020797d0da16f1db67197bcc23c8fae54",
- "sha256:44a13f87670836e153951af9a3c80405d36b43097db869a36e92809673692ce4"
- ],
- "index": "pypi",
- "version": "==6.10.0"
- },
- "requests": {
- "hashes": [
- "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e",
- "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"
- ],
- "index": "pypi",
- "version": "==2.21.0"
- },
- "six": {
- "hashes": [
- "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
- "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
- ],
- "version": "==1.12.0"
- },
- "sqlalchemy": {
- "hashes": [
- "sha256:7dede29f121071da9873e7b8c98091874617858e790dc364ffaab4b09d81216c"
- ],
- "index": "pypi",
- "version": "==1.3.0b3"
- },
- "starlette": {
- "hashes": [
- "sha256:9d48b35d1fc7521d59ae53c421297ab3878d3c7cd4b75266d77f6c73cccb78bb"
- ],
- "version": "==0.11.1"
- },
- "tenacity": {
- "hashes": [
- "sha256:24b7f302a1caa1801e58b39ea557129c095966e64e5b1ddad3c93a6cb033e38b",
- "sha256:a04b97b3bda047f912a75d110dde3e746891b5548b6bec6d157cd100f2d4afac"
- ],
- "index": "pypi",
- "version": "==5.0.3"
- },
- "urllib3": {
- "hashes": [
- "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39",
- "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"
- ],
- "version": "==1.24.1"
- },
- "uvicorn": {
- "hashes": [
- "sha256:f27889a332ee5c55b4841b11b2392d00dac079f39063fabc1e13e18ada3eb7ba"
- ],
- "index": "pypi",
- "version": "==0.4.5"
- },
- "uvloop": {
- "hashes": [
- "sha256:198fe0c196056930ec6c4a0a878e531a66d15467ca7c74a875aa90271f0c6e3f",
- "sha256:1c175f47d34b84e33c0e312f4987c927ea004afc3a5f05d2f0f610d71d0e4c89",
- "sha256:1c47f197be8f0a3c651dd20be1e1bd43268186246f246d4e86c91e95a89e4865",
- "sha256:3fd4943570d20e8cd4d9f0a3190ebd5cf040e5610b685e05c878128a11f7ad14",
- "sha256:435e232869923fd2248e4ca0ad73e24a5b4debf40bed9dcde133cfe1bef98a7a",
- "sha256:9cfdb966ae804c46b96c92207dfd2174935ffc70e706e42e1c94c60d16dbe860",
- "sha256:a585781443eeb2edb858f8c08c503aac237a5f1bebf0c84ea8340cc337afa408",
- "sha256:b296493e033846e46488a6aa227a75c790091f5ee5456ec637bb0badad1e8851",
- "sha256:c684047c6cf6d697ba37872fb1b4489012ea91f3f802c8fbb9c367c4902e88dc",
- "sha256:da5a59d8812188b57b5783c7fb78891d14dd1050b6259680e0dbd4253d7d0f64"
- ],
- "version": "==0.12.1"
- },
- "vine": {
- "hashes": [
- "sha256:3cd505dcf980223cfaf13423d371f2e7ff99247e38d5985a01ec8264e4f2aca1",
- "sha256:ee4813e915d0e1a54e5c1963fde0855337f82655678540a6bc5996bca4165f76"
- ],
- "version": "==1.2.0"
- },
- "websockets": {
- "hashes": [
- "sha256:04b42a1b57096ffa5627d6a78ea1ff7fad3bc2c0331ffc17bc32a4024da7fea0",
- "sha256:08e3c3e0535befa4f0c4443824496c03ecc25062debbcf895874f8a0b4c97c9f",
- "sha256:10d89d4326045bf5e15e83e9867c85d686b612822e4d8f149cf4840aab5f46e0",
- "sha256:232fac8a1978fc1dead4b1c2fa27c7756750fb393eb4ac52f6bc87ba7242b2fa",
- "sha256:4bf4c8097440eff22bc78ec76fe2a865a6e658b6977a504679aaf08f02c121da",
- "sha256:51642ea3a00772d1e48fb0c492f0d3ae3b6474f34d20eca005a83f8c9c06c561",
- "sha256:55d86102282a636e195dad68aaaf85b81d0bef449d7e2ef2ff79ac450bb25d53",
- "sha256:564d2675682bd497b59907d2205031acbf7d3fadf8c763b689b9ede20300b215",
- "sha256:5d13bf5197a92149dc0badcc2b699267ff65a867029f465accfca8abab95f412",
- "sha256:5eda665f6789edb9b57b57a159b9c55482cbe5b046d7db458948370554b16439",
- "sha256:5edb2524d4032be4564c65dc4f9d01e79fe8fad5f966e5b552f4e5164fef0885",
- "sha256:79691794288bc51e2a3b8de2bc0272ca8355d0b8503077ea57c0716e840ebaef",
- "sha256:7fcc8681e9981b9b511cdee7c580d5b005f3bb86b65bde2188e04a29f1d63317",
- "sha256:8e447e05ec88b1b408a4c9cde85aa6f4b04f06aa874b9f0b8e8319faf51b1fee",
- "sha256:90ea6b3e7787620bb295a4ae050d2811c807d65b1486749414f78cfd6fb61489",
- "sha256:9e13239952694b8b831088431d15f771beace10edfcf9ef230cefea14f18508f",
- "sha256:d40f081187f7b54d7a99d8a5c782eaa4edc335a057aa54c85059272ed826dc09",
- "sha256:e1df1a58ed2468c7b7ce9a2f9752a32ad08eac2bcd56318625c3647c2cd2da6f",
- "sha256:e98d0cec437097f09c7834a11c69d79fe6241729b23f656cfc227e93294fc242",
- "sha256:f8d59627702d2ff27cb495ca1abdea8bd8d581de425c56e93bff6517134e0a9b",
- "sha256:fc30cdf2e949a2225b012a7911d1d031df3d23e99b7eda7dfc982dc4a860dae9"
- ],
- "version": "==7.0"
- }
- },
- "develop": {
- "appdirs": {
- "hashes": [
- "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92",
- "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"
- ],
- "version": "==1.4.3"
- },
- "atomicwrites": {
- "hashes": [
- "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4",
- "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"
- ],
- "version": "==1.3.0"
- },
- "attrs": {
- "hashes": [
- "sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69",
- "sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb"
- ],
- "version": "==18.2.0"
- },
- "autoflake": {
- "hashes": [
- "sha256:c103e63466f11db3617167a2c68ff6a0cda35b940222920631c6eeec6b67e807"
- ],
- "index": "pypi",
- "version": "==1.2"
- },
- "backcall": {
- "hashes": [
- "sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4",
- "sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"
- ],
- "version": "==0.1.0"
- },
- "black": {
- "hashes": [
- "sha256:817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739",
- "sha256:e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"
- ],
- "index": "pypi",
- "version": "==18.9b0"
- },
- "bleach": {
- "hashes": [
- "sha256:213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16",
- "sha256:3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa"
- ],
- "version": "==3.1.0"
- },
- "click": {
- "hashes": [
- "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
- "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
- ],
- "version": "==7.0"
- },
- "decorator": {
- "hashes": [
- "sha256:33cd704aea07b4c28b3eb2c97d288a06918275dac0ecebdaf1bc8a48d98adb9e",
- "sha256:cabb249f4710888a2fc0e13e9a16c343d932033718ff62e1e9bc93a9d3a9122b"
- ],
- "version": "==4.3.2"
- },
- "defusedxml": {
- "hashes": [
- "sha256:24d7f2f94f7f3cb6061acb215685e5125fbcdc40a857eff9de22518820b0a4f4",
- "sha256:702a91ade2968a82beb0db1e0766a6a273f33d4616a6ce8cde475d8e09853b20"
- ],
- "version": "==0.5.0"
- },
- "entrypoints": {
- "hashes": [
- "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19",
- "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"
- ],
- "version": "==0.3"
- },
- "flake8": {
- "hashes": [
- "sha256:6d8c66a65635d46d54de59b027a1dda40abbe2275b3164b634835ac9c13fd048",
- "sha256:6eab21c6e34df2c05416faa40d0c59963008fff29b6f0ccfe8fa28152ab3e383"
- ],
- "index": "pypi",
- "version": "==3.7.6"
- },
- "ipykernel": {
- "hashes": [
- "sha256:0aeb7ec277ac42cc2b59ae3d08b10909b2ec161dc6908096210527162b53675d",
- "sha256:0fc0bf97920d454102168ec2008620066878848fcfca06c22b669696212e292f"
- ],
- "version": "==5.1.0"
- },
- "ipython": {
- "hashes": [
- "sha256:06de667a9e406924f97781bda22d5d76bfb39762b678762d86a466e63f65dc39",
- "sha256:5d3e020a6b5f29df037555e5c45ab1088d6a7cf3bd84f47e0ba501eeb0c3ec82"
- ],
- "markers": "python_version >= '3.3'",
- "version": "==7.3.0"
- },
- "ipython-genutils": {
- "hashes": [
- "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8",
- "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"
- ],
- "version": "==0.2.0"
- },
- "ipywidgets": {
- "hashes": [
- "sha256:0f2b5cde9f272cb49d52f3f0889fdd1a7ae1e74f37b48dac35a83152780d2b7b",
- "sha256:a3e224f430163f767047ab9a042fc55adbcab0c24bbe6cf9f306c4f89fdf0ba3"
- ],
- "version": "==7.4.2"
- },
- "isort": {
- "hashes": [
- "sha256:1153601da39a25b14ddc54955dbbacbb6b2d19135386699e2ad58517953b34af",
- "sha256:b9c40e9750f3d77e6e4d441d8b0266cf555e7cdabdcff33c4fd06366ca761ef8",
- "sha256:ec9ef8f4a9bc6f71eec99e1806bfa2de401650d996c59330782b89a5555c1497"
- ],
- "index": "pypi",
- "version": "==4.3.4"
- },
- "jedi": {
- "hashes": [
- "sha256:571702b5bd167911fe9036e5039ba67f820d6502832285cde8c881ab2b2149fd",
- "sha256:c8481b5e59d34a5c7c42e98f6625e633f6ef59353abea6437472c7ec2093f191"
- ],
- "version": "==0.13.2"
- },
- "jinja2": {
- "hashes": [
- "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
- "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
- ],
- "index": "pypi",
- "version": "==2.10"
- },
- "jsonschema": {
- "hashes": [
- "sha256:683fe7ed58763ea0be572de5aad47cd3cc1297640916f9a8ccd222b287da7d2f",
- "sha256:b42d7a292addb57370e6260bcbadb77e00a899fe6ec998c453f45893c41c658b"
- ],
- "version": "==3.0.0b3"
- },
- "jupyter": {
- "hashes": [
- "sha256:3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7",
- "sha256:5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78",
- "sha256:d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f"
- ],
- "index": "pypi",
- "version": "==1.0.0"
- },
- "jupyter-client": {
- "hashes": [
- "sha256:b5f9cb06105c1d2d30719db5ffb3ea67da60919fb68deaefa583deccd8813551",
- "sha256:c44411eb1463ed77548bc2d5ec0d744c9b81c4a542d9637c7a52824e2121b987"
- ],
- "version": "==5.2.4"
- },
- "jupyter-console": {
- "hashes": [
- "sha256:308ce876354924fb6c540b41d5d6d08acfc946984bf0c97777c1ddcb42e0b2f5",
- "sha256:cc80a97a5c389cbd30252ffb5ce7cefd4b66bde98219edd16bf5cb6f84bb3568"
- ],
- "version": "==6.0.0"
- },
- "jupyter-core": {
- "hashes": [
- "sha256:927d713ffa616ea11972534411544589976b2493fc7e09ad946e010aa7eb9970",
- "sha256:ba70754aa680300306c699790128f6fbd8c306ee5927976cbe48adacf240c0b7"
- ],
- "version": "==4.4.0"
- },
- "markupsafe": {
- "hashes": [
- "sha256:048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432",
- "sha256:130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b",
- "sha256:19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9",
- "sha256:1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af",
- "sha256:1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834",
- "sha256:1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd",
- "sha256:1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d",
- "sha256:31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7",
- "sha256:3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b",
- "sha256:4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3",
- "sha256:525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c",
- "sha256:52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2",
- "sha256:52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7",
- "sha256:5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36",
- "sha256:5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1",
- "sha256:5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e",
- "sha256:7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1",
- "sha256:83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c",
- "sha256:857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856",
- "sha256:98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550",
- "sha256:bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492",
- "sha256:d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672",
- "sha256:e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401",
- "sha256:edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6",
- "sha256:efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6",
- "sha256:f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c",
- "sha256:f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd",
- "sha256:fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1"
- ],
- "version": "==1.1.0"
- },
- "mccabe": {
- "hashes": [
- "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
- "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
- ],
- "version": "==0.6.1"
- },
- "mistune": {
- "hashes": [
- "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e",
- "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"
- ],
- "version": "==0.8.4"
- },
- "more-itertools": {
- "hashes": [
- "sha256:0125e8f60e9e031347105eb1682cef932f5e97d7b9a1a28d9bf00c22a5daef40",
- "sha256:590044e3942351a1bdb1de960b739ff4ce277960f2425ad4509446dbace8d9d1"
- ],
- "markers": "python_version > '2.7'",
- "version": "==6.0.0"
- },
- "mypy": {
- "hashes": [
- "sha256:308c274eb8482fbf16006f549137ddc0d69e5a589465e37b99c4564414363ca7",
- "sha256:e80fd6af34614a0e898a57f14296d0dacb584648f0339c2e000ddbf0f4cc2f8d"
- ],
- "index": "pypi",
- "version": "==0.670"
- },
- "mypy-extensions": {
- "hashes": [
- "sha256:37e0e956f41369209a3d5f34580150bcacfabaa57b33a15c0b25f4b5725e0812",
- "sha256:b16cabe759f55e3409a7d231ebd2841378fb0c27a5d1994719e340e4f429ac3e"
- ],
- "version": "==0.4.1"
- },
- "nbconvert": {
- "hashes": [
- "sha256:302554a2e219bc0fc84f3edd3e79953f3767b46ab67626fdec16e38ba3f7efe4",
- "sha256:5de8fb2284422272a1d45abc77c07b888127550a6d602ce619592a2b08a474ff"
- ],
- "version": "==5.4.1"
- },
- "nbformat": {
- "hashes": [
- "sha256:b9a0dbdbd45bb034f4f8893cafd6f652ea08c8c1674ba83f2dc55d3955743b0b",
- "sha256:f7494ef0df60766b7cabe0a3651556345a963b74dbc16bc7c18479041170d402"
- ],
- "version": "==4.4.0"
- },
- "notebook": {
- "hashes": [
- "sha256:3ab2db8bc10e6edbd264c3c4b800bee276c99818386ee0c146d98d7e6bcf0a67",
- "sha256:d908673a4010787625c8952e91a22adf737db031f2aa0793ad92f6558918a74a"
- ],
- "version": "==5.7.4"
- },
- "pandocfilters": {
- "hashes": [
- "sha256:b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"
- ],
- "version": "==1.4.2"
- },
- "parso": {
- "hashes": [
- "sha256:4580328ae3f548b358f4901e38c0578229186835f0fa0846e47369796dd5bcc9",
- "sha256:68406ebd7eafe17f8e40e15a84b56848eccbf27d7c1feb89e93d8fca395706db"
- ],
- "version": "==0.3.4"
- },
- "pexpect": {
- "hashes": [
- "sha256:2a8e88259839571d1251d278476f3eec5db26deb73a70be5ed5dc5435e418aba",
- "sha256:3fbd41d4caf27fa4a377bfd16fef87271099463e6fa73e92a52f92dfee5d425b"
- ],
- "markers": "sys_platform != 'win32'",
- "version": "==4.6.0"
- },
- "pickleshare": {
- "hashes": [
- "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca",
- "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"
- ],
- "version": "==0.7.5"
- },
- "pluggy": {
- "hashes": [
- "sha256:8ddc32f03971bfdf900a81961a48ccf2fb677cf7715108f85295c67405798616",
- "sha256:980710797ff6a041e9a73a5787804f848996ecaa6f8a1b1e08224a5894f2074a"
- ],
- "version": "==0.8.1"
- },
- "prometheus-client": {
- "hashes": [
- "sha256:1b38b958750f66f208bcd9ab92a633c0c994d8859c831f7abc1f46724fcee490"
- ],
- "version": "==0.6.0"
- },
- "prompt-toolkit": {
- "hashes": [
- "sha256:11adf3389a996a6d45cc277580d0d53e8a5afd281d0c9ec71b28e6f121463780",
- "sha256:2519ad1d8038fd5fc8e770362237ad0364d16a7650fb5724af6997ed5515e3c1",
- "sha256:977c6583ae813a37dc1c2e1b715892461fcbdaa57f6fc62f33a528c4886c8f55"
- ],
- "version": "==2.0.9"
- },
- "ptyprocess": {
- "hashes": [
- "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0",
- "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"
- ],
- "markers": "os_name != 'nt'",
- "version": "==0.6.0"
- },
- "py": {
- "hashes": [
- "sha256:bf92637198836372b520efcba9e020c330123be8ce527e535d185ed4b6f45694",
- "sha256:e76826342cefe3c3d5f7e8ee4316b80d1dd8a300781612ddbc765c17ba25a6c6"
- ],
- "version": "==1.7.0"
- },
- "pycodestyle": {
- "hashes": [
- "sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56",
- "sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"
- ],
- "version": "==2.5.0"
- },
- "pyflakes": {
- "hashes": [
- "sha256:5e8c00e30c464c99e0b501dc160b13a14af7f27d4dffb529c556e30a159e231d",
- "sha256:f277f9ca3e55de669fba45b7393a1449009cff5a37d1af10ebb76c52765269cd"
- ],
- "version": "==2.1.0"
- },
- "pygments": {
- "hashes": [
- "sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a",
- "sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d"
- ],
- "version": "==2.3.1"
- },
- "pyrsistent": {
- "hashes": [
- "sha256:07f7ae71291af8b0dbad8c2ab630d8223e4a8c4e10fc37badda158c02e753acf"
- ],
- "version": "==0.14.10"
- },
- "pytest": {
- "hashes": [
- "sha256:067a1d4bf827ffdd56ad21bd46674703fce77c5957f6c1eef731f6146bfcef1c",
- "sha256:9687049d53695ad45cf5fdc7bbd51f0c49f1ea3ecfc4b7f3fde7501b541f17f4"
- ],
- "index": "pypi",
- "version": "==4.3.0"
- },
- "python-dateutil": {
- "hashes": [
- "sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb",
- "sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"
- ],
- "version": "==2.8.0"
- },
- "pyzmq": {
- "hashes": [
- "sha256:07a03450418694fb07e76a0191b6bc9f411afc8e364ca2062edcf28bb0e51c63",
- "sha256:15f0bf7cd80020f165635595e197603aedb37fddf4164ad5ae226afc43242f7b",
- "sha256:1756dc72e192c670490e38c788c3a35f901adc74ee436e5131d5a3e85fdd7dc6",
- "sha256:1d1eb490da54679d724b08ef3ee04530849023670c4ba7e400ed2cdf906720c4",
- "sha256:228402625796821f08706f58cc42a3c51c9897d723550babaefe4feec2b6dacc",
- "sha256:264ac9dcee6a7af2bce4b61f2d19e5926118a5caa629b50f107ef6318670a364",
- "sha256:2b5a43da65f5dec857184d5c2ce13b80071019e96358f146bdecff7238765bc9",
- "sha256:3928534fa00a2aabfcfdb439c08ba37fbe99ab0cf57776c8db8d2b73a51693ba",
- "sha256:3d2a295b1086d450981f73d3561ac204a0cc9c8ded386a4a34327d918f3b1d0a",
- "sha256:411def5b4cbe6111856040a55c8048df113882e90c57ce88de4a48f0189441ac",
- "sha256:4b77e96a7ffc1c5e08eaf274db554f227b31717d086adca1bb42b12ef35a7194",
- "sha256:4c87fa3e449e1f4ab9170cdfe8213dc0ba34a11b160e6adecafa892e451a29b6",
- "sha256:4fd8621a309db6ec23ef1369f43cdf7a9b0dc217d8ff9ca4095a6e932b379bda",
- "sha256:54fe55a1694ffe608c8e4c5183e83cab7a91f3e5c84bd6f188868d6676c12aba",
- "sha256:60acabd86808a16a895a247fd2bf7a127284a33562d79687bb5df163cff068b2",
- "sha256:618887be4ad754228c0cbba7631f6574608b4430fe93974e6322324f1304fdac",
- "sha256:69130efb6efa936de601cb135a8a4eec1caccd4ea2b784237145ff4075c2d3ae",
- "sha256:6e7f78eeac82140bde7e60e975c6e6b1b678a4dd377782ab63319c1c78bf3aa1",
- "sha256:6ee760cdb84e43574da6b3f2f1fc1251e8acf87253900d28a06451c5f5de39e9",
- "sha256:75c87f1dc1e65cea4b709f2ebc78fa18d4b475e41463502aec9cd26208b88e0f",
- "sha256:97cb1b7cd2c46e87b0a26651eccd2bbb8c758035efd1635ebb81ac36aa76a88c",
- "sha256:abfa774dbadacc849121ed92eae05189d226daab583388b499472e1bbb17ef69",
- "sha256:ae3d2627d74195ddc95675f2f814aca998381b73dc4341b9e10e3e191e1bdb0b",
- "sha256:b30c339eb58355f51f4f54dd61d785f1ff58c86bca1c3a5916977631d121867b",
- "sha256:cbabdced5b137cd56aa22633f13ac5690029a0ad43ab6c05f53206e489178362"
- ],
- "version": "==18.0.0"
- },
- "qtconsole": {
- "hashes": [
- "sha256:1ac4a65e81a27b0838330a6d351c2f8435d4013d98a95373e8a41119b2968390",
- "sha256:bc1ba15f50c29ed50f1268ad823bb6543be263c18dd093b80495e9df63b003ac"
- ],
- "version": "==4.4.3"
- },
- "send2trash": {
- "hashes": [
- "sha256:60001cc07d707fe247c94f74ca6ac0d3255aabcb930529690897ca2a39db28b2",
- "sha256:f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"
- ],
- "version": "==1.5.0"
- },
- "six": {
- "hashes": [
- "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
- "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
- ],
- "version": "==1.12.0"
- },
- "terminado": {
- "hashes": [
- "sha256:55abf9ade563b8f9be1f34e4233c7b7bde726059947a593322e8a553cc4c067a",
- "sha256:65011551baff97f5414c67018e908110693143cfbaeb16831b743fe7cad8b927"
- ],
- "version": "==0.8.1"
- },
- "testpath": {
- "hashes": [
- "sha256:46c89ebb683f473ffe2aab0ed9f12581d4d078308a3cb3765d79c6b2317b0109",
- "sha256:b694b3d9288dbd81685c5d2e7140b81365d46c29f5db4bc659de5aa6b98780f8"
- ],
- "version": "==0.4.2"
- },
- "toml": {
- "hashes": [
- "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
- "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
- ],
- "version": "==0.10.0"
- },
- "tornado": {
- "hashes": [
- "sha256:d3b719a0cb7094e2b1ca94b31f4b601639fa7ad01a548a1a2ccdd6cbdfd56671"
- ],
- "version": "==6.0b1"
- },
- "traitlets": {
- "hashes": [
- "sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835",
- "sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9"
- ],
- "version": "==4.3.2"
- },
- "typed-ast": {
- "hashes": [
- "sha256:035a54ede6ce1380599b2ce57844c6554666522e376bd111eb940fbc7c3dad23",
- "sha256:037c35f2741ce3a9ac0d55abfcd119133cbd821fffa4461397718287092d9d15",
- "sha256:049feae7e9f180b64efacbdc36b3af64a00393a47be22fa9cb6794e68d4e73d3",
- "sha256:19228f7940beafc1ba21a6e8e070e0b0bfd1457902a3a81709762b8b9039b88d",
- "sha256:2ea681e91e3550a30c2265d2916f40a5f5d89b59469a20f3bad7d07adee0f7a6",
- "sha256:3a6b0a78af298d82323660df5497bcea0f0a4a25a0b003afd0ce5af049bd1f60",
- "sha256:5385da8f3b801014504df0852bf83524599df890387a3c2b17b7caa3d78b1773",
- "sha256:606d8afa07eef77280c2bf84335e24390055b478392e1975f96286d99d0cb424",
- "sha256:69245b5b23bbf7fb242c9f8f08493e9ecd7711f063259aefffaeb90595d62287",
- "sha256:6f6d839ab09830d59b7fa8fb6917023d8cb5498ee1f1dbd82d37db78eb76bc99",
- "sha256:730888475f5ac0e37c1de4bd05eeb799fdb742697867f524dc8a4cd74bcecc23",
- "sha256:9819b5162ffc121b9e334923c685b0d0826154e41dfe70b2ede2ce29034c71d8",
- "sha256:9e60ef9426efab601dd9aa120e4ff560f4461cf8442e9c0a2b92548d52800699",
- "sha256:af5fbdde0690c7da68e841d7fc2632345d570768ea7406a9434446d7b33b0ee1",
- "sha256:b64efdbdf3bbb1377562c179f167f3bf301251411eb5ac77dec6b7d32bcda463",
- "sha256:bac5f444c118aeb456fac1b0b5d14c6a71ea2a42069b09c176f75e9bd4c186f6",
- "sha256:bda9068aafb73859491e13b99b682bd299c1b5fd50644d697533775828a28ee0",
- "sha256:d659517ca116e6750101a1326107d3479028c5191f0ecee3c7203c50f5b915b0",
- "sha256:eddd3fb1f3e0f82e5915a899285a39ee34ce18fd25d89582bc89fc9fb16cd2c6"
- ],
- "version": "==1.3.1"
- },
- "vulture": {
- "hashes": [
- "sha256:4b5a8980c338e9c068d43e7164555a1e4c9c7d84961ce2bc6f3ed975f6e5bc9d",
- "sha256:524b6b9642d0bbe74ea21478bf260937d1ba9b3b86676ca0b17cd10b4b51ba01"
- ],
- "index": "pypi",
- "version": "==1.0"
- },
- "wcwidth": {
- "hashes": [
- "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
- "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
- ],
- "version": "==0.1.7"
- },
- "webencodings": {
- "hashes": [
- "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78",
- "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"
- ],
- "version": "==0.5.1"
- },
- "widgetsnbextension": {
- "hashes": [
- "sha256:14b2c65f9940c9a7d3b70adbe713dbd38b5ec69724eebaba034d1036cf3d4740",
- "sha256:fa618be8435447a017fd1bf2c7ae922d0428056cfc7449f7a8641edf76b48265"
- ],
- "version": "==3.4.2"
- }
- }
-}
diff --git a/{{cookiecutter.project_slug}}/backend/app/alembic/versions/e6ae69e9dcb9_first_revision.py b/{{cookiecutter.project_slug}}/backend/app/alembic/versions/d4867f3a4c0a_first_revision.py
similarity index 59%
rename from {{cookiecutter.project_slug}}/backend/app/alembic/versions/e6ae69e9dcb9_first_revision.py
rename to {{cookiecutter.project_slug}}/backend/app/alembic/versions/d4867f3a4c0a_first_revision.py
index 6034264e9f..68b3ee4f1c 100644
--- a/{{cookiecutter.project_slug}}/backend/app/alembic/versions/e6ae69e9dcb9_first_revision.py
+++ b/{{cookiecutter.project_slug}}/backend/app/alembic/versions/d4867f3a4c0a_first_revision.py
@@ -1,8 +1,8 @@
"""First revision
-Revision ID: e6ae69e9dcb9
+Revision ID: d4867f3a4c0a
Revises:
-Create Date: 2019-02-13 14:27:57.038583
+Create Date: 2019-04-17 13:53:32.978401
"""
from alembic import op
@@ -10,7 +10,7 @@
# revision identifiers, used by Alembic.
-revision = 'e6ae69e9dcb9'
+revision = 'd4867f3a4c0a'
down_revision = None
branch_labels = None
depends_on = None
@@ -30,11 +30,26 @@ def upgrade():
op.create_index(op.f('ix_user_email'), 'user', ['email'], unique=True)
op.create_index(op.f('ix_user_full_name'), 'user', ['full_name'], unique=False)
op.create_index(op.f('ix_user_id'), 'user', ['id'], unique=False)
+ op.create_table('item',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('title', sa.String(), nullable=True),
+ sa.Column('description', sa.String(), nullable=True),
+ sa.Column('owner_id', sa.Integer(), nullable=True),
+ sa.ForeignKeyConstraint(['owner_id'], ['user.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_item_description'), 'item', ['description'], unique=False)
+ op.create_index(op.f('ix_item_id'), 'item', ['id'], unique=False)
+ op.create_index(op.f('ix_item_title'), 'item', ['title'], unique=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
+ op.drop_index(op.f('ix_item_title'), table_name='item')
+ op.drop_index(op.f('ix_item_id'), table_name='item')
+ op.drop_index(op.f('ix_item_description'), table_name='item')
+ op.drop_table('item')
op.drop_index(op.f('ix_user_id'), table_name='user')
op.drop_index(op.f('ix_user_full_name'), table_name='user')
op.drop_index(op.f('ix_user_email'), table_name='user')
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/api.py b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/api.py
index 8673a99123..2163017268 100644
--- a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/api.py
+++ b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/api.py
@@ -1,8 +1,9 @@
from fastapi import APIRouter
-from app.api.api_v1.endpoints import token, user, utils
+from app.api.api_v1.endpoints import items, login, users, utils
api_router = APIRouter()
-api_router.include_router(token.router)
-api_router.include_router(user.router)
-api_router.include_router(utils.router)
+api_router.include_router(login.router, tags=["login"])
+api_router.include_router(users.router, prefix="/users", tags=["users"])
+api_router.include_router(utils.router, prefix="/utils", tags=["utils"])
+api_router.include_router(items.router, prefix="/items", tags=["items"])
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/items.py b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/items.py
new file mode 100644
index 0000000000..131e9852d4
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/items.py
@@ -0,0 +1,102 @@
+from typing import List
+
+from fastapi import APIRouter, Depends, HTTPException
+from sqlalchemy.orm import Session
+
+from app import crud
+from app.api.utils.db import get_db
+from app.api.utils.security import get_current_active_user
+from app.db_models.user import User as DBUser
+from app.models.item import Item, ItemCreate, ItemUpdate
+
+router = APIRouter()
+
+
+@router.get("/", response_model=List[Item])
+def read_items(
+ db: Session = Depends(get_db),
+ skip: int = 0,
+ limit: int = 100,
+ current_user: DBUser = Depends(get_current_active_user),
+):
+ """
+ Retrieve items.
+ """
+ if crud.user.is_superuser(current_user):
+ items = crud.item.get_multi(db, skip=skip, limit=limit)
+ else:
+ items = crud.item.get_multi_by_owner(
+ db_session=db, owner_id=current_user.id, skip=skip, limit=limit
+ )
+ return items
+
+
+@router.post("/", response_model=Item)
+def create_item(
+ *,
+ db: Session = Depends(get_db),
+ item_in: ItemCreate,
+ current_user: DBUser = Depends(get_current_active_user),
+):
+ """
+ Create new item.
+ """
+ item = crud.item.create(db_session=db, item_in=item_in, owner_id=current_user.id)
+ return item
+
+
+@router.put("/{id}", response_model=Item)
+def update_item(
+ *,
+ db: Session = Depends(get_db),
+ id: int,
+ item_in: ItemUpdate,
+ current_user: DBUser = Depends(get_current_active_user),
+):
+ """
+ Update an item.
+ """
+ item = crud.item.get(db_session=db, id=id)
+ if not item:
+ raise HTTPException(status_code=404, detail="Item not found")
+ if not crud.user.is_superuser(current_user) and (item.owner_id != current_user.id):
+ raise HTTPException(status_code=400, detail="Not enough permissions")
+ item = crud.item.update(db_session=db, item=item, item_in=item_in)
+ return item
+
+
+@router.get("/{id}", response_model=Item)
+def read_user_me(
+ *,
+ db: Session = Depends(get_db),
+ id: int,
+ current_user: DBUser = Depends(get_current_active_user),
+):
+ """
+ Get item by ID.
+ """
+ item = crud.item.get(db_session=db, id=id)
+ if not item:
+ raise HTTPException(status_code=400, detail="Item not found")
+ if not crud.user.is_superuser(current_user) and (item.owner_id != current_user.id):
+ raise HTTPException(status_code=400, detail="Not enough permissions")
+ return item
+
+
+@router.delete("/{id}", response_model=Item)
+def delete_item(
+ *,
+ db: Session = Depends(get_db),
+ id: int,
+ current_user: DBUser = Depends(get_current_active_user),
+):
+ """
+ Delete an item.
+ """
+ item = crud.item.get(db_session=db, id=id)
+ if not item:
+ raise HTTPException(status_code=404, detail="Item not found")
+ if not crud.user.is_superuser(current_user) and (item.owner_id != current_user.id):
+ raise HTTPException(status_code=400, detail="Not enough permissions")
+ item = crud.item.remove(db_session=db, id=id)
+ return item
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/token.py b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/login.py
similarity index 100%
rename from {{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/token.py
rename to {{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/login.py
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/user.py b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/users.py
similarity index 80%
rename from {{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/user.py
rename to {{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/users.py
index 3ac36ecd58..966fe12ddc 100644
--- a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/user.py
+++ b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/users.py
@@ -10,13 +10,13 @@
from app.api.utils.security import get_current_active_superuser, get_current_active_user
from app.core import config
from app.db_models.user import User as DBUser
-from app.models.user import User, UserInCreate, UserInDB, UserInUpdate
+from app.models.user import User, UserCreate, UserInDB, UserUpdate
from app.utils import send_new_account_email
router = APIRouter()
-@router.get("/users/", tags=["users"], response_model=List[User])
+@router.get("/", response_model=List[User])
def read_users(
db: Session = Depends(get_db),
skip: int = 0,
@@ -24,21 +24,21 @@ def read_users(
current_user: DBUser = Depends(get_current_active_superuser),
):
"""
- Retrieve users
+ Retrieve users.
"""
users = crud.user.get_multi(db, skip=skip, limit=limit)
return users
-@router.post("/users/", tags=["users"], response_model=User)
+@router.post("/", response_model=User)
def create_user(
*,
db: Session = Depends(get_db),
- user_in: UserInCreate,
+ user_in: UserCreate,
current_user: DBUser = Depends(get_current_active_superuser),
):
"""
- Create new user
+ Create new user.
"""
user = crud.user.get_by_email(db, email=user_in.email)
if user:
@@ -54,7 +54,7 @@ def create_user(
return user
-@router.put("/users/me", tags=["users"], response_model=User)
+@router.put("/me", response_model=User)
def update_user_me(
*,
db: Session = Depends(get_db),
@@ -64,10 +64,10 @@ def update_user_me(
current_user: DBUser = Depends(get_current_active_user),
):
"""
- Update own user
+ Update own user.
"""
current_user_data = jsonable_encoder(current_user)
- user_in = UserInUpdate(**current_user_data)
+ user_in = UserUpdate(**current_user_data)
if password is not None:
user_in.password = password
if full_name is not None:
@@ -78,18 +78,18 @@ def update_user_me(
return user
-@router.get("/users/me", tags=["users"], response_model=User)
+@router.get("/me", response_model=User)
def read_user_me(
db: Session = Depends(get_db),
current_user: DBUser = Depends(get_current_active_user),
):
"""
- Get current user
+ Get current user.
"""
return current_user
-@router.post("/users/open", tags=["users"], response_model=User)
+@router.post("/open", response_model=User)
def create_user_open(
*,
db: Session = Depends(get_db),
@@ -98,7 +98,7 @@ def create_user_open(
full_name: str = Body(None),
):
"""
- Create new user without the need to be logged in
+ Create new user without the need to be logged in.
"""
if not config.USERS_OPEN_REGISTRATION:
raise HTTPException(
@@ -111,19 +111,19 @@ def create_user_open(
status_code=400,
detail="The user with this username already exists in the system",
)
- user_in = UserInCreate(password=password, email=email, full_name=full_name)
+ user_in = UserCreate(password=password, email=email, full_name=full_name)
user = crud.user.create(db, user_in=user_in)
return user
-@router.get("/users/{user_id}", tags=["users"], response_model=User)
+@router.get("/{user_id}", response_model=User)
def read_user_by_id(
user_id: int,
current_user: DBUser = Depends(get_current_active_user),
db: Session = Depends(get_db),
):
"""
- Get a specific user by id
+ Get a specific user by id.
"""
user = crud.user.get(db, user_id=user_id)
if user == current_user:
@@ -135,19 +135,18 @@ def read_user_by_id(
return user
-@router.put("/users/{user_id}", tags=["users"], response_model=User)
+@router.put("/{user_id}", response_model=User)
def update_user(
*,
db: Session = Depends(get_db),
user_id: int,
- user_in: UserInUpdate,
+ user_in: UserUpdate,
current_user: UserInDB = Depends(get_current_active_superuser),
):
"""
- Update a user
+ Update a user.
"""
user = crud.user.get(db, user_id=user_id)
-
if not user:
raise HTTPException(
status_code=404,
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/utils.py b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/utils.py
index 18995cec1c..cc43abe52a 100644
--- a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/utils.py
+++ b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/utils.py
@@ -10,23 +10,23 @@
router = APIRouter()
-@router.post("/test-celery/", tags=["utils"], response_model=Msg, status_code=201)
+@router.post("/test-celery/", response_model=Msg, status_code=201)
def test_celery(
msg: Msg, current_user: UserInDB = Depends(get_current_active_superuser)
):
"""
- Test Celery worker
+ Test Celery worker.
"""
celery_app.send_task("app.worker.test_celery", args=[msg.msg])
return {"msg": "Word received"}
-@router.post("/test-email/", tags=["utils"], response_model=Msg, status_code=201)
+@router.post("/test-email/", response_model=Msg, status_code=201)
def test_email(
email_to: EmailStr, current_user: UserInDB = Depends(get_current_active_superuser)
):
"""
- Test emails
+ Test emails.
"""
send_test_email(email_to=email_to)
return {"msg": "Test email sent"}
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/crud/__init__.py b/{{cookiecutter.project_slug}}/backend/app/app/crud/__init__.py
index f9b61db23e..9330490546 100644
--- a/{{cookiecutter.project_slug}}/backend/app/app/crud/__init__.py
+++ b/{{cookiecutter.project_slug}}/backend/app/app/crud/__init__.py
@@ -1 +1 @@
-from . import user
+from . import item, user
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/crud/item.py b/{{cookiecutter.project_slug}}/backend/app/app/crud/item.py
new file mode 100644
index 0000000000..4d92b041ca
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/backend/app/app/crud/item.py
@@ -0,0 +1,54 @@
+from typing import List, Optional
+
+from fastapi.encoders import jsonable_encoder
+from sqlalchemy.orm import Session
+
+from app.db_models.item import Item
+from app.models.item import ItemCreate, ItemUpdate
+
+
+def get(db_session: Session, *, id: int) -> Optional[Item]:
+ return db_session.query(Item).filter(Item.id == id).first()
+
+
+def get_multi(db_session: Session, *, skip=0, limit=100) -> List[Optional[Item]]:
+ return db_session.query(Item).offset(skip).limit(limit).all()
+
+
+def get_multi_by_owner(
+ db_session: Session, *, owner_id: int, skip=0, limit=100
+) -> List[Optional[Item]]:
+ return (
+ db_session.query(Item)
+ .filter(Item.owner_id == owner_id)
+ .offset(skip)
+ .limit(limit)
+ .all()
+ )
+
+
+def create(db_session: Session, *, item_in: ItemCreate, owner_id: int) -> Item:
+ item = Item(title=item_in.title, description=item_in.description, owner_id=owner_id)
+ db_session.add(item)
+ db_session.commit()
+ db_session.refresh(item)
+ return item
+
+
+def update(db_session: Session, *, item: Item, item_in: ItemUpdate) -> Item:
+ item_data = jsonable_encoder(item)
+ update_data = item_in.dict(skip_defaults=True)
+ for field in item_data:
+ if field in update_data:
+ setattr(item, field, update_data[field])
+ db_session.add(item)
+ db_session.commit()
+ db_session.refresh(item)
+ return item
+
+
+def remove(db_session: Session, *, id: int):
+ item = db_session.query(Item).filter(Item.id == id).first()
+ db_session.delete(item)
+ db_session.commit()
+ return item
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/crud/user.py b/{{cookiecutter.project_slug}}/backend/app/app/crud/user.py
index 294ae9ab20..66f80753c0 100644
--- a/{{cookiecutter.project_slug}}/backend/app/app/crud/user.py
+++ b/{{cookiecutter.project_slug}}/backend/app/app/crud/user.py
@@ -1,21 +1,22 @@
from typing import List, Optional
from fastapi.encoders import jsonable_encoder
+from sqlalchemy.orm import Session
from app.core.security import get_password_hash, verify_password
from app.db_models.user import User
-from app.models.user import UserInCreate, UserInUpdate
+from app.models.user import UserCreate, UserUpdate
-def get(db_session, *, user_id: int) -> Optional[User]:
+def get(db_session: Session, *, user_id: int) -> Optional[User]:
return db_session.query(User).filter(User.id == user_id).first()
-def get_by_email(db_session, *, email: str) -> Optional[User]:
+def get_by_email(db_session: Session, *, email: str) -> Optional[User]:
return db_session.query(User).filter(User.email == email).first()
-def authenticate(db_session, *, email: str, password: str) -> Optional[User]:
+def authenticate(db_session: Session, *, email: str, password: str) -> Optional[User]:
user = get_by_email(db_session, email=email)
if not user:
return None
@@ -32,11 +33,11 @@ def is_superuser(user) -> bool:
return user.is_superuser
-def get_multi(db_session, *, skip=0, limit=100) -> List[Optional[User]]:
+def get_multi(db_session: Session, *, skip=0, limit=100) -> List[Optional[User]]:
return db_session.query(User).offset(skip).limit(limit).all()
-def create(db_session, *, user_in: UserInCreate) -> User:
+def create(db_session: Session, *, user_in: UserCreate) -> User:
user = User(
email=user_in.email,
hashed_password=get_password_hash(user_in.password),
@@ -49,13 +50,12 @@ def create(db_session, *, user_in: UserInCreate) -> User:
return user
-def update(db_session, *, user: User, user_in: UserInUpdate) -> User:
+def update(db_session: Session, *, user: User, user_in: UserUpdate) -> User:
user_data = jsonable_encoder(user)
+ update_data = user_in.dict(skip_defaults=True)
for field in user_data:
- if field in user_in.fields:
- value_in = getattr(user_in, field)
- if value_in is not None:
- setattr(user, field, value_in)
+ if field in update_data:
+ setattr(user, field, update_data[field])
if user_in.password:
passwordhash = get_password_hash(user_in.password)
user.hashed_password = passwordhash
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/db/base.py b/{{cookiecutter.project_slug}}/backend/app/app/db/base.py
index 44a65b5647..1665277300 100644
--- a/{{cookiecutter.project_slug}}/backend/app/app/db/base.py
+++ b/{{cookiecutter.project_slug}}/backend/app/app/db/base.py
@@ -2,3 +2,4 @@
# imported by Alembic
from app.db.base_class import Base # noqa
from app.db_models.user import User # noqa
+from app.db_models.item import Item # noqa
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/db/init_db.py b/{{cookiecutter.project_slug}}/backend/app/app/db/init_db.py
index 4b9d825d99..4f1d6f5aa3 100644
--- a/{{cookiecutter.project_slug}}/backend/app/app/db/init_db.py
+++ b/{{cookiecutter.project_slug}}/backend/app/app/db/init_db.py
@@ -1,6 +1,6 @@
from app import crud
from app.core import config
-from app.models.user import UserInCreate
+from app.models.user import UserCreate
def init_db(db_session):
@@ -11,7 +11,7 @@ def init_db(db_session):
user = crud.user.get_by_email(db_session, email=config.FIRST_SUPERUSER)
if not user:
- user_in = UserInCreate(
+ user_in = UserCreate(
email=config.FIRST_SUPERUSER,
password=config.FIRST_SUPERUSER_PASSWORD,
is_superuser=True,
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/db_models/item.py b/{{cookiecutter.project_slug}}/backend/app/app/db_models/item.py
new file mode 100755
index 0000000000..685687a098
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/backend/app/app/db_models/item.py
@@ -0,0 +1,12 @@
+from sqlalchemy import Column, ForeignKey, Integer, String
+from sqlalchemy.orm import relationship
+
+from app.db.base_class import Base
+
+
+class Item(Base):
+ id = Column(Integer, primary_key=True, index=True)
+ title = Column(String, index=True)
+ description = Column(String, index=True)
+ owner_id = Column(Integer, ForeignKey("user.id"))
+ owner = relationship("User", back_populates="items")
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/db_models/user.py b/{{cookiecutter.project_slug}}/backend/app/app/db_models/user.py
index cdfffaa443..1052908a4b 100755
--- a/{{cookiecutter.project_slug}}/backend/app/app/db_models/user.py
+++ b/{{cookiecutter.project_slug}}/backend/app/app/db_models/user.py
@@ -1,4 +1,5 @@
from sqlalchemy import Boolean, Column, Integer, String
+from sqlalchemy.orm import relationship
from app.db.base_class import Base
@@ -10,3 +11,4 @@ class User(Base):
hashed_password = Column(String)
is_active = Column(Boolean(), default=True)
is_superuser = Column(Boolean(), default=False)
+ items = relationship("Item", back_populates="owner")
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/models/item.py b/{{cookiecutter.project_slug}}/backend/app/app/models/item.py
new file mode 100644
index 0000000000..cc7511e920
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/backend/app/app/models/item.py
@@ -0,0 +1,34 @@
+from pydantic import BaseModel
+
+
+# Shared properties
+class ItemBase(BaseModel):
+ title: str = None
+ description: str = None
+
+
+# Properties to receive on item creation
+class ItemCreate(ItemBase):
+ title: str
+
+
+# Properties to receive on item update
+class ItemUpdate(ItemBase):
+ pass
+
+
+# Properties shared by models stored in DB
+class ItemInDBBase(ItemBase):
+ id: int
+ title: str
+ owner_id: int
+
+
+# Properties to return to client
+class Item(ItemInDBBase):
+ pass
+
+
+# Properties properties stored in DB
+class ItemInDB(ItemInDBBase):
+ pass
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/models/user.py b/{{cookiecutter.project_slug}}/backend/app/app/models/user.py
index 54636e1f1b..51f1b02579 100644
--- a/{{cookiecutter.project_slug}}/backend/app/app/models/user.py
+++ b/{{cookiecutter.project_slug}}/backend/app/app/models/user.py
@@ -16,13 +16,13 @@ class UserBaseInDB(UserBase):
# Properties to receive via API on creation
-class UserInCreate(UserBaseInDB):
+class UserCreate(UserBaseInDB):
email: str
password: str
# Properties to receive via API on update
-class UserInUpdate(UserBaseInDB):
+class UserUpdate(UserBaseInDB):
password: Optional[str] = None
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/tests/api/api_v1/test_celery.py b/{{cookiecutter.project_slug}}/backend/app/app/tests/api/api_v1/test_celery.py
index eb480309f7..2270243c72 100644
--- a/{{cookiecutter.project_slug}}/backend/app/app/tests/api/api_v1/test_celery.py
+++ b/{{cookiecutter.project_slug}}/backend/app/app/tests/api/api_v1/test_celery.py
@@ -8,7 +8,7 @@ def test_celery_worker_test(superuser_token_headers):
server_api = get_server_api()
data = {"msg": "test"}
r = requests.post(
- f"{server_api}{config.API_V1_STR}/test-celery/",
+ f"{server_api}{config.API_V1_STR}/utils/test-celery/",
json=data,
headers=superuser_token_headers,
)
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/tests/api/api_v1/test_items.py b/{{cookiecutter.project_slug}}/backend/app/app/tests/api/api_v1/test_items.py
new file mode 100644
index 0000000000..330ae360bd
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/backend/app/app/tests/api/api_v1/test_items.py
@@ -0,0 +1,34 @@
+import requests
+
+from app.core import config
+from app.tests.utils.item import create_random_item
+from app.tests.utils.utils import get_server_api
+
+
+def test_create_item(superuser_token_headers):
+ server_api = get_server_api()
+ data = {"title": "Foo", "description": "Fighters"}
+ response = requests.post(
+ f"{server_api}{config.API_V1_STR}/items/",
+ headers=superuser_token_headers,
+ json=data,
+ )
+ content = response.json()
+ assert content["title"] == data["title"]
+ assert content["description"] == data["description"]
+ assert "id" in content
+ assert "owner_id" in content
+
+
+def test_read_item(superuser_token_headers):
+ item = create_random_item()
+ server_api = get_server_api()
+ response = requests.get(
+ f"{server_api}{config.API_V1_STR}/items/{item.id}",
+ headers=superuser_token_headers,
+ )
+ content = response.json()
+ assert content["title"] == item.title
+ assert content["description"] == item.description
+ assert content["id"] == item.id
+ assert content["owner_id"] == item.owner_id
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/tests/api/api_v1/test_token.py b/{{cookiecutter.project_slug}}/backend/app/app/tests/api/api_v1/test_login.py
similarity index 100%
rename from {{cookiecutter.project_slug}}/backend/app/app/tests/api/api_v1/test_token.py
rename to {{cookiecutter.project_slug}}/backend/app/app/tests/api/api_v1/test_login.py
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/tests/api/api_v1/test_user.py b/{{cookiecutter.project_slug}}/backend/app/app/tests/api/api_v1/test_users.py
similarity index 90%
rename from {{cookiecutter.project_slug}}/backend/app/app/tests/api/api_v1/test_user.py
rename to {{cookiecutter.project_slug}}/backend/app/app/tests/api/api_v1/test_users.py
index cf7b8be062..119ed219fd 100644
--- a/{{cookiecutter.project_slug}}/backend/app/app/tests/api/api_v1/test_user.py
+++ b/{{cookiecutter.project_slug}}/backend/app/app/tests/api/api_v1/test_users.py
@@ -3,7 +3,7 @@
from app import crud
from app.core import config
from app.db.session import db_session
-from app.models.user import UserInCreate
+from app.models.user import UserCreate
from app.tests.utils.user import user_authentication_headers
from app.tests.utils.utils import get_server_api, random_lower_string
@@ -40,7 +40,7 @@ def test_get_existing_user(superuser_token_headers):
server_api = get_server_api()
username = random_lower_string()
password = random_lower_string()
- user_in = UserInCreate(email=username, password=password)
+ user_in = UserCreate(email=username, password=password)
user = crud.user.create(db_session, user_in=user_in)
user_id = user.id
r = requests.get(
@@ -58,7 +58,7 @@ def test_create_user_existing_username(superuser_token_headers):
username = random_lower_string()
# username = email
password = random_lower_string()
- user_in = UserInCreate(email=username, password=password)
+ user_in = UserCreate(email=username, password=password)
user = crud.user.create(db_session, user_in=user_in)
data = {"email": username, "password": password}
r = requests.post(
@@ -75,7 +75,7 @@ def test_create_user_by_normal_user():
server_api = get_server_api()
username = random_lower_string()
password = random_lower_string()
- user_in = UserInCreate(email=username, password=password)
+ user_in = UserCreate(email=username, password=password)
user = crud.user.create(db_session, user_in=user_in)
user_token_headers = user_authentication_headers(server_api, username, password)
data = {"email": username, "password": password}
@@ -89,12 +89,12 @@ def test_retrieve_users(superuser_token_headers):
server_api = get_server_api()
username = random_lower_string()
password = random_lower_string()
- user_in = UserInCreate(email=username, password=password)
+ user_in = UserCreate(email=username, password=password)
user = crud.user.create(db_session, user_in=user_in)
username2 = random_lower_string()
password2 = random_lower_string()
- user_in2 = UserInCreate(email=username2, password=password2)
+ user_in2 = UserCreate(email=username2, password=password2)
user2 = crud.user.create(db_session, user_in=user_in2)
r = requests.get(
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/tests/crud/test_item.py b/{{cookiecutter.project_slug}}/backend/app/app/tests/crud/test_item.py
new file mode 100644
index 0000000000..33d8b7bbee
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/backend/app/app/tests/crud/test_item.py
@@ -0,0 +1,61 @@
+from app import crud
+from app.models.item import ItemCreate, ItemUpdate
+from app.tests.utils.user import create_random_user
+from app.tests.utils.utils import random_lower_string
+from app.db.session import db_session
+
+
+def test_create_item():
+ title = random_lower_string()
+ description = random_lower_string()
+ item_in = ItemCreate(title=title, description=description)
+ user = create_random_user()
+ item = crud.item.create(db_session=db_session, item_in=item_in, owner_id=user.id)
+ assert item.title == title
+ assert item.description == description
+ assert item.owner_id == user.id
+
+
+def test_get_item():
+ title = random_lower_string()
+ description = random_lower_string()
+ item_in = ItemCreate(title=title, description=description)
+ user = create_random_user()
+ item = crud.item.create(db_session=db_session, item_in=item_in, owner_id=user.id)
+ stored_item = crud.item.get(db_session=db_session, id=item.id)
+ assert item.id == stored_item.id
+ assert item.title == stored_item.title
+ assert item.description == stored_item.description
+ assert item.owner_id == stored_item.owner_id
+
+
+def test_update_item():
+ title = random_lower_string()
+ description = random_lower_string()
+ item_in = ItemCreate(title=title, description=description)
+ user = create_random_user()
+ item = crud.item.create(db_session=db_session, item_in=item_in, owner_id=user.id)
+ description2 = random_lower_string()
+ item_update = ItemUpdate(description=description2)
+ item2 = crud.item.update(
+ db_session=db_session, item=item, item_in=item_update
+ )
+ assert item.id == item2.id
+ assert item.title == item2.title
+ assert item2.description == description2
+ assert item.owner_id == item2.owner_id
+
+
+def test_delete_item():
+ title = random_lower_string()
+ description = random_lower_string()
+ item_in = ItemCreate(title=title, description=description)
+ user = create_random_user()
+ item = crud.item.create(db_session=db_session, item_in=item_in, owner_id=user.id)
+ item2 = crud.item.remove(db_session=db_session, id=item.id)
+ item3 = crud.item.get(db_session=db_session, id=item.id)
+ assert item3 is None
+ assert item2.id == item.id
+ assert item2.title == title
+ assert item2.description == description
+ assert item2.owner_id == user.id
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/tests/crud/test_user.py b/{{cookiecutter.project_slug}}/backend/app/app/tests/crud/test_user.py
index 175e4f4c3e..e239cbac01 100644
--- a/{{cookiecutter.project_slug}}/backend/app/app/tests/crud/test_user.py
+++ b/{{cookiecutter.project_slug}}/backend/app/app/tests/crud/test_user.py
@@ -2,14 +2,14 @@
from app import crud
from app.db.session import db_session
-from app.models.user import UserInCreate
+from app.models.user import UserCreate
from app.tests.utils.utils import random_lower_string
def test_create_user():
email = random_lower_string()
password = random_lower_string()
- user_in = UserInCreate(email=email, password=password)
+ user_in = UserCreate(email=email, password=password)
user = crud.user.create(db_session, user_in=user_in)
assert user.email == email
assert hasattr(user, "hashed_password")
@@ -18,7 +18,7 @@ def test_create_user():
def test_authenticate_user():
email = random_lower_string()
password = random_lower_string()
- user_in = UserInCreate(email=email, password=password)
+ user_in = UserCreate(email=email, password=password)
user = crud.user.create(db_session, user_in=user_in)
authenticated_user = crud.user.authenticate(
db_session, email=email, password=password
@@ -37,7 +37,7 @@ def test_not_authenticate_user():
def test_check_if_user_is_active():
email = random_lower_string()
password = random_lower_string()
- user_in = UserInCreate(email=email, password=password)
+ user_in = UserCreate(email=email, password=password)
user = crud.user.create(db_session, user_in=user_in)
is_active = crud.user.is_active(user)
assert is_active is True
@@ -46,7 +46,7 @@ def test_check_if_user_is_active():
def test_check_if_user_is_active_inactive():
email = random_lower_string()
password = random_lower_string()
- user_in = UserInCreate(email=email, password=password, disabled=True)
+ user_in = UserCreate(email=email, password=password, disabled=True)
print(user_in)
user = crud.user.create(db_session, user_in=user_in)
print(user)
@@ -58,7 +58,7 @@ def test_check_if_user_is_active_inactive():
def test_check_if_user_is_superuser():
email = random_lower_string()
password = random_lower_string()
- user_in = UserInCreate(email=email, password=password, is_superuser=True)
+ user_in = UserCreate(email=email, password=password, is_superuser=True)
user = crud.user.create(db_session, user_in=user_in)
is_superuser = crud.user.is_superuser(user)
assert is_superuser is True
@@ -67,7 +67,7 @@ def test_check_if_user_is_superuser():
def test_check_if_user_is_superuser_normal_user():
username = random_lower_string()
password = random_lower_string()
- user_in = UserInCreate(email=username, password=password)
+ user_in = UserCreate(email=username, password=password)
user = crud.user.create(db_session, user_in=user_in)
is_superuser = crud.user.is_superuser(user)
assert is_superuser is False
@@ -76,7 +76,7 @@ def test_check_if_user_is_superuser_normal_user():
def test_get_user():
password = random_lower_string()
username = random_lower_string()
- user_in = UserInCreate(email=username, password=password, is_superuser=True)
+ user_in = UserCreate(email=username, password=password, is_superuser=True)
user = crud.user.create(db_session, user_in=user_in)
user_2 = crud.user.get(db_session, user_id=user.id)
assert user.email == user_2.email
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/tests/utils/item.py b/{{cookiecutter.project_slug}}/backend/app/app/tests/utils/item.py
new file mode 100644
index 0000000000..49398fa55d
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/backend/app/app/tests/utils/item.py
@@ -0,0 +1,17 @@
+from app import crud
+from app.db.session import db_session
+from app.models.item import ItemCreate
+from app.tests.utils.user import create_random_user
+from app.tests.utils.utils import random_lower_string
+
+
+def create_random_item(owner_id: int = None):
+ if owner_id is None:
+ user = create_random_user()
+ owner_id = user.id
+ title = random_lower_string()
+ description = random_lower_string()
+ item_in = ItemCreate(title=title, description=description, id=id)
+ return crud.item.create(
+ db_session=db_session, item_in=item_in, owner_id=owner_id
+ )
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/tests/utils/user.py b/{{cookiecutter.project_slug}}/backend/app/app/tests/utils/user.py
index 9f8009fc3a..6a5b947e4a 100644
--- a/{{cookiecutter.project_slug}}/backend/app/app/tests/utils/user.py
+++ b/{{cookiecutter.project_slug}}/backend/app/app/tests/utils/user.py
@@ -1,6 +1,10 @@
import requests
+from app import crud
from app.core import config
+from app.db.session import db_session
+from app.models.user import UserCreate
+from app.tests.utils.utils import random_lower_string
def user_authentication_headers(server_api, email, password):
@@ -11,3 +15,11 @@ def user_authentication_headers(server_api, email, password):
auth_token = response["access_token"]
headers = {"Authorization": f"Bearer {auth_token}"}
return headers
+
+
+def create_random_user():
+ email = random_lower_string()
+ password = random_lower_string()
+ user_in = UserCreate(username=email, email=email, password=password)
+ user = crud.user.create(db_session=db_session, user_in=user_in)
+ return user
diff --git a/{{cookiecutter.project_slug}}/backend/app/app/tests_pre_start.py b/{{cookiecutter.project_slug}}/backend/app/app/tests_pre_start.py
index 7a2a78b6ec..c1b0ccaf1a 100644
--- a/{{cookiecutter.project_slug}}/backend/app/app/tests_pre_start.py
+++ b/{{cookiecutter.project_slug}}/backend/app/app/tests_pre_start.py
@@ -3,7 +3,7 @@
from tenacity import after_log, before_log, retry, stop_after_attempt, wait_fixed
from app.db.session import db_session
-from app.tests.api.api_v1.test_token import test_get_access_token
+from app.tests.api.api_v1.test_login import test_get_access_token
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
diff --git a/{{cookiecutter.project_slug}}/backend/app/scripts/lint.sh b/{{cookiecutter.project_slug}}/backend/app/scripts/lint.sh
index 08bb841e97..11184afd99 100644
--- a/{{cookiecutter.project_slug}}/backend/app/scripts/lint.sh
+++ b/{{cookiecutter.project_slug}}/backend/app/scripts/lint.sh
@@ -5,4 +5,4 @@ set -x
autoflake --remove-all-unused-imports --recursive --remove-unused-variables --in-place app --exclude=__init__.py
isort --multi-line=3 --trailing-comma --force-grid-wrap=0 --combine-as --line-width 88 --recursive --apply app
black app
-vulture app
+vulture app --min-confidence 70
diff --git a/{{cookiecutter.project_slug}}/backend/backend.dockerfile b/{{cookiecutter.project_slug}}/backend/backend.dockerfile
index 35a2b613f4..60fd3b3018 100644
--- a/{{cookiecutter.project_slug}}/backend/backend.dockerfile
+++ b/{{cookiecutter.project_slug}}/backend/backend.dockerfile
@@ -1,6 +1,6 @@
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7
-RUN pip install celery~=4.3 passlib[bcrypt] tenacity requests emails "fastapi>=0.7.1" uvicorn gunicorn pyjwt python-multipart email_validator jinja2 psycopg2-binary alembic SQLAlchemy
+RUN pip install celery~=4.3 passlib[bcrypt] tenacity requests emails "fastapi>=0.16.0" uvicorn gunicorn pyjwt python-multipart email_validator jinja2 psycopg2-binary alembic SQLAlchemy
# For development, Jupyter remote kernel, Hydrogen
# Using inside the container:
diff --git a/{{cookiecutter.project_slug}}/backend/celeryworker.dockerfile b/{{cookiecutter.project_slug}}/backend/celeryworker.dockerfile
index bec8c5a1c5..b0d5497c10 100644
--- a/{{cookiecutter.project_slug}}/backend/celeryworker.dockerfile
+++ b/{{cookiecutter.project_slug}}/backend/celeryworker.dockerfile
@@ -1,6 +1,6 @@
FROM python:3.7
-RUN pip install raven celery~=4.3 passlib[bcrypt] tenacity requests "fastapi>=0.7.1" emails pyjwt email_validator jinja2 psycopg2-binary alembic SQLAlchemy
+RUN pip install raven celery~=4.3 passlib[bcrypt] tenacity requests "fastapi>=0.16.0" emails pyjwt email_validator jinja2 psycopg2-binary alembic SQLAlchemy
# For development, Jupyter remote kernel, Hydrogen
# Using inside the container:
diff --git a/{{cookiecutter.project_slug}}/backend/tests.dockerfile b/{{cookiecutter.project_slug}}/backend/tests.dockerfile
index 390b7ee0e2..55d7036481 100644
--- a/{{cookiecutter.project_slug}}/backend/tests.dockerfile
+++ b/{{cookiecutter.project_slug}}/backend/tests.dockerfile
@@ -1,6 +1,6 @@
FROM python:3.7
-RUN pip install requests pytest tenacity passlib[bcrypt] "fastapi>=0.7.1" psycopg2-binary SQLAlchemy
+RUN pip install requests pytest tenacity passlib[bcrypt] "fastapi>=0.16.0" psycopg2-binary SQLAlchemy
# For development, Jupyter remote kernel, Hydrogen
# Using inside the container: