Skip to content

Commit 56f91b4

Browse files
authored
Merge pull request #11 from jowilf/dev
Accept additional metadata from `File` object
2 parents 5a4fb5a + f7a1dfc commit 56f91b4

File tree

21 files changed

+250
-1557
lines changed

21 files changed

+250
-1557
lines changed

.gitignore

+14-188
Original file line numberDiff line numberDiff line change
@@ -1,190 +1,16 @@
1-
2-
### macOS ###
3-
# General
4-
.DS_Store
5-
.AppleDouble
6-
.LSOverride
7-
8-
# Icon must end with two \r
9-
Icon
10-
11-
# Thumbnails
12-
._*
13-
14-
# Files that might appear in the root of a volume
15-
.DocumentRevisions-V100
16-
.fseventsd
17-
.Spotlight-V100
18-
.TemporaryItems
19-
.Trashes
20-
.VolumeIcon.icns
21-
.com.apple.timemachine.donotpresent
22-
23-
# Directories potentially created on remote AFP share
24-
.AppleDB
25-
.AppleDesktop
26-
Network Trash Folder
27-
Temporary Items
28-
.apdisk
29-
30-
### Python ###
31-
# Byte-compiled / optimized / DLL files
32-
__pycache__/
33-
*.py[cod]
34-
*$py.class
35-
36-
# C extensions
37-
*.so
38-
39-
# Distribution / packaging
40-
.Python
41-
build/
42-
develop-eggs/
43-
dist/
44-
downloads/
45-
eggs/
46-
.eggs/
47-
parts/
48-
sdist/
49-
var/
50-
wheels/
51-
pip-wheel-metadata/
52-
share/python-wheels/
53-
*.egg-info/
54-
.installed.cfg
55-
*.egg
56-
MANIFEST
57-
58-
# PyInstaller
59-
*.manifest
60-
*.spec
61-
62-
# Installer logs
63-
pip-log.txt
64-
pip-delete-this-directory.txt
65-
66-
# Unit test / coverage reports
67-
htmlcov/
68-
.tox/
69-
.nox/
1+
*.pyc
2+
env*
3+
.mypy_cache
4+
.vscode
5+
.idea
6+
poetry.lock
7+
dist
8+
htmlcov
9+
*.egg-info
7010
.coverage
71-
.coverage.*
72-
.cache
73-
nosetests.xml
7411
coverage.xml
75-
*.cover
76-
*.py,cover
77-
.hypothesis/
78-
.pytest_cache/
79-
pytestdebug.log
80-
81-
# Translations
82-
*.mo
83-
*.pot
84-
85-
# Django stuff:
86-
*.log
87-
local_settings.py
88-
db.sqlite3
89-
db.sqlite3-journal
90-
91-
# Flask stuff:
92-
instance/
93-
.webassets-cache
94-
95-
# Scrapy stuff:
96-
.scrapy
97-
98-
# Sphinx documentation
99-
docs/_build/
100-
doc/_build/
101-
102-
# PyBuilder
103-
target/
104-
105-
# Jupyter Notebook
106-
.ipynb_checkpoints
107-
108-
# IPython
109-
profile_default/
110-
ipython_config.py
111-
112-
# pyenv
113-
.python-version
114-
115-
# pipenv
116-
# Pipfile.lock
117-
118-
# poetry
119-
# poetry.lock
120-
121-
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
122-
__pypackages__/
123-
124-
# Celery stuff
125-
celerybeat-schedule
126-
celerybeat.pid
127-
128-
# SageMath parsed files
129-
*.sage.py
130-
131-
# Environments
132-
*.env*
133-
.env/
134-
.venv/
135-
env/
136-
venv/
137-
ENV/
138-
env.bak/
139-
venv.bak/
140-
pythonenv*
141-
142-
# Spyder project settings
143-
.spyderproject
144-
.spyproject
145-
146-
# Rope project settings
147-
.ropeproject
148-
149-
# mkdocs documentation
150-
/public
151-
/site
152-
153-
# mypy
154-
.mypy_cache/
155-
.dmypy.json
156-
dmypy.json
157-
158-
# Pyre type checker
159-
.pyre/
160-
161-
# pytype static type analyzer
162-
.pytype/
163-
164-
# operating system-related files
165-
# *.DS_Store
166-
Thumbs.db
167-
168-
# profiling data
169-
.prof
170-
171-
### VirtualEnv ###
172-
# Virtualenv
173-
[Bb]in
174-
[Ii]nclude
175-
[Ll]ib
176-
[Ll]ib64
177-
[Ll]ocal
178-
pyvenv.cfg
179-
.venv
180-
pip-selfcheck.json
181-
182-
### VisualStudioCode ###
183-
*.code-workspace
184-
.vscode/*
185-
!.vscode/tasks.json
186-
!.vscode/launch.json
187-
.history
188-
.ionide
189-
190-
.idea/
12+
site
13+
*.db
14+
.cache
15+
__pycache__/
16+
.pytest_cache/

CHANGELOG.md

+18-2
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,35 @@
11
# Changelog
2+
23
All notable changes to this project will be documented in this file.
34

4-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
6+
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [0.1.4] - 2022-08-30
9+
10+
---
11+
12+
### Added
13+
14+
- Add `upload_storage` to the default information saved into the database. Before, `upload_storage` can be extracted
15+
from `path` attribute. Now you can access directly with `file['upload_storage']` by
16+
@jowilf https://github.com/jowilf/sqlalchemy-file/pull/11
17+
- Accept additional metadata from `File` object by @jowilf https://github.com/jowilf/sqlalchemy-file/pull/11
18+
- Add section [Upload File](https://jowilf.github.io/sqlalchemy-file/tutorial/using-files-in-models/#upload-file) to the
19+
documentation
620

721
## [0.1.3] - 2022-08-23
822

923
---
1024

1125
### Added
26+
1227
- Add `thumbnail_size` property to ImageField by @jowilf https://github.com/jowilf/sqlalchemy-file/pull/9
1328

1429
## [0.1.2] - 2022-08-11
1530

1631
---
1732

1833
### Added
34+
1935
- Add CHANGELOG.md

docs/changelog.md

+12
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [0.1.4] - 2022-08-30
8+
9+
---
10+
11+
### Added
12+
13+
- Add `upload_storage` to the default information saved into the database. Before, `upload_storage` can be extracted
14+
from `path` attribute. Now you can access directly with `file['upload_storage']` in [#11](https://github.com/jowilf/sqlalchemy-file/pull/11)
15+
- Accept additional metadata from `File` object in [#11](https://github.com/jowilf/sqlalchemy-file/pull/11)
16+
- Add section [Upload File](https://jowilf.github.io/sqlalchemy-file/tutorial/using-files-in-models/#upload-file) to the
17+
documentation
18+
719
## [0.1.3] - 2022-08-23
820

921
---

docs/tutorial/using-files-in-models.md

+96-4
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,104 @@ uploaded file is a valid image.
5252
title = Column(String(100), unique=True)
5353
cover = Column(ImageField(thumbnail_size=(128, 128)))
5454
```
55-
## Uploaded Files Information
55+
## Upload File
56+
57+
Let's say you defined your model like this
58+
```python
59+
class Attachment(Base):
60+
__tablename__ = "attachment"
61+
62+
id = Column(Integer, autoincrement=True, primary_key=True)
63+
name = Column(String(50), unique=True)
64+
content = Column(FileField)
65+
```
66+
and configure your storage like this
67+
```python
68+
container = LocalStorageDriver("/tmp/storage").get_container("attachment")
69+
StorageManager.add_storage("default", container)
70+
```
71+
72+
### Save file object
73+
5674
Whenever a supported object is assigned to a [FileField][sqlalchemy_file.types.FileField] or [ImageField][sqlalchemy_file.types.ImageField]
5775
it will be converted to a [File][sqlalchemy_file.file.File] object.
76+
```python
77+
with Session(engine) as session:
78+
session.add(Attachment(name="attachment1", content=open("./example.txt", "rb")))
79+
session.add(Attachment(name="attachment2", content=b"Hello world"))
80+
session.add(Attachment(name="attachment3", content="Hello world"))
81+
file = File(content="Hello World", filename="hello.txt", content_type="text/plain")
82+
session.add(Attachment(name="attachment4", content=file))
83+
session.commit()
84+
```
85+
The file itself will be uploaded to your configured storage, and only the [File][sqlalchemy_file.file.File]
86+
information will be stored on the database as JSON.
5887

59-
This is the same object you will get back when reloading the models from database and apart from the file itself which is accessible
60-
through the `.file` property, it provides additional attributes described into the [File][sqlalchemy_file.file.File] documentation itself.
88+
### Retrieve file object
89+
90+
This is the same [File][sqlalchemy_file.file.File] object you will get back when reloading the models from database and the file itself is accessible
91+
through the `.file` property.
92+
93+
!!! note
94+
Check the [File][sqlalchemy_file.file.File] documentation for all default attributes save into the database.
95+
96+
```python
97+
with Session(engine) as session:
98+
attachment = session.execute(
99+
select(Attachment).where(Attachment.name == "attachment3")
100+
).scalar_one()
101+
assert attachment.content.saved # saved is True for saved file
102+
assert attachment.content.file.read() == b"Hello world" # access file content
103+
assert attachment.content["filename"] is not None # `unnamed` when no filename are provided
104+
assert attachment.content["file_id"] is not None # uuid v4
105+
assert attachment.content["upload_storage"] == "default"
106+
assert attachment.content["content_type"] is not None
107+
assert attachment.content["uploaded_at"] is not None
108+
```
109+
110+
### Save additional information
111+
112+
It's important to note that [File][sqlalchemy_file.file.File] object inherit from python `dict`.
113+
Therefore, you can add additional information to your file object like a dict object. Just make sure to not use
114+
the default attributes used by [File][sqlalchemy_file.file.File] object internally.
115+
116+
!!! Example
117+
```python
118+
content = File(open("./example.txt", "rb"),custom_key1="custom_value1", custom_key2="custom_value2")
119+
content["custom_key3"] = "custom_value3"
120+
attachment = Attachment(name="Dummy", content=content)
121+
122+
session.add(attachment)
123+
session.commit()
124+
session.refresh(attachment)
125+
126+
assert attachment.custom_key1 == "custom_value1"
127+
assert attachment.custom_key2 == "custom_value2"
128+
assert attachment["custom_key3"] == "custom_value3"
129+
```
130+
131+
!!! important
132+
[File][sqlalchemy_file.file.File] provides also attribute style access.
133+
You can access your keys as attributes.
134+
135+
### Metadata
136+
137+
*SQLAlchemy-file* store the uploaded file with some metadata. Only `filename` and `content_type` are sent by default,
138+
. You can complete with `metadata` key inside your [File][sqlalchemy_file.file.File] object.
139+
140+
!!! Example
141+
```py hl_lines="2"
142+
with Session(engine) as session:
143+
content = File(DummyFile(), metadata={"key1": "val1", "key2": "val2"})
144+
attachment = Attachment(name="Additional metadata", content=content)
145+
session.add(attachment)
146+
session.commit()
147+
attachment = session.execute(
148+
select(Attachment).where(Attachment.name == "Additional metadata")
149+
).scalar_one()
150+
assert attachment.content.file.object.meta_data["key1"] == "val1"
151+
assert attachment.content.file.object.meta_data["key2"] == "val2"
152+
```
61153

62154
## Uploading on a Specific Storage
63155

@@ -119,7 +211,7 @@ Validators can add additional properties to the file object. For example
119211
the file object.
120212

121213
**SQLAlchemy-file** has built-in validators to get started, but you can create your own validator
122-
by extending [ValidationError][sqlalchemy_file.exceptions.ValidationError] base class.
214+
by extending [Validator][sqlalchemy_file.validators.Validator] base class.
123215

124216
Built-in validators:
125217

docs_src/__init__.py

Whitespace-only changes.

docs_src/tutorial/__init__.py

Whitespace-only changes.

docs_src/tutorial/quick-start/__init__.py

Whitespace-only changes.

docs_src/tutorial/storage-manager/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)