Skip to content

Commit

Permalink
Update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
gi0baro committed Oct 10, 2024
1 parent 984115a commit 39b7efd
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 6 deletions.
7 changes: 4 additions & 3 deletions docs/forms.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ Here is the complete list of parameters accepted by `Form` class:
> **Note:** the `fields` and `exclude_fields` parameters should not be used together. If you need to hide just a few fields, you'd better using the `exclude_fields`, and you should use `fields` if you have to show only few table fields. The advantages of these parameters are lost if you use both.
### Uploads with forms

As we saw above, the `upload` parameter of forms needs an URL for download. Let's focus a bit on uploads and see an example to completely understand this requirement.

Let's say you want to handle the upload of avatar images from your user. So, in your model you would have an upload field:
Expand All @@ -99,14 +100,14 @@ Let's say you want to handle the upload of avatar images from your user. So, in
avatar = Field.upload()
```

and the forms produced by Emmett will handle uploads for you. How would you display this image in your template? You need a streaming function like this:
and the forms produced by Emmett will handle uploads for you. How would you display this image in your template? You need a route which will send back the uploaded files' contents:

```python
from emmett.helpers import stream_dbfile
from emmett import response

@app.route("/download/<str:filename>")
async def download(filename):
stream_dbfile(db, filename)
return response.wrap_dbfile(db, filename)
```

and then, in your template, you can create an `img` tag pointing to the `download` function you've just exposed:
Expand Down
2 changes: 2 additions & 0 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,8 @@ The output will be a JSON object with the converted content of your Python dicti

The `service` module has other helpers, like *XML* format: go further in the [Services chapter](./services) of the documentation.

Also, Emmett allows you to respond with streams and files: check the [Responses chapter](./response#wrapping-methods) of the documentation for further details.

Dealing with requests
---------------------

Expand Down
51 changes: 49 additions & 2 deletions docs/request.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,18 @@ Now, let's see how to deal with request variables.

### Request variables

Emmett's `request` object also provides three important attributes about the active request:
*Changed in version 2.6*

Emmett's `request` object also provides four important attributes about the active request:

| attribute | awaitable | description |
| --- | --- | --- |
| query_params | no | contains the URL query parameters |
| body | yes | contains the raw (bytes) request body |
| body_params | yes | contains parameters passed into the request body |
| files | yes | contains files passed into the request body |

All three attributes are `sdict` objects and they work in the same way, within the exception of requiring `await` or not, and an example may help you understand their dynamic:
All the attributes but `body` are `sdict` objects and they work in the same way, within the exception of requiring `await` or not, and an example may help you understand their dynamic:

```python
from emmett import App, request
Expand Down Expand Up @@ -90,6 +93,50 @@ Simple: the `request`'s params attributes will look like this:

You can always access the variables you need.

#### Request files

The `files` attribute works in the same way of `body_params` for multipart requests, but its values are objects wrapping the underlying file.

These objects have some useful attributes, specifically:

| attribute | description |
| --- | --- |
| filename | name of the file |
| content\_type | MIME type of the file |
| size | file size |

Also, these object provides two methods to interact with the file contents: the `read` method, which allows you to load the file content, and the async `save` method, which allows you to directly store the file contents into a file-like object.

```python
@app.route()
async def multipart_load():
files = await request.files
# at this point you can either:
# i) read all the file contents
data = files.myfile.read()
# ii) read up to 4k of the file contents
data = files.myfile.read(4096)
# iii) store the file
await files.myfile.save(f"some/destination/{files.myfile.filename}")
```

#### Working with raw requests' bodies

The `body` attribute gives you direct access to the request body. It's an awaitable object, so you can either load the whole body or iterate over it:

```python
@app.route()
async def post():
raw_body = await request.body

@app.route()
async def iterpost():
async for raw_chunk in request.body:
# do something
```

> **Note:** you cannot mix the two approaches. Also, directly interacting with `body` will prevent you to use `body_params` and `files` within the same request.
Errors and redirects
--------------------

Expand Down
55 changes: 55 additions & 0 deletions docs/response.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,61 @@ Then, in your template, you can just write:

and you will have all the meta tags included in your HTML.

Wrapping methods
----------------

*New in version 2.6*

Emmett `Response` object also provides some *wrapping* methods in order to respond with files or streams of data, specifically:

- `wrap_iter`
- `wrap_aiter`
- `wrap_file`
- `wrap_io`

These methods can be used to produce responses from iterators and files.

### Iterable responses

The `wrap_iter` and `wrap_aiter` methods are very similar, both accepts iterables: you can use the latter for asynchronous iterators:

```python
def iterator():
for _ in range(3):
yield b"hello"

async def aiterator():
for _ in range(3):
yield b"hello"

@app.route()
async def response_iter():
return response.wrap_iter(iterator())

@app.route()
async def response_aiter():
return response.wrap_aiter(aiterator())
```

### File responses

You can produce responses from file using two different methods in Emmett:

- `wrap_file` when you want to create a response from a path
- `wrap_io` when you want to create a response from a *file-like* object

```python
@app.route("/file/<name:str>")
async def file(name):
return response.wrap_file(f"assets/{name}")


@app.route("/io/<name:str>")
async def io(name):
with open(f"assets/{name}", "r") as f:
return response.wrap_io(f)
```

Message flashing
----------------

Expand Down
5 changes: 4 additions & 1 deletion docs/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ folder. When you need to use a different template name, just tell Emmett to load

### Output

*New in version 2.0*
*Changed in version 2.6*

The `output` parameter can be used to increase Emmett's performance in building the proper response from the exposed function. Here is the list of accepted outputs:

Expand All @@ -120,6 +120,9 @@ The `output` parameter can be used to increase Emmett's performance in building
| bytes | `bytes` string return value |
| str | `str` return value |
| template | `dict` return value to be used in templates |
| iter | iterable (of `bytes`) return value |
| aiter | async iterable (of `bytes`) return value |
| http | `HTTPResponse` return value |

Under normal circumstances, the default behaviour is the best for most of usage cases.

Expand Down

0 comments on commit 39b7efd

Please sign in to comment.