Skip to content

Commit 202ab4f

Browse files
committed
Some updates to the readme
1 parent abf1ff8 commit 202ab4f

5 files changed

Lines changed: 307 additions & 59 deletions

File tree

README.md

Lines changed: 262 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ And more — see [feature list](https://duckframework.com/features)
7575
10. **Complete Reverse Proxy Server****Duck** only acts as reverse proxy for Django only. Need to make Duck a full-fledged reverse proxy server with optional sticky sessions.
7676
11. ~~**Component Mutation Observer** – Need to build an optional component mutation observer for keeping track of child changes for fastest re-render (approx. 75x fast on unchanged children).~~
7777
12. **MCP (Model Context Protocol) Server** – Need to make it easy for creating MCP servers for easy AI communication.
78-
13. **...and more**[Request a feature](./feature_request.md)
78+
13. **JWT (JSON-based Web Token) Authentication ** – Need to add JWT authentication persistent logins.
79+
14. **...and more**[Request a feature](./feature_request.md)
7980

8081
---
8182

@@ -145,6 +146,265 @@ This starts the server at **http://localhost:8000**
145146

146147
---
147148

149+
## Understanding the Project
150+
151+
Duck generates a set of files and directories as you build your application. This section walks through the core ones you'll interact with most.
152+
153+
---
154+
155+
### `web/main.py`
156+
157+
The entry point for your Duck application. You can run it directly with `python web/main.py`, or use the `duck runserver` command instead.
158+
159+
```py
160+
#!/usr/bin/env python
161+
"""
162+
Main script for creating and running the Duck application.
163+
"""
164+
165+
from duck.app import App
166+
167+
app = App(port=8000, addr="0.0.0.0", domain="localhost")
168+
169+
if __name__ == "__main__":
170+
app.run()
171+
```
172+
173+
---
174+
175+
### `web/urls.py`
176+
177+
Defines the URL routes for your application. Each route maps a static or dynamic path to a **view** — a callable that handles incoming requests for that path.
178+
179+
By default, `urlpatterns` is an empty list. Add your own routes to wire up the app.
180+
181+
**HTTP route example:**
182+
183+
```py
184+
from duck.urls import path
185+
from duck.http.response import HttpResponse
186+
187+
def home(request):
188+
return HttpResponse("Hello world")
189+
190+
urlpatterns = [
191+
path('/', home, name="home"),
192+
]
193+
```
194+
195+
**WebSocket route example:**
196+
197+
```py
198+
from duck.urls import path
199+
from duck.contrib.websockets import WebSocketView
200+
201+
class SomeWebSocket(WebSocketView):
202+
async def on_receive(self, data: bytes, opcode):
203+
# Handle incoming WebSocket data
204+
await self.send_text("Some text")
205+
206+
# Other available send methods:
207+
# send_json, send_binary, send_ping, send_pong, send_close
208+
209+
urlpatterns = [
210+
path('/some_endpoint', SomeWebSocket, name="some_ws_endpoint"),
211+
]
212+
```
213+
214+
---
215+
216+
### `web/views.py`
217+
218+
An optional file for organising your view functions. Import it as a module in `urls.py` to keep your routes clean.
219+
220+
```py
221+
# web/urls.py
222+
from duck.urls import path
223+
from . import views
224+
225+
urlpatterns = [
226+
path('/', views.home, name="home"),
227+
]
228+
```
229+
230+
---
231+
232+
### `web/ui/`
233+
234+
Contains all frontend logic — components, pages, templates, and static files.
235+
236+
---
237+
238+
#### `web/ui/pages/`
239+
240+
Duck recommends building UI with **Pages** — Python classes that represent full HTML pages. Pages unlock the [Lively Component System](https://docs.duckframework.com/main/lively-components), which enables fast navigation and real-time interactivity without JavaScript or full page reloads.
241+
242+
**What is an HTML Component?**
243+
244+
A component is a Python class that represents an HTML element. You configure it with props and style, then render it to HTML.
245+
246+
```py
247+
from duck.html.components import InnerComponent
248+
249+
class Button(InnerComponent):
250+
def get_element(self):
251+
return "button"
252+
253+
btn = Button(text="Hello world")
254+
255+
print(btn.render()) # <button>Hello world</button>
256+
```
257+
258+
Duck ships with many built-in components — `Button`, `Navbar`, `Modal`, `Input`, and more — available under `duck.html.components`.
259+
260+
**Creating Pages**
261+
262+
Subclass `duck.html.components.page.Page` to create a page. The recommended pattern is a `BasePage` that defines the shared layout, with individual pages overriding only what they need.
263+
264+
```py
265+
# web/ui/pages/base.py
266+
from duck.html.components.container import FlexContainer
267+
from duck.html.components.page import Page
268+
269+
class BasePage(Page):
270+
def on_create(self):
271+
super().on_create()
272+
self.set_title("MySite")
273+
self.set_description("Some base description ...")
274+
275+
# Set up the root layout container
276+
self.main = FlexContainer(flex_direction="column")
277+
self.add_to_body(self.main)
278+
279+
self.build_layout(self.main)
280+
281+
def build_layout(self, main):
282+
# Override in subclasses to define page-specific layout
283+
pass
284+
```
285+
286+
```py
287+
# web/ui/pages/home.py
288+
from duck.html.components.container import Container
289+
from web.ui.pages.base import BasePage
290+
291+
class HomePage(BasePage):
292+
def build_layout(self, main):
293+
main.add_child(Container(text="Hello world"))
294+
```
295+
296+
**Using Pages in views:**
297+
298+
```py
299+
# web/views.py
300+
from duck.shortcuts import to_response
301+
302+
def home(request):
303+
return to_response(HomePage(request))
304+
```
305+
306+
> Pages automatically enable fast client-side navigation via Lively. Unlike templates, switching between pages does not trigger a full reload.
307+
308+
---
309+
310+
#### `web/ui/components/`
311+
312+
Where your custom reusable components live. The example below shows a feedback form with real-time UI updates powered by Lively.
313+
314+
```py
315+
# web/ui/components/form.py
316+
from duck.html.components.form import Form
317+
from duck.html.components.input import Input, InputWithLabel
318+
from duck.html.components.textarea import TextArea
319+
from duck.html.components.button import Button
320+
from duck.html.components.label import Label
321+
322+
class MyFeedbackForm(Form):
323+
def on_create(self):
324+
super().on_create()
325+
326+
# Status label for displaying feedback or errors
327+
self.label = Label(text="")
328+
329+
self.add_children([
330+
self.label,
331+
InputWithLabel(
332+
label_text="Your name",
333+
input=Input(name="name", type="text", placeholder="Enter your name", required=True),
334+
),
335+
InputWithLabel(
336+
label_text="Your message",
337+
input=TextArea(name="message", placeholder="Your message", required=True),
338+
),
339+
Button(text="Submit", props={"type": "submit"}),
340+
])
341+
342+
# Bind submit event — update_targets lists components to re-render on the client
343+
self.bind("submit", self.on_form_submit, update_self=True, update_targets=[self.label])
344+
345+
async def on_form_submit(self, form, event, form_inputs, ws):
346+
name = form_inputs.get("name").strip()
347+
message = form_inputs.get("message").strip()
348+
349+
# Validate and persist the message here
350+
351+
# Patch the label in-place on the client
352+
self.label.text = "Your message has been received"
353+
self.label.color = "green"
354+
```
355+
356+
---
357+
358+
#### `web/ui/templates/`
359+
360+
Prefer classic server-rendered templates? Store them here. Duck supports both **Django** and **Jinja2** template engines.
361+
362+
```django
363+
{# web/ui/templates/home.html #}
364+
{% extends 'base.html' %}
365+
366+
{% block main %}
367+
Hello world!
368+
{% endblock main %}
369+
```
370+
371+
```py
372+
# web/views.py
373+
from duck.shortcuts import render, async_render
374+
375+
def home(request):
376+
return render("home.html", engine="django") # or engine="jinja2"
377+
378+
async def async_home(request):
379+
return await async_render("home.html", engine="django")
380+
```
381+
382+
> You can also use HTML components inside templates. See [Lively Components](https://docs.duckframework.com/main/lively-components) for details.
383+
384+
#### `web/ui/static/`
385+
386+
This directory contains all the static files like css/js files, images or videos for your application.
387+
388+
> Instead of hard-coding static file URLs in components or templates, use the `static` function in `duck.shortcuts` module.
389+
390+
```py
391+
# views.py
392+
from duck.shortcuts import static
393+
394+
def home(request):
395+
# Instead of:
396+
my_image_url = "/static/images/my-image.png"
397+
398+
# Do this instead:
399+
my_image_url = static("images/my-image.png")
400+
401+
return "Hello world" # Anything here.
402+
```
403+
404+
> The same applies with hard-coding internal URLs, use the `resolve()` function in `duck.shortcuts`.
405+
406+
---
407+
148408
## Django Integration
149409

150410
If you have an existing **Django** project and want production features like **HTTPS**, **HTTP/2**, and **resumable downloads**, Duck makes it easy.
@@ -200,7 +460,7 @@ duck runserver -dj
200460

201461
### Sponsorship/Donations:
202462

203-
Support development at [Open Collective](https://opencollective.com/duckframework/contribute)
463+
Support development on [Patreon](https://patreon.com/duckframework)
204464

205465
### Report issues:
206466

duck/backend/django/urls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def get_correct_urlpatterns() -> List:
4545
urlpatterns = []
4646
duck_urlpatterns = get_duck_urlpatterns()
4747

48-
# make new django urlpatterns
48+
# Make new django urlpatterns
4949
for urlpattern in duck_urlpatterns:
5050
url, name, methods = (
5151
urlpattern.get("url"),

duck/contrib/reloader/ducksight.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
**Ducksight Reloader**
2+
**Duck Sight Reloader**
33
44
Watches for file changes in Duck framework projects and restarts the
55
webserver in DEBUG mode whenever relevant `.py` files change.

0 commit comments

Comments
 (0)