Adding docs
This commit is contained in:
parent
f27164e39d
commit
67b7dba341
@ -134,8 +134,8 @@ public class RequestClassGenerator
|
|||||||
var fullMatch = match.Groups[0].Value;
|
var fullMatch = match.Groups[0].Value;
|
||||||
|
|
||||||
// Ignore primary_key fields
|
// Ignore primary_key fields
|
||||||
if (fullMatch.IndexOf("primary_key", StringComparison.Ordinal) <
|
if (fullMatch.IndexOf("=", StringComparison.Ordinal) <
|
||||||
fullMatch.IndexOf("=", StringComparison.Ordinal)) continue;
|
fullMatch.IndexOf("primary_key", StringComparison.Ordinal)) continue;
|
||||||
|
|
||||||
// Ignore relationship fields, these need to be done manually
|
// Ignore relationship fields, these need to be done manually
|
||||||
if (fullMatch.IndexOf("=", StringComparison.Ordinal) <
|
if (fullMatch.IndexOf("=", StringComparison.Ordinal) <
|
||||||
|
@ -33,7 +33,7 @@ public static class ServiceCollectionExtensions
|
|||||||
services.AddScoped<ISubCommandOf<Commands.MycroForge.Api.Generate>, Commands.MycroForge.Api.Generate.Router>();
|
services.AddScoped<ISubCommandOf<Commands.MycroForge.Api.Generate>, Commands.MycroForge.Api.Generate.Router>();
|
||||||
services.AddScoped<ISubCommandOf<Commands.MycroForge.Api.Generate>, Commands.MycroForge.Api.Generate.Crud>();
|
services.AddScoped<ISubCommandOf<Commands.MycroForge.Api.Generate>, Commands.MycroForge.Api.Generate.Crud>();
|
||||||
|
|
||||||
// Register "m4g orm"
|
// Register "m4g db"
|
||||||
services.AddScoped<ISubCommandOf<Commands.MycroForge>, Commands.MycroForge.Db>();
|
services.AddScoped<ISubCommandOf<Commands.MycroForge>, Commands.MycroForge.Db>();
|
||||||
services.AddScoped<ISubCommandOf<Commands.MycroForge.Db>, Commands.MycroForge.Db.Migrate>();
|
services.AddScoped<ISubCommandOf<Commands.MycroForge.Db>, Commands.MycroForge.Db.Migrate>();
|
||||||
services.AddScoped<ISubCommandOf<Commands.MycroForge.Db>, Commands.MycroForge.Db.Rollback>();
|
services.AddScoped<ISubCommandOf<Commands.MycroForge.Db>, Commands.MycroForge.Db.Rollback>();
|
||||||
|
231
docs/todo-example/README.md
Normal file
231
docs/todo-example/README.md
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
# Quick tutorial
|
||||||
|
|
||||||
|
Summary: We're gonna build a todo app to demonstrate how the `m4g` command works.<br/>
|
||||||
|
TODO: Supplement this section.
|
||||||
|
|
||||||
|
## Initialize the project
|
||||||
|
|
||||||
|
Run `m4g init todo-example` to initialize a new project.
|
||||||
|
|
||||||
|
Open the newly created project by running `code todo-example`, make sure you have `vscode` on you machine before running
|
||||||
|
this command. If you prefer using another editor, then manually open the generated folder.
|
||||||
|
|
||||||
|
## Setup the database
|
||||||
|
|
||||||
|
Summary: This section explains how to interact with the database.
|
||||||
|
TODO: Supplement this section.
|
||||||
|
|
||||||
|
### Create a docker compose file for the database
|
||||||
|
|
||||||
|
Create a file called `docker-compose.yml` in the root of your project with the following content.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
# MariaDB for the database
|
||||||
|
todo_db:
|
||||||
|
image: 'mariadb:10.4'
|
||||||
|
container_name: 'todo_db'
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- default
|
||||||
|
ports:
|
||||||
|
- '3306:3306'
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: 'root'
|
||||||
|
MYSQL_USER: 'root'
|
||||||
|
MYSQL_PASSWORD: 'root'
|
||||||
|
MYSQL_DATABASE: 'todo'
|
||||||
|
volumes:
|
||||||
|
- 'todo_db:/var/lib/mysql'
|
||||||
|
|
||||||
|
# PhpMyAdmin for the database UI
|
||||||
|
phpmyadmin:
|
||||||
|
image: phpmyadmin/phpmyadmin
|
||||||
|
container_name: phpmyadmin
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- 8888:80
|
||||||
|
networks:
|
||||||
|
- default
|
||||||
|
environment:
|
||||||
|
PMA_HOST: todo_db
|
||||||
|
PMA_PORT: 3306
|
||||||
|
PMA_ARBITRARY: 1
|
||||||
|
links:
|
||||||
|
- todo_db
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
todo_db:
|
||||||
|
```
|
||||||
|
|
||||||
|
Run `docker compose up -d` to start the database containers.<br/>
|
||||||
|
You should now be able to log into the database server at http://localhost:8888, with the following credentials.<br/>
|
||||||
|
**username**: root, **password**: root
|
||||||
|
|
||||||
|
### Update the database connectionstring
|
||||||
|
|
||||||
|
Replace the contents of `db/settings.py` with the following.
|
||||||
|
|
||||||
|
```python
|
||||||
|
class DbSettings:
|
||||||
|
def get_connectionstring() -> str:
|
||||||
|
connectionstring = "mysql+asyncmy://root:root@localhost:3306/todo"
|
||||||
|
return connectionstring
|
||||||
|
```
|
||||||
|
|
||||||
|
The connectionstring has been hardcoded for demo purposes.
|
||||||
|
In a production app, you would use some kind of secret manager to retrieve it.
|
||||||
|
|
||||||
|
### Create the entities
|
||||||
|
|
||||||
|
`m4g db generate entity Tag -c "description:str:String(255)"`
|
||||||
|
|
||||||
|
`m4g db generate entity Todo -c "description:str:String(255)" -c "is_done:bool:Boolean()"`
|
||||||
|
|
||||||
|
### Define a many to many relation between Todo & Tag
|
||||||
|
|
||||||
|
`m4g db link many Todo --to-many Tag`
|
||||||
|
|
||||||
|
### Generate the migration
|
||||||
|
|
||||||
|
`m4g db generate migration initial_migration`
|
||||||
|
|
||||||
|
### Apply the migration
|
||||||
|
|
||||||
|
`m4g db migrate`
|
||||||
|
|
||||||
|
If you inspect the database in [PhpMyAdmin](http://localhost:8888), you should now see a populated schema.
|
||||||
|
|
||||||
|
## Setup the API
|
||||||
|
|
||||||
|
### Generate CRUD for Tag & Todo
|
||||||
|
|
||||||
|
`m4g api generate crud Tag`
|
||||||
|
|
||||||
|
`m4g api generate crud Todo`
|
||||||
|
|
||||||
|
### Modify generated TodoService
|
||||||
|
|
||||||
|
Add the following import in `api/services/todo_service.py`.
|
||||||
|
|
||||||
|
`from sqlalchemy.orm import selectinload`
|
||||||
|
|
||||||
|
Modify `TodoService.list`
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Before
|
||||||
|
async with async_session() as session:
|
||||||
|
stmt = select(Todo)
|
||||||
|
results = (await session.scalars(stmt)).all()
|
||||||
|
return results
|
||||||
|
|
||||||
|
# After
|
||||||
|
async with async_session() as session:
|
||||||
|
stmt = select(Todo).options(selectinload(Todo.tags))
|
||||||
|
results = (await session.scalars(stmt)).all()
|
||||||
|
return results
|
||||||
|
```
|
||||||
|
|
||||||
|
Modify `TodoService.get_by_id`
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Before
|
||||||
|
async def get_by_id(self, id: int) -> Optional[Todo]:
|
||||||
|
async with async_session() as session:
|
||||||
|
stmt = select(Todo).where(Todo.id == id)
|
||||||
|
result = (await session.scalars(stmt)).first()
|
||||||
|
return result
|
||||||
|
|
||||||
|
# After
|
||||||
|
async def get_by_id(self, id: int) -> Optional[Todo]:
|
||||||
|
async with async_session() as session:
|
||||||
|
stmt = select(Todo).where(Todo.id == id).options(selectinload(Todo.tags))
|
||||||
|
result = (await session.scalars(stmt)).first()
|
||||||
|
return result
|
||||||
|
```
|
||||||
|
|
||||||
|
Modify `TodoService.create`
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Before
|
||||||
|
async def create(self, data: Dict[str, Any]) -> None:
|
||||||
|
async with async_session() as session:
|
||||||
|
entity = Todo(**data)
|
||||||
|
session.add(entity)
|
||||||
|
await session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
# After
|
||||||
|
async def create(self, data: Dict[str, Any]) -> None:
|
||||||
|
tag_ids = []
|
||||||
|
|
||||||
|
if "tag_ids" in data.keys():
|
||||||
|
tag_ids = data["tag_ids"]
|
||||||
|
del data["tag_ids"]
|
||||||
|
|
||||||
|
async with async_session() as session:
|
||||||
|
entity = Todo(**data)
|
||||||
|
|
||||||
|
if len(tag_ids) > 0:
|
||||||
|
stmt = select(Tag).where(Tag.id.in_(tag_ids))
|
||||||
|
result = (await session.scalars(stmt)).all()
|
||||||
|
entity.tags = list(result)
|
||||||
|
|
||||||
|
session.add(entity)
|
||||||
|
await session.commit()
|
||||||
|
```
|
||||||
|
|
||||||
|
Modify `TodoService.update`
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Before
|
||||||
|
async def update(self, id: int, data: Dict[str, Any]) -> bool:
|
||||||
|
tag_ids = []
|
||||||
|
|
||||||
|
if "tag_ids" in data.keys():
|
||||||
|
tag_ids = data["tag_ids"]
|
||||||
|
del data["tag_ids"]
|
||||||
|
|
||||||
|
async with async_session() as session:
|
||||||
|
stmt = select(Todo).where(Todo.id == id)
|
||||||
|
entity = (await session.scalars(stmt)).first()
|
||||||
|
|
||||||
|
if entity is None:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
for key, value in data.items():
|
||||||
|
setattr(entity, key, value)
|
||||||
|
await session.commit()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# After
|
||||||
|
async def update(self, id: int, data: Dict[str, Any]) -> bool:
|
||||||
|
tag_ids = []
|
||||||
|
|
||||||
|
if "tag_ids" in data.keys():
|
||||||
|
tag_ids = data["tag_ids"]
|
||||||
|
del data["tag_ids"]
|
||||||
|
|
||||||
|
async with async_session() as session:
|
||||||
|
stmt = select(Todo).where(Todo.id == id).options(selectinload(Todo.tags))
|
||||||
|
entity = (await session.scalars(stmt)).first()
|
||||||
|
|
||||||
|
if entity is None:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
for key, value in data.items():
|
||||||
|
setattr(entity, key, value)
|
||||||
|
|
||||||
|
if len(tag_ids) > 0:
|
||||||
|
stmt = select(Tag).where(Tag.id.in_(tag_ids))
|
||||||
|
result = (await session.scalars(stmt)).all()
|
||||||
|
entity.tags = list(result)
|
||||||
|
else:
|
||||||
|
entity.tags = []
|
||||||
|
|
||||||
|
await session.commit()
|
||||||
|
return True
|
||||||
|
```
|
Loading…
Reference in New Issue
Block a user