-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: mv allauth.tests, extract (de)serialization into modelkit
- Loading branch information
Showing
133 changed files
with
653 additions
and
443 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import base64 | ||
import json | ||
|
||
from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured | ||
from django.core.files.base import ContentFile | ||
from django.core.serializers.json import DjangoJSONEncoder | ||
from django.db.models import FileField | ||
from django.db.models.fields import ( | ||
BinaryField, | ||
DateField, | ||
DateTimeField, | ||
TimeField, | ||
) | ||
from django.utils import dateparse | ||
from django.utils.encoding import force_bytes, force_str | ||
|
||
|
||
SERIALIZED_DB_FIELD_PREFIX = "_db_" | ||
|
||
|
||
def serialize_instance(instance): | ||
""" | ||
Since Django 1.6 items added to the session are no longer pickled, | ||
but JSON encoded by default. We are storing partially complete models | ||
in the session (user, account, token, ...). We cannot use standard | ||
Django serialization, as these are models are not "complete" yet. | ||
Serialization will start complaining about missing relations et al. | ||
""" | ||
data = {} | ||
for k, v in instance.__dict__.items(): | ||
if k.startswith("_") or callable(v): | ||
continue | ||
try: | ||
field = instance._meta.get_field(k) | ||
if isinstance(field, BinaryField): | ||
if v is not None: | ||
v = force_str(base64.b64encode(v)) | ||
elif isinstance(field, FileField): | ||
if v and not isinstance(v, str): | ||
v = { | ||
"name": v.name, | ||
"content": base64.b64encode(v.read()).decode("ascii"), | ||
} | ||
# Check if the field is serializable. If not, we'll fall back | ||
# to serializing the DB values which should cover most use cases. | ||
try: | ||
json.dumps(v, cls=DjangoJSONEncoder) | ||
except TypeError: | ||
v = field.get_prep_value(v) | ||
k = SERIALIZED_DB_FIELD_PREFIX + k | ||
except FieldDoesNotExist: | ||
pass | ||
data[k] = v | ||
return json.loads(json.dumps(data, cls=DjangoJSONEncoder)) | ||
|
||
|
||
def deserialize_instance(model, data): | ||
ret = model() | ||
for k, v in data.items(): | ||
is_db_value = False | ||
if k.startswith(SERIALIZED_DB_FIELD_PREFIX): | ||
k = k[len(SERIALIZED_DB_FIELD_PREFIX) :] | ||
is_db_value = True | ||
if v is not None: | ||
try: | ||
f = model._meta.get_field(k) | ||
if isinstance(f, DateTimeField): | ||
v = dateparse.parse_datetime(v) | ||
elif isinstance(f, TimeField): | ||
v = dateparse.parse_time(v) | ||
elif isinstance(f, DateField): | ||
v = dateparse.parse_date(v) | ||
elif isinstance(f, BinaryField): | ||
v = force_bytes(base64.b64decode(force_bytes(v))) | ||
elif isinstance(f, FileField): | ||
if isinstance(v, dict): | ||
v = ContentFile(base64.b64decode(v["content"]), name=v["name"]) | ||
elif is_db_value: | ||
try: | ||
# This is quite an ugly hack, but will cover most | ||
# use cases... | ||
# The signature of `from_db_value` changed in Django 3 | ||
# https://docs.djangoproject.com/en/3.0/releases/3.0/#features-removed-in-3-0 | ||
v = f.from_db_value(v, None, None) | ||
except Exception: | ||
raise ImproperlyConfigured( | ||
"Unable to auto serialize field '{}', custom" | ||
" serialization override required".format(k) | ||
) | ||
except FieldDoesNotExist: | ||
pass | ||
setattr(ret, k, v) | ||
return ret |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
from datetime import date, datetime | ||
|
||
from django.core.files.base import ContentFile | ||
from django.db import models | ||
|
||
from allauth.core.internal import modelkit | ||
|
||
|
||
def test_serializer(): | ||
class SomeValue: | ||
pass | ||
|
||
some_value = SomeValue() | ||
|
||
class SomeField(models.Field): | ||
def get_prep_value(self, value): | ||
return "somevalue" | ||
|
||
def from_db_value(self, value, expression, connection): | ||
return some_value | ||
|
||
class SomeModel(models.Model): | ||
dt = models.DateTimeField() | ||
t = models.TimeField() | ||
d = models.DateField() | ||
img1 = models.ImageField() | ||
img2 = models.ImageField() | ||
img3 = models.ImageField() | ||
something = SomeField() | ||
|
||
def method(self): | ||
pass | ||
|
||
instance = SomeModel( | ||
dt=datetime.now(), | ||
d=date.today(), | ||
something=some_value, | ||
t=datetime.now().time(), | ||
) | ||
instance.img1 = ContentFile(b"%PDF", name="foo.pdf") | ||
instance.img2 = ContentFile( | ||
b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x01\x00" | ||
b"\x00\x00\x007n\xf9$\x00\x00\x00\nIDATx\x9cc`\x00\x00\x00\x02\x00\x01H\xaf" | ||
b"\xa4q\x00\x00\x00\x00IEND\xaeB`\x82", | ||
name="foo.png", | ||
) | ||
# make sure serializer doesn't fail if a method is attached to | ||
# the instance | ||
instance.method = method | ||
instance.nonfield = "hello" | ||
data = modelkit.serialize_instance(instance) | ||
instance2 = modelkit.deserialize_instance(SomeModel, data) | ||
assert getattr(instance, "method", None) == method | ||
assert getattr(instance2, "method", None) is None | ||
assert instance2.something == some_value | ||
assert instance2.img1.name == "foo.pdf" | ||
assert instance2.img2.name == "foo.png" | ||
assert instance2.img3.name == "" | ||
assert instance.nonfield == instance2.nonfield | ||
assert instance.d == instance2.d | ||
assert instance.dt.date() == instance2.dt.date() | ||
for t1, t2 in [ | ||
(instance.t, instance2.t), | ||
(instance.dt.time(), instance2.dt.time()), | ||
]: | ||
assert t1.hour == t2.hour | ||
assert t1.minute == t2.minute | ||
assert t1.second == t2.second | ||
# AssertionError: datetime.time(10, 6, 28, 705776) | ||
# != datetime.time(10, 6, 28, 705000) | ||
assert int(t1.microsecond / 1000) == int(t2.microsecond / 1000) | ||
|
||
|
||
def test_serializer_binary_field(): | ||
class SomeBinaryModel(models.Model): | ||
bb = models.BinaryField() | ||
bb_empty = models.BinaryField() | ||
|
||
instance = SomeBinaryModel(bb=b"some binary data") | ||
|
||
serialized = modelkit.serialize_instance(instance) | ||
deserialized = modelkit.deserialize_instance(SomeBinaryModel, serialized) | ||
|
||
assert serialized["bb"] == "c29tZSBiaW5hcnkgZGF0YQ==" | ||
assert serialized["bb_empty"] == "" | ||
assert deserialized.bb == b"some binary data" | ||
assert deserialized.bb_empty == b"" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.