Skip to content

Commit 8c5cbbe

Browse files
committed
Let the encode and decode methods behave the same as the ones from Hashids package.
1 parent 65676ac commit 8c5cbbe

File tree

1 file changed

+42
-26
lines changed

1 file changed

+42
-26
lines changed

flask_hashids.py

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
from flask import abort, current_app, Flask
1+
from flask import current_app, Flask
22
from hashids import Hashids as _Hashids
3-
from typing import Any, Dict
4-
from werkzeug.routing import BaseConverter
3+
from typing import Any, Dict, Tuple, Union
4+
from werkzeug.routing import BaseConverter, ValidationError
55

66

77
class HashidMixin:
@@ -17,38 +17,45 @@ class HashidMixin:
1717
__id_attribute__ and must be of type int!
1818
'''
1919

20-
__id_attribute__ = 'id'
20+
__id_attribute__: str = 'id'
2121

2222
@property
2323
def hashid(self) -> str:
24-
id = getattr(self, self.__class__.__id_attribute__)
24+
id: int = getattr(self, self.__class__.__id_attribute__)
2525
return current_app.extensions['hashids'].encode(id)
2626

2727

2828
class HashidConverter(BaseConverter):
29-
'''
30-
Hashid Converter.
29+
''' Hashid Converter.
3130
32-
Converts given hashids from routes to integers.
33-
Example:
31+
Converts and decodes a hashid from routes.
32+
Examples:
3433
@app.route('/resources/<hashid:resource_id')
3534
def get_resource(resource_id: int):
3635
print(isinstance(resource_id, int)) # True
3736
38-
Converts integers to hashids when generating urls.
39-
Example:
37+
@app.route('/resources/<hashid:resource_ids')
38+
def get_resources(resource_ids: Tuple[int, ...]):
39+
print(isinstance(resource_ids, tuple)) # True
40+
print(all(isinstance(i, int) for i in resource_ids)) # True
41+
42+
Converts and encodes values when generating urls.
43+
Examples:
4044
url_for('get_resource', resource_id=123) # /resources/Mj3
45+
46+
url_for('get_resource', resource_id=(123, 456)) # /resources/Nk4
4147
'''
4248

43-
def to_python(self, value: str) -> int:
44-
try:
45-
decoded_value = current_app.extensions['hashids'].decode(value)
46-
except IndexError:
47-
abort(404)
48-
return decoded_value
49+
def to_python(self, hashid: str) -> Union[int, Tuple[int, ...]]:
50+
decoded_hashid = current_app.extensions['hashids'].decode(hashid)
51+
if isinstance(decoded_hashid, tuple) and len(decoded_hashid) == 0:
52+
raise ValidationError()
53+
return decoded_hashid
4954

50-
def to_url(self, value: int) -> str:
51-
return current_app.extensions['hashids'].encode(value)
55+
def to_url(self, value_or_values: Union[int, Tuple[int, ...]]) -> str:
56+
if isinstance(value_or_values, int):
57+
return current_app.extensions['hashids'].encode(value_or_values)
58+
return current_app.extensions['hashids'].encode(*value_or_values)
5259

5360

5461
class Hashids:
@@ -75,10 +82,19 @@ def init_app(self, app: Flask):
7582
app.extensions['hashids'] = self
7683
app.url_map.converters['hashid'] = HashidConverter
7784

78-
def decode(self, value: str) -> int:
79-
''' Decode a hashid to an integer. '''
80-
return self._hashids.decode(value)[0]
81-
82-
def encode(self, value: int) -> str:
83-
''' Encode an integer to a hashid. '''
84-
return self._hashids.encode(value)
85+
def decode(self, hashid: str) -> Union[int, Tuple[int, ...], Tuple[()]]:
86+
''' Decode the passed `hashid`.
87+
88+
Possible return values:
89+
- `int` if the hashid contains only one value.
90+
- `Tuple[int, ...]` if the hashid contains multiple values.
91+
- `Tuple[()]` if the hashid is invalid.
92+
'''
93+
decoded_hashid = self._hashids.decode(hashid)
94+
if len(decoded_hashid) == 1:
95+
return decoded_hashid[0]
96+
return decoded_hashid
97+
98+
def encode(self, *values: int) -> str:
99+
''' Encode the passed `values`. '''
100+
return self._hashids.encode(*values)

0 commit comments

Comments
 (0)