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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
from decimal import Decimal
from django.db import models
from django.core.exceptions import ValidationError
from .forms import ImageBinaryFormField, PdfBinaryFormField
from django.forms import ModelChoiceField
import io
from PIL import Image, ImageFile
from postgresqleu.util.magic import magicdb
from postgresqleu.util.image import rescale_image
class LowercaseEmailField(models.EmailField):
def get_prep_value(self, value):
value = super(models.EmailField, self).get_prep_value(value)
if value is not None:
value = value.lower()
return value
class ImageBinaryField(models.Field):
empty_values = [None, b'']
def __init__(self, max_length, *args, **kwargs):
self.resolution = kwargs.pop('resolution', None)
self.auto_scale = kwargs.pop('auto_scale', False)
super(ImageBinaryField, self).__init__(*args, **kwargs)
self.max_length = max_length
def deconstruct(self):
name, path, args, kwargs = super(ImageBinaryField, self).deconstruct()
return name, path, args, kwargs
def get_internal_type(self):
return "ImageBinaryField"
def get_placeholder(self, value, compiler, connection):
return '%s'
def get_default(self):
return b''
def db_type(self, connection):
return 'bytea'
def get_db_prep_value(self, value, connection, prepared=False):
value = super(ImageBinaryField, self).get_db_prep_value(value, connection, prepared)
if value is not None:
return connection.Database.Binary(value)
return value
def value_to_string(self, obj):
"""Binary data is serialized as base64"""
return b64encode(force_bytes(self.value_from_object(obj))).decode('ascii')
def to_python(self, value):
if self.max_length is not None and len(value) > self.max_length:
raise ValidationError("Maximum size of file is {} bytes".format(self.max_length))
if isinstance(value, memoryview):
v = bytes(value)
else:
v = value
try:
p = ImageFile.Parser()
p.feed(v)
p.close()
img = p.image
except Exception as e:
raise ValidationError("Could not parse image: %s" % e)
if img.format.upper() not in ('JPEG', 'PNG'):
raise ValidationError("Only JPEG or PNG files are allowed")
if self.resolution:
if img.size[0] != self.resolution[0] or img.size[1] != self.resolution[1]:
if self.auto_scale:
value = rescale_image(img, self.resolution, centered=True)
else:
raise ValidationError("Image size must be {}x{}".format(*self.resolution))
return value
def save_form_data(self, instance, data):
if data is not None:
if not data:
data = b''
setattr(instance, self.name, data)
def formfield(self, **kwargs):
defaults = {'form_class': ImageBinaryFormField}
defaults.update(kwargs)
return super(ImageBinaryField, self).formfield(**defaults)
class PdfBinaryField(ImageBinaryField):
def get_internal_type(self):
return "PdfBinaryField"
def formfield(self, **kwargs):
defaults = {'form_class': PdfBinaryFormField}
defaults.update(kwargs)
return super(PdfBinaryField, self).formfield(**defaults)
def to_python(self, value):
if self.max_length is not None and len(value) > self.max_length:
raise ValidationError("Maximum size of file is {} bytes".format(self.max_length))
mtype = magicdb.buffer(value)
if not mtype.startswith('application/pdf'):
raise ValidationError("File must be PDF, not %s" % mtype)
return value
class UserModelChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return "{0} - {1} {2} <{3}>".format(obj.username, obj.first_name, obj.last_name, obj.email)
class NormalizedDecimalField(models.DecimalField):
def from_db_value(self, value, expression, connection):
if value is None:
return value
return value.quantize(Decimal(1)) if value == value.to_integral() else value.normalize()
def to_python(self, value):
val = super().to_python(value)
if val is None:
return val
return val.quantize(Decimal(1)) if val == val.to_integral() else val.normalize()
|