Skip to content

Commit 07412d0

Browse files
committed
fixed problems related to optional modules
1 parent 64f7b80 commit 07412d0

File tree

2 files changed

+146
-0
lines changed

2 files changed

+146
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import sqlalchemy as sa
2+
from sqlalchemy.dialects import postgresql
3+
4+
from aiohttp_admin2.mappers.base import Mapper
5+
from aiohttp_admin2.mappers import fields
6+
7+
8+
__all__ = [
9+
"PostgresMapperGeneric",
10+
]
11+
12+
13+
class PostgresMapperGeneric(Mapper):
14+
"""
15+
This class need for generate Mapper from sqlAlchemy's model.
16+
"""
17+
18+
FIELDS_MAPPER = {
19+
sa.Integer: fields.IntField,
20+
sa.BigInteger: fields.IntField,
21+
sa.SmallInteger: fields.SmallIntField,
22+
sa.Float: fields.FloatField,
23+
sa.String: fields.StringField,
24+
sa.Text: fields.LongStringField,
25+
sa.Enum: fields.ChoicesField,
26+
postgresql.ENUM: fields.ChoicesField,
27+
sa.Boolean: fields.BooleanField,
28+
sa.ARRAY: fields.ArrayField,
29+
sa.DateTime: fields.DateTimeField,
30+
sa.Date: fields.DateField,
31+
sa.JSON: fields.JsonField,
32+
}
33+
DEFAULT_FIELD = fields.StringField
34+
35+
def __init_subclass__(cls, table: sa.Table) -> None:
36+
super().__init_subclass__()
37+
cls._fields = {}
38+
39+
existing_fields = [field.name for field in cls._fields_cls]
40+
41+
for name, column in table.columns.items():
42+
field_cls = \
43+
cls.FIELDS_MAPPER.get(type(column.type), cls.DEFAULT_FIELD)
44+
45+
max_length = hasattr(column.type, 'length') and column.type.length
46+
field_kwargs = {
47+
"max_length": max_length,
48+
"required": not column.nullable,
49+
"primary_key": column.primary_key,
50+
}
51+
52+
if field_cls is fields.ChoicesField:
53+
field = fields.ChoicesField(
54+
choices=[(n, n) for n in column.type.enums],
55+
**field_kwargs
56+
)
57+
elif field_cls is fields.ArrayField:
58+
field = field_cls(
59+
field_cls=(
60+
cls.FIELDS_MAPPER
61+
.get(type(column.type.item_type), cls.DEFAULT_FIELD)
62+
),
63+
**field_kwargs
64+
)
65+
else:
66+
field = field_cls(**field_kwargs)
67+
68+
field.name = name
69+
if name not in existing_fields:
70+
cls._fields[name] = field
71+
cls._fields_cls.append(field)
+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import umongo
2+
3+
from marshmallow import EXCLUDE
4+
from marshmallow.exceptions import ValidationError as MarshmallowValidationErr
5+
from aiohttp_admin2.mappers.base import Mapper
6+
from aiohttp_admin2.mappers import fields
7+
from aiohttp_admin2.mappers.fields import mongo_fields
8+
from aiohttp_admin2.mappers.exceptions import ValidationError
9+
10+
11+
__all__ = [
12+
"MongoMapperGeneric",
13+
]
14+
15+
16+
class MongoMapperGeneric(Mapper):
17+
"""
18+
This class need for generate Mapper from Mongo model.
19+
"""
20+
table: umongo.Document
21+
22+
FIELDS_MAPPER = {
23+
umongo.fields.ObjectIdField: mongo_fields.ObjectIdField,
24+
umongo.fields.IntegerField: fields.IntField,
25+
umongo.fields.StringField: fields.StringField,
26+
umongo.fields.DateTimeField: fields.DateTimeField,
27+
umongo.fields.List: fields.ArrayField,
28+
}
29+
30+
DEFAULT_FIELD = fields.StringField
31+
32+
def __init_subclass__(cls, table: umongo.Document) -> None:
33+
cls._fields = {}
34+
cls.table = table
35+
36+
existing_fields = [field.name for field in cls._fields_cls]
37+
38+
for name, column in table.schema.fields.items():
39+
field = \
40+
cls.FIELDS_MAPPER.get(type(column), cls.DEFAULT_FIELD)()
41+
field.name = name
42+
if name not in existing_fields:
43+
cls._fields_cls.append(field)
44+
cls._fields[name] = field
45+
46+
def validation(self):
47+
"""
48+
In the current method we cover marshmallow validation. We create/update
49+
instances via umongo which use marshmallow validation for it. We can't
50+
to copy all validation from marshmallow to our mapper because user can
51+
to set custom validation. So we just check twice that data is valid for
52+
the our mapper and for the marshmallow schema.
53+
"""
54+
is_valid = True
55+
errors = {}
56+
57+
try:
58+
# mapper may have additional fields which are not specify in the
59+
# schema so we need to skip validation of fields which are not
60+
# exist in the schema
61+
self.table.schema.as_marshmallow_schema()().load(
62+
self.raw_data,
63+
unknown=EXCLUDE,
64+
)
65+
except MarshmallowValidationErr as e:
66+
errors = e.messages
67+
68+
# validation for each field
69+
for f in self.fields.values():
70+
if errors.get(f.name):
71+
f.errors.append(errors.get(f.name)[0])
72+
is_valid = False
73+
74+
if not is_valid:
75+
raise ValidationError

0 commit comments

Comments
 (0)