diff --git a/Makefile b/Makefile index 42bfc29..9428b2d 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,9 @@ black: @black oscar_odin/ @black tests/ +test: + python3 manage.py test tests/ + ill: rm db.sqlite3 cp klaas.sqlite3 db.sqlite3 diff --git a/oscar_odin/django_resolver.py b/oscar_odin/django_resolver.py index 0c54f0b..abd6b9d 100644 --- a/oscar_odin/django_resolver.py +++ b/oscar_odin/django_resolver.py @@ -1,8 +1,6 @@ """Resolver for resolving attributes on Django models.""" from typing import Dict, Optional -from django.db.models import ForeignKey - from odin import Field from odin.mapping import FieldResolverBase from odin.utils import getmeta @@ -17,7 +15,8 @@ def get_field_dict(self) -> Dict[str, Optional[Field]]: meta = getmeta(self.obj) if meta.object_name == "Product": - fields = {f: meta._forward_fields_map[f] for f in meta._forward_fields_map} + # pylint: disable=protected-access + fields = dict(meta._forward_fields_map.items()) else: fields = {f.name: f for f in meta.fields} diff --git a/oscar_odin/management/commands/test_illshit.py b/oscar_odin/management/commands/test_illshit.py index 881fa8f..7e1647a 100644 --- a/oscar_odin/management/commands/test_illshit.py +++ b/oscar_odin/management/commands/test_illshit.py @@ -1,4 +1,4 @@ -# pylint: disable=W0611 +# pylint: skip-file import io import PIL diff --git a/oscar_odin/mappings/_model_mapper.py b/oscar_odin/mappings/_model_mapper.py index b49b537..a01a98a 100644 --- a/oscar_odin/mappings/_model_mapper.py +++ b/oscar_odin/mappings/_model_mapper.py @@ -1,10 +1,8 @@ """Extended model mapper for Django models.""" from typing import Sequence, cast -from collections import defaultdict from django.db.models.fields.related import ForeignKey, ManyToManyField from django.db.models.fields.reverse_related import ( - OneToOneRel, ManyToOneRel, ManyToManyRel, ) @@ -32,9 +30,8 @@ def __new__(cls, name, bases, attrs): # Extract out foreign field types. mapping_type.many_to_one_fields = many_to_one_fields = [] mapping_type.one_to_many_fields = one_to_many_fields = [] - mapping_type.many_to_many_fields = many_to_many_fields = [ - field for field in meta.many_to_many - ] + + mapping_type.many_to_many_fields = [field for field in meta.many_to_many] mapping_type.foreign_key_fields = [ field for field in meta.fields if isinstance(field, ForeignKey) ] @@ -65,7 +62,7 @@ class ModelMapping(MappingBase, metaclass=ModelMappingMeta): def create_object(self, **field_values): """Create a new product model.""" attribute_values = field_values.pop("attributes", []) - + print(self.many_to_many_fields) m2m_related_values = { field: field_values.pop(field.name) for field in self.many_to_many_fields @@ -90,8 +87,6 @@ def create_object(self, **field_values): for key, value in attribute_values.items(): parent.attr.set(key, value) - self.context.add_attribute_data((parent, attribute_values)) - for relation, instances in m2o_related_values.items(): if instances: self.context.add_instances_to_m2o_relation( diff --git a/oscar_odin/mappings/catalogue.py b/oscar_odin/mappings/catalogue.py index 1b758ad..f06937f 100644 --- a/oscar_odin/mappings/catalogue.py +++ b/oscar_odin/mappings/catalogue.py @@ -1,12 +1,12 @@ +# pylint: disable=W0613 """Mappings between odin and django-oscar models.""" from decimal import Decimal -from typing import Any, Dict, Iterable, List, Optional, Tuple, Union, NamedTuple +from typing import Any, Dict, Iterable, List, Optional, Tuple, Union import odin from django.contrib.auth.models import AbstractUser from django.db import transaction from django.db.models import QuerySet, Model, ManyToManyField, ForeignKey -from django.core.exceptions import ValidationError from django.db.models.fields.files import ImageFieldFile from django.http import HttpRequest from oscar.apps.partner.strategy import Default as DefaultStrategy @@ -15,7 +15,7 @@ from datetime import datetime from .. import resources -from ..resources.catalogue import Structure, ProductAttributeValue +from ..resources.catalogue import Structure from ._common import map_queryset from ._model_mapper import ModelMapping from .utils import ( @@ -101,7 +101,6 @@ class CategoryToModel(odin.Mapping): @odin.map_field def image(self, value: Optional[str]) -> Optional[str]: """Convert value into a pure URL.""" - # TODO convert into a form that can be accepted by a model return value @odin.map_field @@ -223,9 +222,7 @@ def map_stock_price(self) -> Tuple[Decimal, str, int]: stock_strategy: DefaultStrategy = self.context["stock_strategy"] # Switch here based on if this is a parent or child product - price, availability, stock_record = stock_strategy.fetch_for_product( - self.source - ) + price, availability, _ = stock_strategy.fetch_for_product(self.source) if availability.is_available_to_buy: return price.excl_tax, price.currency, availability.num_available @@ -400,7 +397,7 @@ def products_to_db( context.fields_to_update = fields_to_update context.identifier_mapping = identifier_mapping - errors = {} + errors = [] with transaction.atomic(): # Save all the foreign keys; parents and productclasses @@ -410,7 +407,7 @@ def products_to_db( save_products(instances, context, errors) # Save all product attributes - save_attributes(context, errors) + save_attributes(instances) # Save and set all one to many relations; images, stockrecords save_one_to_many(context, errors) diff --git a/oscar_odin/mappings/context.py b/oscar_odin/mappings/context.py index cc6327d..492380b 100644 --- a/oscar_odin/mappings/context.py +++ b/oscar_odin/mappings/context.py @@ -1,8 +1,6 @@ from collections import defaultdict from operator import attrgetter -from odin.utils import getmeta - from oscar_odin.utils import in_bulk from oscar_odin.exceptions import OscarOdinException @@ -18,6 +16,7 @@ def get_instances_to_create_or_update(Model, instances, identifier_mapping): identifiers = identifier_mapping.get(Model, {}) if identifiers: + # pylint: disable=protected-access id_mapping = in_bulk( Model._default_manager, instances=instances, field_names=identifiers ) @@ -31,6 +30,7 @@ def get_instances_to_create_or_update(Model, instances, identifier_mapping): if key in id_mapping: instance.pk = id_mapping[key] + # pylint: disable=protected-access instance._state.db = "default" instance._state.adding = False instances_to_update.append(instance) @@ -90,11 +90,10 @@ def get_fields_to_update(self, Model): def get_create_and_update_relations(self, related_instance_items): to_create = defaultdict(list) to_update = defaultdict(list) - instance_update_pks = [] for relation in related_instance_items.keys(): all_instances = [] - for product, instances in related_instance_items[relation]: + for _, instances in related_instance_items[relation]: all_instances.extend(instances) ( @@ -121,7 +120,6 @@ def get_o2m_relations(self): def get_fk_relations(self): to_create = defaultdict(list) to_update = defaultdict(list) - instance_update_pks = [] for relation, instances in self.foreign_key_items.items(): ( diff --git a/oscar_odin/mappings/utils.py b/oscar_odin/mappings/utils.py index 7c484e0..f2c4655 100644 --- a/oscar_odin/mappings/utils.py +++ b/oscar_odin/mappings/utils.py @@ -1,21 +1,25 @@ from collections import defaultdict -from django.db.models import prefetch_related_objects +from django.core.exceptions import ValidationError from oscar.core.loading import get_model from oscar_odin.mappings.context import get_instances_to_create_or_update -from oscar_odin.utils import querycounter, in_bulk +from oscar_odin.utils import in_bulk Product = get_model("catalogue", "Product") ProductAttributeValue = get_model("catalogue", "ProductAttributeValue") -def validate_instances(instances, errors={}, validate_unique=True): +def validate_instances(instances, errors, validate_unique=True): validated_instances = [] for instance in instances: - instance.full_clean(validate_unique=validate_unique) + try: + instance.full_clean(validate_unique=validate_unique) + except ValidationError as e: + errors.append(e) + validated_instances.append(instance) return validated_instances, errors @@ -58,7 +62,7 @@ def save_one_to_many(context, errors): for relation, instances in instances_to_create.items(): validated_instances_to_create, errors = validate_instances(instances, errors) - relation.related_model.objects.bulk_create(instances) + relation.related_model.objects.bulk_create(validated_instances_to_create) for relation, instances in instances_to_update.items(): fields = context.get_fields_to_update(relation.related_model) @@ -71,7 +75,7 @@ def save_many_to_many(context, errors): # Create many to many's for relation, instances in m2m_to_create.items(): - validated_m2m_instances, errors = validate_instances(instances) + validated_m2m_instances, errors = validate_instances(instances, errors) relation.related_model.objects.bulk_create(validated_m2m_instances) # Update many to many's @@ -110,13 +114,13 @@ def save_many_to_many(context, errors): Through.objects.bulk_create(throughs.values()) -def save_attributes(context, errors): +def save_attributes(instances): attributes_to_create = [] attributes_to_delete = [] attributes_to_update = [] fields_to_be_updated = set() - for product, attr in context.attribute_data: + for product in instances: product.attr.invalidate() ( to_be_deleted, diff --git a/pylintrc b/pylintrc index 080e1c1..422dfbd 100644 --- a/pylintrc +++ b/pylintrc @@ -4,5 +4,4 @@ load-plugins = pylint_django score = n [MESSAGES CONTROL] -disable = R,C,W5103,W0707,E5110,W0511 - +disable = R,C,W5103,W0707,E5110,W0511,W0102