-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add generics to feature properties #29
Conversation
I was not aware that pydantic supported generics, this is really cool, I think a good feature to add. #28 adds similar functionality for exposing the Feature's geometry type, but using pydantic's |
Really like this and agree that this is preferable to the model factory pattern. cc: @vincentsarago for thoughts |
@iwpnd 🙏 I love unsolicited PR. I like the idea of generics (even if I'm just learning about it). Just noticing that Pydantic generic is only supported with python >3.6 (https://pydantic-docs.helpmanual.io/usage/models/#generic-models) I'm fine dropping support of python 3.6 but this will need bigger discussion cc @geospatial-jeff @drewbo |
Up to you. :) Let me know if I can help to move this forward or there's something else you want me to do. |
I'm also fine with dropping support for python 3.6 |
@iwpnd I'm trying to update this PR, could you check if we are allowed to push to your branch 🙏 |
I allowed edits by the maintainer @vincentsarago |
Codecov Report
@@ Coverage Diff @@
## master #29 +/- ##
==========================================
+ Coverage 93.61% 93.75% +0.13%
==========================================
Files 4 4
Lines 94 96 +2
==========================================
+ Hits 88 90 +2
Misses 6 6
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
@geospatial-jeff I agree we shouldn't use both but I'm not sure there is an easy way to allow FYI: I'm going to |
Not too sure what you're trying to achieve, but you can pass as many types as you want to |
@iwpnd so what we wanted with #28 was to create model that only validate Polygon Feature for example as explained in #20 (comment) |
Okay. So something like that: from typing import TypeVar, Generic, Dict
from pydantic.generics import GenericModel, BaseModel
from geojson_pydantic.geometries import Geometry, Polygon
Props = TypeVar('Props', bound=Dict)
Geom = TypeVar('Geom', bound=Geometry)
class Feature(GenericModel, Generic[Geom, Props]):
type: str = 'Feature'
geometry: Geom
properties: Props
class MyFeatureProperties(BaseModel):
id: str
description: str
feature = Feature[Polygon, MyFeatureProperties](**{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[13.38272,52.46385],
[13.42786,52.46385],
[13.42786,52.48445],
[13.38272,52.48445],
[13.38272,52.46385]
]
]
},
"properties": {
"id": "test",
"description": "i will be parsed"
}
}) Where this passes as you'd expect it to do. And feature = Feature[Polygon, MyFeatureProperties](**{
"type": "Feature",
"geometry": {
"type": "Cheese",
"coordinates": [
[
[13.38272,52.46385],
[13.42786,52.46385],
[13.42786,52.48445],
[13.38272,52.48445],
[13.38272,52.46385]
]
]
},
"properties": {
"id": "test",
"description": "i will be parsed"
}
}) raises: ValidationError: 1 validation error for Feature[Polygon, MyFeatureProperties]
geometry -> type
unexpected value; permitted: 'Polygon' (type=value_error.const; given=Cheese; permitted=['Polygon']) |
yes but does |
Only like this. class Feature(GenericModel, Generic[Geom, Props]):
type: str = 'Feature'
geometry: Geom
properties: Props
class MyFeatureProperties(BaseModel):
id: str
description: str
feature = Feature[None, MyFeatureProperties](**{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[13.38272,52.46385],
[13.42786,52.46385],
[13.42786,52.48445],
[13.38272,52.48445],
[13.38272,52.46385]
]
]
},
"properties": {
"id": "test",
"description": "i will be parsed"
}
}) You either have it for both, or don't use them at all and stick to But this is kind off drifting away from the scope of this PR, I guess. |
Do you want to move forward with this? I'd be happy to spend some more time to make this work for you. Otherwise I'd be closing this PR and create a fork for my use-case. |
Hi @iwpnd |
Hi @vincentsarago, re-reading my comment I didn't mean to come across as pushy as it turned out. If you're saying you're still considering it, I'm okay. |
@iwpnd so I'm trying to add Generic for Geom and Props as Props = TypeVar('Props', bound=Dict)
Geom = TypeVar('Geom', bound=Geometry)
class Feature(GenericModel, Generic[Geom, Props]):
"""Feature Model"""
type: str = Field("Feature", const=True)
geometry: Geom
properties: Optional[Props]
id: Optional[str]
bbox: Optional[BBox] Feature[Polygon, None](**f)
>>> ValidationError: 1 validation error for Feature[Polygon, NoneType]
properties
value is not None (type=type_error.not_none)
Feature[None, MyFeatureProperties](**f)
>>> ValidationError: 1 validation error for Feature[NoneType, MyFeatureProperties]
geometry
value is not None (type=type_error.not_none) To be able to use IMO this is not optimal, and |
After talking with @geospatial-jeff (on slack) I agree that's it's ok to being forced to provide both types (geom and props) feat = Feature[Polygon, Dict]
feat = Feature[Geom, MyPropsModel] I'm going to update this PR and wait for feedback |
I have added Please 👍 👎 |
Hi!
First of all sorry for the unsolicited PR.
I wanted to start a project similar to this but luckily I researched up front. Something was missing for me, and I kinda jumped on the idea to add generics to feature properties and it turned out quite okay right away.
The idea is, that instead of using a
Dict[Any, Any]
for the feature properties, you can opt to pass a model toFeature
that is then parsed just like e.g. theGeometry
.This allows you, among other things, to validate feature properties in a Feature. Even cooler, it is not even a must. You can use it just like you are doing right now, but give the option to also parse the
properties
.Example:
Let me know what you think.