Repository: saleor/saleor

Issue:
Serialization with dumpdata fails for product.Product and product.ProductVariant
### What I'm trying to achieve
Serialize data for products for backups (helped already when the database was not working any more with after updating the code base with custom changes)

### Steps to reproduce the problem
1.  ```
    $ ./manage.py dumpdata --indent 4 product.Product
    …
    CommandError: Unable to serialize database: Money('190.00', 'EUR') is not JSON serializable

    ```
2.  ```
    $ ./manage.py dumpdata --indent 4 product.ProductVariant
    …
    CommandError: Unable to serialize database: Money('90.00', 'EUR') is not JSON serializable
    ```
With a previous version of Saleor it worked without a problem. Then the field price was a PriceField, now it’s a MoneyField (both extending models.DecimalField).


### What I expected to happen
Serialization of the product data :-)

**System information**
Operating system: Debian 9 Stretch (up to date)
Browser: not applicable



Discussion:
same problem here, trying to backup
### System information
os: debian 9
saleor: april 2018 version
django: 2.0.3

Resolving patch:
diff --git a/saleor/core/utils/json_serializer.py b/saleor/core/utils/json_serializer.py
new file mode 100644
--- /dev/null
+++ b/saleor/core/utils/json_serializer.py
@@ -0,0 +1,20 @@
+from django.core.serializers.json import (
+    Deserializer as JsonDeserializer, DjangoJSONEncoder,
+    Serializer as JsonSerializer)
+from prices import Money
+
+
+class Serializer(JsonSerializer):
+    def _init_options(self):
+        super()._init_options()
+        self.json_kwargs['cls'] = CustomJsonEncoder
+
+
+class CustomJsonEncoder(DjangoJSONEncoder):
+    def default(self, obj):
+        if isinstance(obj, Money):
+            return obj.amount
+        return super().default(obj)
+
+
+Deserializer = JsonDeserializer
diff --git a/saleor/settings.py b/saleor/settings.py
--- a/saleor/settings.py
+++ b/saleor/settings.py
@@ -535,3 +535,7 @@ def get_host():
     RAVEN_CONFIG = {
         'dsn': SENTRY_DSN,
         'release': __version__}
+
+
+SERIALIZATION_MODULES = {
+    'json': 'saleor.core.utils.json_serializer'}


Test patch:
diff --git a/tests/test_product.py b/tests/test_product.py
--- a/tests/test_product.py
+++ b/tests/test_product.py
@@ -1,8 +1,10 @@
 import datetime
 import json
+from decimal import Decimal
 from unittest.mock import patch
 
 import pytest
+from django.core import serializers
 from django.urls import reverse
 from prices import Money, TaxedMoney, TaxedMoneyRange
 
@@ -589,3 +591,42 @@ def test_variant_base_price(product):
     variant.save()
 
     assert variant.base_price == variant.price_override
+
+
+def test_product_json_serialization(product):
+    product.price = Money('10.00', 'USD')
+    product.save()
+    data = json.loads(serializers.serialize(
+        "json", models.Product.objects.all()))
+    assert data[0]['fields']['price'] == '10.00'
+
+
+def test_product_json_deserialization(default_category, product_type):
+    product_json = """
+    [{{
+        "model": "product.product",
+        "pk": 60,
+        "fields": {{
+            "seo_title": null,
+            "seo_description": "Future almost cup national. Study left manage we else.",
+            "product_type": {product_type_pk},
+            "name": "Kelly-Clark",
+            "description": "Future almost cup national",
+            "category": {category_pk},
+            "price": "35.98",
+            "available_on": null,
+            "is_published": true,
+            "attributes": "{{\\"9\\": \\"24\\", \\"10\\": \\"26\\", \\"11\\": \\"29\\"}}",
+            "updated_at": "2018-07-19T13:30:24.195Z",
+            "is_featured": false,
+            "charge_taxes": true,
+            "tax_rate": "standard"
+        }}
+    }}]
+    """.format(
+        category_pk=default_category.pk, product_type_pk=product_type.pk)
+    product_deserialized = list(serializers.deserialize(
+        'json', product_json, ignorenonexistent=True))[0]
+    product_deserialized.save()
+    product = models.Product.objects.first()
+    assert product.price == Money(Decimal('35.98'), 'USD')