Skip to content

Commit 3aa280e

Browse files
committed
Added code sample and fixed failing tests
1 parent 9a98ed0 commit 3aa280e

File tree

31 files changed

+611
-27
lines changed

31 files changed

+611
-27
lines changed

.github/workflows/test_full.yml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,8 @@ jobs:
1111
strategy:
1212
matrix:
1313
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
14-
django-version: ['<3.2', '<3.3', '<4.2', '<4.3', '<5.1']
14+
django-version: ['<3.2', '<3.3', '<4.2', '<4.3']
1515
exclude:
16-
- python-version: '3.8'
17-
django-version: '<5.1'
18-
- python-version: '3.9'
19-
django-version: '<5.1'
2016
- python-version: '3.12'
2117
django-version: '<3.2'
2218
- python-version: '3.12'

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ MIDDLEWARE = [
205205
"whitenoise.middleware.WhiteNoiseMiddleware",
206206
...
207207
]
208+
WHITENOISE_STATIC_PREFIX = '/static/'
208209
...
209210
```
210211
So when you visit [http://localhost:8000/-django-example/admin](http://localhost:8000/-django-example/admin) again, the page should be functional.

ellar_django/commands.py

Lines changed: 144 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,156 @@
11
import sys
2+
import typing as t
23

3-
import click
4+
import django
5+
import ellar_cli.click as click
46

5-
HELP_MESSAGE = """
6-
Ellar will always intercept and command with '--help'.
77

8-
So if you want to get help on any django command,
9-
simply wrap the command and --help in quotes
8+
class _CommandItem(t.NamedTuple):
9+
name: str
10+
description: str
1011

11-
For example: ellar django 'migrate --help' or python manage.py django 'migrate --help'
12-
"""
1312

13+
_django_support_commands: t.List[_CommandItem] = [
14+
_CommandItem(
15+
name="migrate",
16+
description="Synchronizes the database state with the current set of models and migrations",
17+
),
18+
_CommandItem(
19+
name="makemigrations",
20+
description="Creates new migrations based on the changes detected to your models.",
21+
),
22+
_CommandItem(
23+
name="check",
24+
description="inspects the entire Django project for common problems",
25+
),
26+
_CommandItem(
27+
name="createcachetable",
28+
description="Creates the cache tables for use with the database cache backend",
29+
),
30+
_CommandItem(
31+
name="dbshell",
32+
description="Runs the command-line client for the database engine",
33+
),
34+
_CommandItem(
35+
name="diffsettings",
36+
description="Displays differences between the current settings and default settings",
37+
),
38+
_CommandItem(
39+
name="dumpdata",
40+
description="Outputs to standard output all data in the database",
41+
),
42+
_CommandItem(
43+
name="flush",
44+
description="Removes all data from the database and re-executes any post-synchronization handlers",
45+
),
46+
_CommandItem(
47+
name="inspectdb", description="Introspects the database tables in the database"
48+
),
49+
_CommandItem(
50+
name="loaddata",
51+
description="Searches for and loads the contents into the database.",
52+
),
53+
_CommandItem(
54+
name="optimizemigration",
55+
description="Optimizes the operations for the named migration and overrides the existing file",
56+
),
57+
_CommandItem(
58+
name="showmigrations", description="Shows all migrations in a project."
59+
),
60+
_CommandItem(
61+
name="sqlflush",
62+
description="Prints the SQL statements that would be executed for the flush command",
63+
),
64+
_CommandItem(
65+
name="sqlmigrate", description="Prints the SQL for the named migration."
66+
),
67+
_CommandItem(
68+
name="sqlsequencereset",
69+
description="Prints the SQL statements for resetting sequences for the given app name(s)",
70+
),
71+
_CommandItem(
72+
name="squashmigrations", description="Squashes the migrations for app_label"
73+
),
74+
_CommandItem(
75+
name="startapp", description="Creates a Django app directory structure"
76+
),
77+
_CommandItem(
78+
name="changepassword",
79+
description="This command is only available if Django’s authentication system",
80+
),
81+
_CommandItem(
82+
name="createsuperuser",
83+
description="Creates a superuser account (a user who has all permissions)",
84+
),
85+
_CommandItem(
86+
name="collectstatic", description="Expose static files to STATIC_ROOT folder"
87+
),
88+
_CommandItem(name="findstatic", description="Search for a static file location"),
89+
_CommandItem(
90+
name="clearsessions",
91+
description="Can be run as a cron job or directly to clean out expired sessions.",
92+
),
93+
]
1494

15-
@click.command(
95+
96+
def version_callback(ctx: click.Context, _: t.Any, value: bool) -> None:
97+
if value:
98+
click.echo(f"Django Version: {django.__version__}")
99+
raise click.Exit(0)
100+
101+
102+
@click.group(
16103
name="django",
17-
context_settings={"ignore_unknown_options": True, "allow_extra_args": True},
18-
help=HELP_MESSAGE,
104+
help="- Ellar Django Commands",
19105
)
20-
def django_command_wrapper() -> None:
21-
from django.core.management import execute_from_command_line
106+
@click.option(
107+
"-v",
108+
"--version",
109+
callback=version_callback,
110+
help="Show the version and exit.",
111+
is_flag=True,
112+
expose_value=False,
113+
is_eager=True,
114+
)
115+
@click.pass_context
116+
def django_command(ctx: click.Context) -> None:
117+
pass
118+
119+
120+
def _add_django_command(command_item: _CommandItem) -> None:
121+
def help_callback(ctx: click.Context, _: t.Any, value: bool) -> None:
122+
from django.core.management import execute_from_command_line
123+
124+
if value:
125+
args = ["manage.py", command_item.name, "--help"]
126+
127+
execute_from_command_line(args)
128+
raise click.Exit(0)
129+
130+
@django_command.command(
131+
name=command_item.name,
132+
help=command_item.description,
133+
context_settings={"ignore_unknown_options": True, "allow_extra_args": True},
134+
add_help_option=False,
135+
)
136+
@click.option(
137+
"-h",
138+
"--help",
139+
callback=help_callback,
140+
help="Show the version and exit.",
141+
is_flag=True,
142+
expose_value=False,
143+
is_eager=True,
144+
)
145+
@click.with_app_context
146+
def _command() -> None:
147+
from django.core.management import execute_from_command_line
148+
149+
args = ["manage.py", command_item.name]
22150

23-
args = []
24-
skip = 1
151+
for item in sys.argv[3:]:
152+
args.extend(item.split(" "))
153+
execute_from_command_line(args)
25154

26-
if "manage.py" in sys.argv: # pragma: no cover
27-
skip = 2
28155

29-
for item in sys.argv[skip:]:
30-
args.extend(item.split(" "))
31-
execute_from_command_line(args)
156+
list(map(_add_django_command, _django_support_commands))

ellar_django/module.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from starlette.responses import RedirectResponse
88
from starlette.routing import Mount
99

10-
from .commands import django_command_wrapper
10+
from .commands import django_command
1111
from .middleware import DjangoAdminRedirectMiddleware
1212

1313
_router = ModuleRouter()
@@ -18,7 +18,7 @@ async def _redirect_route(req: Request) -> RedirectResponse:
1818
return RedirectResponse(url=str(req.base_url))
1919

2020

21-
@Module(commands=[django_command_wrapper])
21+
@Module(commands=[django_command])
2222
class DjangoModule(IModuleSetup):
2323
@classmethod
2424
def setup(cls, settings_module: str, path_prefix: str = "/dj") -> "DynamicModule":

sample/ellar-and-django-orm/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
## Introduction
2+
This is an illustration of using DjangoORM within your Ellar Project.
3+
4+
## Project setup
5+
```
6+
pip install -r requirements.txt
7+
```
8+
9+
## Apply Migrations
10+
```shell
11+
python manage.py django migrate
12+
```
13+
14+
## Development Server
15+
```
16+
python manage.py runserver --reload
17+
```

sample/ellar-and-django-orm/ellar_and_django_orm/__init__.py

Whitespace-only changes.

sample/ellar-and-django-orm/ellar_and_django_orm/apps/__init__.py

Whitespace-only changes.

sample/ellar-and-django-orm/ellar_and_django_orm/apps/event/__init__.py

Whitespace-only changes.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""
2+
Define endpoints routes in python class-based fashion
3+
example:
4+
5+
@Controller("/dogs", tag="Dogs", description="Dogs Resources")
6+
class MyController(ControllerBase):
7+
@get('/')
8+
def index(self):
9+
return {'detail': "Welcome Dog's Resources"}
10+
"""
11+
12+
import typing as t
13+
14+
from ellar.common import Controller, ControllerBase, get, post
15+
16+
from ...interfaces.events_repository import IEventRepository
17+
from .schemas import EventSchema, EventSchemaOut
18+
19+
20+
@Controller("/event")
21+
class EventController(ControllerBase):
22+
def __init__(self, event_repo: IEventRepository):
23+
self.event_repo = event_repo
24+
25+
@post("/", response={201: EventSchemaOut})
26+
def create_event(self, event: EventSchema):
27+
event = self.event_repo.create_event(**event.dict())
28+
return 201, event
29+
30+
@get("/", response=t.List[EventSchema], name="event-list")
31+
def list_events(self):
32+
return list(self.event_repo.list_events())
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
@Module(
3+
controllers=[MyController],
4+
providers=[
5+
YourService,
6+
ProviderConfig(IService, use_class=AService),
7+
ProviderConfig(IFoo, use_value=FooService()),
8+
],
9+
routers=(routerA, routerB)
10+
statics='statics',
11+
template='template_folder',
12+
# base_directory -> default is the `event` folder
13+
)
14+
class MyModule(ModuleBase):
15+
def register_providers(self, container: Container) -> None:
16+
# for more complicated provider registrations
17+
pass
18+
19+
"""
20+
21+
from ellar.common import Module
22+
from ellar.core import ModuleBase
23+
from ellar.di import Container
24+
25+
from .controllers import EventController
26+
27+
28+
@Module(
29+
controllers=[EventController],
30+
providers=[],
31+
routers=[],
32+
)
33+
class EventModule(ModuleBase):
34+
"""
35+
Event Module
36+
"""
37+
38+
def register_providers(self, container: Container) -> None:
39+
"""for more complicated provider registrations, use container.register_instance(...)"""

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy