Skip to content

Commit

Permalink
[FEAT] Delete related models (#16)
Browse files Browse the repository at this point in the history
* chore ♻️ add sorl-thumbnail in test requirements

* fix 🔧 resolve lint errors

* feat ✨ delete related models of product

* tests ✅ add tests checking product related model deletion

* refactor 📦 try sorl thumbnail version 12.9

* refactor 📦 try sorl thumbnail version 12.8

* fix 🔧 update poetry lock

* refactor 📦 improve deleting related models

* fix 🔧 resolve tests failing

* lint

* lint again

* feat ⭐ categories update based on fields_to_update attribute

* tests ✅ only validate related_models for fields in fields_to_update for bulk_update

* lint

* fix 🔧 filter related_models based on product before deleting

* tests ✅ test partial deletion of one_to_many related fields

* tests ✅ test fields_to_update on product concrete fields

* feat ⭐ add and update product_class using product resource

* feat ⭐ validate resources before updating

* fix 🔧 resolve tests failing

* refactor 📦 remove continue statements

* refactor 📦 remove fail fast

* refactor 📦 rename to instance keys and identifier

* remove import

---------

Co-authored-by: Viggo de Vries <[email protected]>
  • Loading branch information
samar-hassan and viggo-devries authored Feb 23, 2024
1 parent f13b238 commit 67446e6
Show file tree
Hide file tree
Showing 12 changed files with 917 additions and 221 deletions.
10 changes: 2 additions & 8 deletions oscar_odin/mappings/_model_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,10 @@ def add_related_field_values_to_context(self, parent, related_field_values):
)

for relation, instances in related_field_values["m2m_related_values"].items():
if instances:
self.context.add_instances_to_m2m_relation(
relation, (parent, instances)
)
self.context.add_instances_to_m2m_relation(relation, (parent, instances))

for relation, instances in related_field_values["o2m_related_values"].items():
if instances:
self.context.add_instances_to_o2m_relation(
relation, (parent, instances)
)
self.context.add_instances_to_o2m_relation(relation, (parent, instances))

for field, instance in related_field_values["fk_related_values"].items():
if instance:
Expand Down
27 changes: 16 additions & 11 deletions oscar_odin/mappings/catalogue.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union

from django.contrib.auth.models import AbstractUser
from django.db.models import QuerySet, Model, ManyToManyField, ForeignKey
from django.db.models import QuerySet
from django.db.models.fields.files import ImageFieldFile
from django.http import HttpRequest
from oscar.apps.partner.strategy import Default as DefaultStrategy
Expand All @@ -15,9 +15,9 @@
from datetime import datetime

from .. import resources
from ..resources.catalogue import Structure
from ._common import map_queryset, OscarBaseMapping
from ._model_mapper import ModelMapping
from ..utils import validate_resources

from .context import ProductModelMapperContext
from .constants import ALL_CATALOGUE_FIELDS, MODEL_IDENTIFIERS_MAPPING
Expand Down Expand Up @@ -143,11 +143,6 @@ class ProductToResource(OscarBaseMapping):
from_obj = ProductModel
to_obj = resources.catalogue.Product

@odin.map_field
def structure(self, value: str) -> Structure:
"""Map structure to enum."""
return Structure(value)

@odin.assign_field
def title(self) -> str:
"""Map title field."""
Expand Down Expand Up @@ -296,7 +291,7 @@ def stockrecords(

@odin.map_field
def product_class(self, value) -> ProductClassModel:
if not value and self.source.structure == ProductModel.CHILD:
if not value or self.source.structure == ProductModel.CHILD:
return None

return ProductClassToModel.apply(value)
Expand Down Expand Up @@ -391,9 +386,11 @@ def product_queryset_to_resources(


def products_to_model(
products: List[resources.catalogue.Product], product_mapper=ProductToModel
products: List[resources.catalogue.Product],
product_mapper=ProductToModel,
delete_related=False,
) -> Tuple[List[ProductModel], Dict]:
context = ProductModelMapperContext(ProductModel)
context = ProductModelMapperContext(ProductModel, delete_related=delete_related)

result = product_mapper.apply(products, context=context)

Expand All @@ -408,14 +405,22 @@ def products_to_db(
fields_to_update=ALL_CATALOGUE_FIELDS,
identifier_mapping=MODEL_IDENTIFIERS_MAPPING,
product_mapper=ProductToModel,
delete_related=False,
) -> Tuple[List[ProductModel], Dict]:
"""Map mulitple products to a model and store them in the database.
The method will first bulk update or create the foreign keys like parent products and productclasses
After that all the products will be bulk saved.
At last all related models like images, stockrecords, and related_products can will be saved and set on the product.
"""
instances, context = products_to_model(products, product_mapper=product_mapper)
errors = validate_resources(products)
if errors:
return [], errors
instances, context = products_to_model(
products,
product_mapper=product_mapper,
delete_related=delete_related,
)

products, errors = context.bulk_save(
instances, fields_to_update, identifier_mapping
Expand Down
21 changes: 17 additions & 4 deletions oscar_odin/mappings/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@
PRODUCT_DESCRIPTION = "Product.description"
PRODUCT_META_TITLE = "Product.meta_title"
PRODUCT_META_DESCRIPTION = "Product.meta_description"
PRODUCT_PRODUCT_CLASS = "Product.product_class"
PRODUCT_PARENT = "Product.parent"
PRODUCT_IS_DISCOUNTABLE = "Product.is_discountable"

PRODUCTCLASS_SLUG = "ProductClass.slug"
PRODUCTCLASS_REQUIRESSHIPPING = "ProductClass.requires_shipping"
PRODUCTCLASS_TRACKSTOCK = "ProductClass.track_stock"
PRODUCTCLASS_NAME = "ProductClass.name"

CATEGORY_NAME = "Category.name"
CATEGORY_CODE = "Category.code"
CATEGORY_DESCRIPTION = "Category.description"
Expand Down Expand Up @@ -51,11 +55,17 @@
PRODUCT_DESCRIPTION,
PRODUCT_META_TITLE,
PRODUCT_META_DESCRIPTION,
PRODUCT_PRODUCT_CLASS,
PRODUCT_IS_DISCOUNTABLE,
PRODUCT_PARENT,
]

ALL_PRODUCTCLASS_FIELDS = [
PRODUCTCLASS_SLUG,
PRODUCTCLASS_REQUIRESSHIPPING,
PRODUCTCLASS_TRACKSTOCK,
PRODUCTCLASS_NAME,
]

ALL_CATEGORY_FIELDS = [
CATEGORY_NAME,
CATEGORY_CODE,
Expand Down Expand Up @@ -85,13 +95,16 @@


ALL_CATALOGUE_FIELDS = (
ALL_PRODUCT_FIELDS + ALL_PRODUCTIMAGE_FIELDS + ALL_STOCKRECORD_FIELDS
ALL_PRODUCT_FIELDS
+ ALL_PRODUCTIMAGE_FIELDS
+ ALL_STOCKRECORD_FIELDS
+ [PRODUCTCLASS_SLUG, CATEGORY_CODE]
)

MODEL_IDENTIFIERS_MAPPING = {
Category: ("code",),
Product: ("upc",),
StockRecord: ("product_id",),
StockRecord: ("partner_id", "partner_sku"),
ProductClass: ("slug",),
ProductImage: ("code",),
Partner: ("slug",),
Expand Down
Loading

0 comments on commit 67446e6

Please sign in to comment.