-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathfields.py
104 lines (76 loc) · 2.88 KB
/
fields.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
from collections import UserList
from django import forms
from django.core.validators import RegexValidator
from django.db.models.fields import TextField
from django.forms.widgets import TextInput
from collections.abc import Iterable
path_label_validator = RegexValidator(
r"^(?P<root>[a-zA-Z][a-zA-Z0-9_]*|\d+)(?:\.[a-zA-Z0-9_]+)*$",
"A label is a sequence of alphanumeric characters and underscores separated by dots.",
"invalid",
)
class PathValue(UserList):
def __init__(self, value):
if isinstance(value, str):
split_by = "/" if "/" in value else "."
value = value.strip().split(split_by) if value else []
elif isinstance(value, int):
value = [str(value)]
elif isinstance(value, Iterable):
value = [str(v) for v in value]
else:
raise ValueError("Invalid value: {!r} for path".format(value))
super().__init__(initlist=value)
def __repr__(self):
return str(self)
def __str__(self):
return ".".join(self)
class PathValueProxy:
def __init__(self, field_name):
self.field_name = field_name
def __get__(self, instance, owner):
if instance is None:
return self
value = instance.__dict__[self.field_name]
if value is None:
return value
return PathValue(instance.__dict__[self.field_name])
def __set__(self, instance, value):
if instance is None:
return self
instance.__dict__[self.field_name] = value
class PathFormField(forms.CharField):
default_validators = [path_label_validator]
class PathField(TextField):
default_validators = [path_label_validator]
def db_type(self, connection):
return "ltree"
def formfield(self, **kwargs):
kwargs["form_class"] = PathFormField
kwargs["widget"] = TextInput(attrs={"class": "vTextField"})
return super().formfield(**kwargs)
def contribute_to_class(self, cls, name, private_only=False):
super().contribute_to_class(cls, name)
setattr(cls, self.name, PathValueProxy(self.name))
def from_db_value(self, value, expression, connection, *args):
if value is None:
return value
return PathValue(value)
def get_prep_value(self, value):
if value is None:
return value
return str(PathValue(value))
def to_python(self, value):
if value is None:
return value
elif isinstance(value, PathValue):
return value
return PathValue(value)
def get_db_prep_value(self, value, connection, prepared=False):
if value is None:
return value
elif isinstance(value, PathValue):
return str(value)
elif isinstance(value, (list, str)):
return str(PathValue(value))
raise ValueError(f"Unknown value type {type(value)}")