Skip to content

Commit

Permalink
Oscarify django-oscar-odin (#46)
Browse files Browse the repository at this point in the history
* Load resources with get_class and register model field resolver in __init__

* Add inheritable resources that allow the usage of get_class

* Add the 'Resource' suffis to all resources, as right now its confusing to work with

* More suffixes

* New major release because breaking changes

* Load all resources now with get_class

* Load more things with get_classes. Remove '_' prefix from some files names, as they're not just used internally. Rename inheritable resources to same odin name, as this is just what should be used for oscar resources if you want them to be able to be subclassed.

* Load ModelMapping & ParentProductResource with get_class too

* fix modelmapping import

---------

Co-authored-by: Joey Jurjens <joey@highbiza.nl>
  • Loading branch information
joeyjurjens and Joey Jurjens authored Nov 1, 2024
1 parent 500c596 commit 3dafbd5
Show file tree
Hide file tree
Showing 20 changed files with 354 additions and 207 deletions.
8 changes: 8 additions & 0 deletions oscar_odin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,11 @@
Odin Resources and mappings to Oscar models.
"""

from django.db.models import Model

from odin import registration

from .django_resolver import ModelFieldResolver

registration.register_field_resolver(ModelFieldResolver, Model)
10 changes: 2 additions & 8 deletions oscar_odin/apps.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
"""Django App Config for Oscar Odin."""

from django.apps import AppConfig
from django.db.models import Model
from django.utils.translation import gettext_lazy as _
from odin import registration

from .django_resolver import ModelFieldResolver
from oscar.core.application import OscarConfig


class OscarOdinAppConfig(AppConfig):
class OscarOdinAppConfig(OscarConfig):
name = "oscar_odin"
label = "oscar_odin"
verbose_name = _("Oscar Odin")

def ready(self):
"""Hook that Django apps have been loaded."""

# Register the Django model field resolver
registration.register_field_resolver(ModelFieldResolver, Model)

# Register the default prefetches for the product queryset
from oscar_odin.mappings.prefetching.prefetch import register_default_prefetches

Expand Down
24 changes: 15 additions & 9 deletions oscar_odin/mappings/address.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
"""Mappings between odin and django-oscar models."""
import odin
from oscar.core.loading import get_model

from .. import resources
from ._common import OscarBaseMapping
from oscar.core.loading import get_model, get_class, get_classes

__all__ = (
"BillingAddressToResource",
Expand All @@ -14,22 +11,31 @@
ShippingAddressModel = get_model("order", "ShippingAddress")
CountryModel = get_model("address", "Country")

# mappings
OscarBaseMapping = get_class("oscar_odin.mappings.common", "OscarBaseMapping")

# resources
CountryResource, BillingAddressResource, ShippingAddressResource = get_classes(
"oscar_odin.resources.address",
["CountryResource", "BillingAddressResource", "ShippingAddressResource"],
)


class CountryToResource(OscarBaseMapping):
"""Mapping from country model to resource."""

from_obj = CountryModel
to_obj = resources.address.Country
to_obj = CountryResource


class BillingAddressToResource(OscarBaseMapping):
"""Mapping from billing address model to resource."""

from_obj = BillingAddressModel
to_obj = resources.address.BillingAddress
to_obj = BillingAddressResource

@odin.assign_field
def country(self) -> resources.address.Country:
def country(self) -> CountryResource:
"""Map country."""
return CountryToResource.apply(self.source.country)

Expand All @@ -38,9 +44,9 @@ class ShippingAddressToResource(OscarBaseMapping):
"""Mapping from shipping address model to resource."""

from_obj = ShippingAddressModel
to_obj = resources.address.ShippingAddress
to_obj = ShippingAddressResource

@odin.assign_field
def country(self) -> resources.address.Country:
def country(self) -> CountryResource:
"""Map country."""
return CountryToResource.apply(self.source.country)
13 changes: 8 additions & 5 deletions oscar_odin/mappings/auth.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
"""Mappings between odin and django auth models."""
from oscar.core.loading import get_model

from .. import resources
from ._common import OscarBaseMapping
from oscar.core.loading import get_model, get_class

__all__ = ("UserToResource",)

UserModel = get_model("auth", "User")

# mappings
OscarBaseMapping = get_class("oscar_odin.mappings.common", "OscarBaseMapping")

# resources
UserResource = get_class("oscar_odin.resources.auth", "UserResource")


class UserToResource(OscarBaseMapping):
"""Mapping from user model to resource."""

from_obj = UserModel
to_obj = resources.auth.User
to_obj = UserResource
61 changes: 42 additions & 19 deletions oscar_odin/mappings/catalogue.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,10 @@
from django.http import HttpRequest
from odin.mapping import ImmediateResult
from oscar.apps.partner.strategy import Default as DefaultStrategy
from oscar.core.loading import get_class, get_model
from oscar.core.loading import get_class, get_classes, get_model

from datetime import datetime

from .. import resources
from ._common import map_queryset, OscarBaseMapping
from ._model_mapper import ModelMapping
from ..utils import validate_resources
from .prefetching.prefetch import prefetch_product_queryset

Expand All @@ -41,12 +38,38 @@
StockRecordModel = get_model("partner", "StockRecord")
ProductAttributeValueModel = get_model("catalogue", "ProductAttributeValue")

# mappings
ModelMapping = get_class("oscar_odin.mappings.model_mapper", "ModelMapping")
map_queryset, OscarBaseMapping = get_classes(
"oscar_odin.mappings.common", ["map_queryset", "OscarBaseMapping"]
)

# resources
(
ProductImageResource,
CategoryResource,
ProductClassResource,
ProductResource,
ParentProductResource,
ProductRecommentationResource,
) = get_classes(
"oscar_odin.resources.catalogue",
[
"ProductImageResource",
"CategoryResource",
"ProductClassResource",
"ProductResource",
"ParentProductResource",
"ProductRecommentationResource",
],
)


class ProductImageToResource(OscarBaseMapping):
"""Map from an image model to a resource."""

from_obj = ProductImageModel
to_obj = resources.catalogue.Image
to_obj = ProductImageResource

@odin.map_field
def original(self, value: ImageFieldFile) -> str:
Expand All @@ -61,7 +84,7 @@ def original(self, value: ImageFieldFile) -> str:
class ProductImageToModel(OscarBaseMapping):
"""Map from an image resource to a model."""

from_obj = resources.catalogue.Image
from_obj = ProductImageResource
to_obj = ProductImageModel

@odin.map_field
Expand All @@ -76,7 +99,7 @@ class CategoryToResource(OscarBaseMapping):
"""Map from a category model to a resource."""

from_obj = CategoryModel
to_obj = resources.catalogue.Category
to_obj = CategoryResource

@odin.assign_field
def meta_title(self) -> str:
Expand All @@ -94,7 +117,7 @@ def image(self, value: ImageFieldFile) -> Optional[str]:
class CategoryToModel(OscarBaseMapping):
"""Map from a category resource to a model."""

from_obj = resources.catalogue.Category
from_obj = CategoryResource
to_obj = CategoryModel

@odin.map_field
Expand Down Expand Up @@ -132,21 +155,21 @@ class ProductClassToResource(OscarBaseMapping):
"""Map from a product class model to a resource."""

from_obj = ProductClassModel
to_obj = resources.catalogue.ProductClass
to_obj = ProductClassResource


class ProductClassToModel(OscarBaseMapping):
"""Map from a product class resource to a model."""

from_obj = resources.catalogue.ProductClass
from_obj = ProductClassResource
to_obj = ProductClassModel


class ProductToResource(OscarBaseMapping):
"""Map from a product model to a resource."""

from_obj = ProductModel
to_obj = resources.catalogue.Product
to_obj = ProductResource

@odin.assign_field
def title(self) -> str:
Expand All @@ -159,7 +182,7 @@ def meta_title(self) -> str:
return self.source.get_meta_title()

@odin.assign_field(to_list=True)
def images(self) -> List[resources.catalogue.Image]:
def images(self) -> List[ProductImageResource]:
"""Map related image."""
items = self.source.get_all_images()
return map_queryset(ProductImageToResource, items, context=self.context)
Expand Down Expand Up @@ -219,7 +242,7 @@ def attributes(self) -> Dict[str, Any]:
}

@odin.assign_field
def children(self) -> Tuple[Optional[List[resources.catalogue.Product]]]:
def children(self) -> Tuple[Optional[List[ProductResource]]]:
"""Children of parent products."""

if self.context.get("include_children", False) and self.source.is_parent:
Expand Down Expand Up @@ -253,7 +276,7 @@ def map_stock_price(self) -> Tuple[Decimal, str, int, bool]:
class ProductToModel(ModelMapping):
"""Map from a product resource to a model."""

from_obj = resources.catalogue.Product
from_obj = ProductResource
to_obj = ProductModel

mappings = (odin.define(from_field="children", skip_if_none=True),)
Expand Down Expand Up @@ -322,12 +345,12 @@ def product_class(self, value) -> ProductClassModel:


class RecommendedProductToModel(OscarBaseMapping):
from_obj = resources.catalogue.ProductRecommentation
from_obj = ProductRecommentationResource
to_obj = ProductModel


class ParentToModel(OscarBaseMapping):
from_obj = resources.catalogue.ParentProduct
from_obj = ParentProductResource
to_obj = ProductModel

@odin.assign_field
Expand Down Expand Up @@ -368,7 +391,7 @@ def product_to_resource(
include_children: bool = False,
product_mapper: OscarBaseMapping = ProductToResource,
**kwargs,
) -> Union[resources.catalogue.Product, Iterable[resources.catalogue.Product]]:
) -> Union[ProductResource, Iterable[ProductResource]]:
"""Map a product model to a resource.
This method will accept either a single product or an iterable of product
Expand Down Expand Up @@ -397,7 +420,7 @@ def product_queryset_to_resources(
include_children: bool = False,
product_mapper=ProductToResource,
**kwargs,
) -> Iterable[resources.catalogue.Product]:
) -> Iterable[ProductResource]:
"""Map a queryset of product models to a list of resources.
The request and user are optional, but if provided they are supplied to the
Expand All @@ -423,7 +446,7 @@ def product_queryset_to_resources(


def products_to_model(
products: List[resources.catalogue.Product],
products: List[ProductResource],
product_mapper=ProductToModel,
delete_related=False,
) -> Tuple[List[ProductModel], Dict]:
Expand Down
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 3dafbd5

Please sign in to comment.