Hi,
Here is a method to override django-import-export functions with product_db 
import export function.
1. pip install django-import-export
2. Add to# settings.py

INSTALLED_APPS = (
    ...
    'import_export',)

3. Add image files to /tmp/orig/product

4. Make changes to the 
/home/sathish/impexp/lib/python2.7/site-packages/cartridge/shop/admin.py as in 
the attached file.

5. python manage.py runserver

6. Enter admin panel

7. click on products

8. You will find import and export buttons in admin panel

9. Do export of file using csv format as csv format is only supported now for 
importing.

10. save the downloaded file.

11. Delete the single django pony product for testing purpose.

12. Do import of same csv file.

13. Review wont produce any output as import function is overridden by 
product_db import.

14. confirm submit. and products will be added as done by product_db.

Thanks and Regards,

Sathish Anton. A

+919176613030



On Friday, April 29, 2016 at 11:36:33 PM UTC+5:30, Sathish Anton wrote:
>
> Hi,
> Please provide Suggestion on how to make import and export of products 
> using csv from admin panel?
>
> Thanks and Regards,
> Sathish Anton. A
>

-- 
You received this message because you are subscribed to the Google Groups 
"Mezzanine Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Attachment: admin.py.orig
Description: Binary data

*** admin.py.orig	2016-05-03 14:25:39.686562188 +0530
--- admin.py	2016-05-03 21:20:08.118995198 +0530
***************
*** 1,5 ****
--- 1,21 ----
  from __future__ import unicode_literals
  from future.builtins import super, zip
+ from import_export import resources
+ from import_export.admin import ImportExportModelAdmin
+ from import_export.results import Error, Result, RowResult
+ try:
+     from collections import OrderedDict
+ except ImportError:
+     from django.utils.datastructures import SortedDict as OrderedDict
+     
+ import tablib
+ import csv
+ import os
+ import shutil
+ import sys
+ import datetime
+ from optparse import make_option
+ 
  
  """
  Admin classes for all the shop models.
***************
*** 41,46 ****
--- 57,63 ----
                                    TabularDynamicInlineAdmin,
                                    BaseTranslationModelAdmin)
  from mezzanine.pages.admin import PageAdmin
+ from mezzanine.core.models import CONTENT_STATUS_PUBLISHED
  
  from cartridge.shop.fields import MoneyField
  from cartridge.shop.forms import ProductAdminForm, ProductVariationAdminForm
***************
*** 51,56 ****
--- 68,116 ----
  from cartridge.shop.models import OrderItem, Sale, DiscountCode
  from cartridge.shop.views import HAS_PDF
  
+ # images get copied from thie directory
+ LOCAL_IMAGE_DIR = "/tmp/orig"
+ # images get copied to this directory under STATIC_ROOT
+ IMAGE_SUFFIXES = [".jpg", ".png", ".JPG", ".jpeg", ".JPEG", ".tif", ".gif", ".GIF"]
+ EMPTY_IMAGE_ENTRIES = ["Please add", "N/A", ""]
+ DATE_FORMAT = "%Y-%m-%d"
+ TIME_FORMAT = "%H:%M"
+ 
+ # Here we define what column headings are used in the csv.
+ TITLE = _("Title")
+ CONTENT = _("Content")
+ DESCRIPTION = _("Description")
+ SKU = _("SKU")
+ IMAGE = _("Image")
+ CATEGORY = _("Category")
+ SUB_CATEGORY = _("Sub-Category")
+ # SIZE = _("Size")
+ NUM_IN_STOCK = _("Number in Stock")
+ UNIT_PRICE = _("Unit Price")
+ SALE_PRICE = _("Sale Price")
+ SALE_START_DATE = _("Sale Start Date")
+ SALE_START_TIME = _("Sale Start Time")
+ SALE_END_DATE = _("Sale End Date")
+ SALE_END_TIME = _("Sale End Time")
+ 
+ DATETIME_FORMAT = "%s %s" % (DATE_FORMAT, TIME_FORMAT)
+ SITE_MEDIA_IMAGE_DIR = _("product")
+ SITE_MEDIA_IMAGE_NDIR = _("media/product")
+ PRODUCT_IMAGE_DIR = os.path.join(str(settings.STATIC_ROOT), str(SITE_MEDIA_IMAGE_NDIR))
+ 
+ 
+ TYPE_CHOICES = dict()
+ for id, choice in settings.SHOP_OPTION_TYPE_CHOICES:
+     TYPE_CHOICES[choice] = id
+ 
+ fieldnames = [TITLE, CONTENT, DESCRIPTION, CATEGORY, SUB_CATEGORY,
+     SKU, IMAGE, NUM_IN_STOCK, UNIT_PRICE,
+     SALE_PRICE, SALE_START_DATE, SALE_START_TIME, SALE_END_DATE, SALE_END_TIME]
+ # TODO: Make sure no options conflict with other fieldnames.
+ fieldnames += TYPE_CHOICES.keys()
+ csv_file = "/tmp/importcsv.csv"
+ 
+ 
  
  # Lists of field names.
  option_fields = [f.name for f in ProductVariation.option_fields()]
***************
*** 154,165 ****
      product_list_editable.extend(extra_list_fields)
  
  
! class ProductAdmin(DisplayableAdmin):
  
      class Media:
          js = (static("cartridge/js/admin/product_variations.js"),)
          css = {"all": (static("cartridge/css/admin/product.css"),)}
  
      list_display = product_list_display
      list_display_links = ("admin_thumb", "title")
      list_editable = product_list_editable
--- 214,385 ----
      product_list_editable.extend(extra_list_fields)
  
  
! class ProductResource(resources.ModelResource):
! 
!     def _product_from_row(self, row):
!         product, created = Product.objects.get_or_create(title=row[TITLE])
!         product.content = row[CONTENT]
!         product.description = row[DESCRIPTION]
!         # TODO: set the 2 below from spreadsheet.
!         product.status = CONTENT_STATUS_PUBLISHED
!         product.available = True
!         # TODO: allow arbitrary level/number of categories.
!         print("sub_cat=%s" % row[SUB_CATEGORY])
!         base_cat, created = Category.objects.get_or_create(title=row[CATEGORY])
!         if row[SUB_CATEGORY]:
!             sub_cat, created = Category.objects.get_or_create(
!                 title=row[SUB_CATEGORY],
!                 parent=base_cat)
!             product.categories.add(sub_cat)
!         shop_cat, created = Category.objects.get_or_create(title="Shop")
!         product.categories.add(shop_cat)
!         return product
! 
!     def _make_image(self, image_str, product):
!         if image_str in EMPTY_IMAGE_ENTRIES:
!             return None
!         # try adding various image suffixes, if none given in original filename.
!         root, suffix = os.path.splitext(image_str)
!         if suffix not in IMAGE_SUFFIXES:
!             raise CommandError("INCORRECT SUFFIX: %s" % image_str)
!         image_path = os.path.join(LOCAL_IMAGE_DIR, image_str)
!         if not os.path.exists(image_path):
!             raise CommandError("NO FILE %s" % image_path)
!         else:
!             print("local orig image_path= %s PRODUCT_IMAGE_DIR= %s" % (image_path, PRODUCT_IMAGE_DIR))
!         shutil.copy(image_path, PRODUCT_IMAGE_DIR)
!         # shutil.copy(image_path, os.path.join(PRODUCT_IMAGE_DIR, "orig"))
!         print("SITE_MEDIA_IMAGE_DIR=%s image_str=%s" % (SITE_MEDIA_IMAGE_DIR, image_str))
!         image, created = ProductImage.objects.get_or_create(
!             file="%s" % (image_str),
!             description=image_str,  # TODO: handle column for this.
!             product=product)
!         return image
! 
! 
!     def _make_date(self, date_str, time_str):
!         date_string = '%s %s' % (date_str, time_str)
!         date = datetime.datetime.strptime(date_string, DATETIME_FORMAT)
!         return date
! 
! 
!     def export(self, queryset=None):
!         print(_("Exporting .."))
!         data = tablib.Dataset()
!         count = 0
!         for pv in ProductVariation.objects.all():
!             row = dict()
!             row[TITLE] = pv.product.title
!             row[CONTENT] = pv.product.content
!             row[DESCRIPTION] = pv.product.description
!             row[SKU] = pv.sku
!             row[IMAGE] = pv.image
!             # TODO: handle multiple categories, and multiple levels of categories
!             cat = pv.product.categories.all()[0]
!             if cat.parent:
!                 row[SUB_CATEGORY] = cat.title
!                 row[CATEGORY] = cat.parent.title
!             else:
!                 row[CATEGORY] = cat.title
!                 row[SUB_CATEGORY] = ""
! 
!             for option in TYPE_CHOICES:
!                 row[option] = getattr(pv, "option%s" % TYPE_CHOICES[option])
! 
!             row[NUM_IN_STOCK] = pv.num_in_stock
!             row[UNIT_PRICE] = pv.unit_price
!             row[SALE_PRICE] = pv.sale_price
!             try:
!                 row[SALE_START_DATE] = pv.sale_from.strftime(DATE_FORMAT)
!                 row[SALE_START_TIME] = pv.sale_from.strftime(TIME_FORMAT)
!             except AttributeError:
!                 pass
!             try:
!                 row[SALE_END_DATE] = pv.sale_to.strftime(DATE_FORMAT)
!                 row[SALE_END_TIME] = pv.sale_to.strftime(TIME_FORMAT)
!             except AttributeError:
!                 pass
!             try:
!                 row[SALE_END_DATE] = pv.sale_to.strftime(DATE_FORMAT)
!                 row[SALE_END_TIME] = pv.sale_to.strftime(TIME_FORMAT)
!             except AttributeError:
!                 pass
!             if count == 0:
!                 list_keys = [ k for k in row ]
!                 data.headers = list_keys
!                 count=1
! 
!             list_values = [ v for v in row.values() ]
!             print(list_values)
!             data.append(list_values)
!         return data
! 
!     def import_data(self, dataset, dry_run=False, raise_errors=False,
!                     use_transactions=None, **kwargs):
!         filehandle = open(csv_file, 'w')
!         filehandle.write(dataset.csv)
!         filehandle.close()
!         result = self.get_result_class()()
!         result.diff_headers = self.get_diff_headers()
!         result.totals = OrderedDict([(RowResult.IMPORT_TYPE_NEW, 0),
!                                         (RowResult.IMPORT_TYPE_UPDATE, 0),
!                                         (RowResult.IMPORT_TYPE_DELETE, 0),
!                                         (RowResult.IMPORT_TYPE_SKIP, 0),
!                                         (RowResult.IMPORT_TYPE_ERROR, 0),
!                                         ('total', len(dataset))])
!         result.totals['total'] = len(dataset)
! 
!         reader = csv.DictReader(open(csv_file))
!         for row in reader:
!             print(row)
!             product = self._product_from_row(row)
!             try:
!                 variation = ProductVariation.objects.create(
!                     # strip whitespace
!                     sku=row[SKU].replace(" ", ""),
!                     product=product,
!                 )
!             except IntegrityError:
!                 raise CommandError("Product with SKU exists! sku: %s" % row[SKU])
!             if row[NUM_IN_STOCK]:
!                 variation.num_in_stock = row[NUM_IN_STOCK]
!             if row[UNIT_PRICE]:
!                 variation.unit_price = row[UNIT_PRICE]
!             if row[SALE_PRICE]:
!                 variation.sale_price = row[SALE_PRICE]
!             #if row[SALE_START_DATE] and row[SALE_START_TIME]:
!                 #variation.sale_from = self._make_date(row[SALE_START_DATE],
!                 #                                    row[SALE_START_TIME])
!             #if row[SALE_END_DATE] and row[SALE_END_TIME]:
!                 #variation.sale_to = self._make_date(row[SALE_END_DATE],
!                 #                                   row[SALE_END_TIME])
!             for option in TYPE_CHOICES:
!                 if row[option]:
!                     name = "option%s" % TYPE_CHOICES[option]
!                     setattr(variation, name, row[option])
!                     new_option, created = ProductOption.objects.get_or_create(
!                         type=TYPE_CHOICES[option],  # TODO: set dynamically
!                         name=row[option])
!             variation.save()
!             image = self._make_image(row[IMAGE], product)
!             if image:
!                 variation.image = image
!             product.variations.manage_empty()
!             product.variations.set_default_images([])
!             product.copy_default_variation()
!             product.save()
!         return result
!     class Meta:
!         model = Product
! 
! 
! class ProductAdmin(ImportExportModelAdmin, DisplayableAdmin):
  
      class Media:
          js = (static("cartridge/js/admin/product_variations.js"),)
          css = {"all": (static("cartridge/css/admin/product.css"),)}
  
+     resource_class = ProductResource
      list_display = product_list_display
      list_display_links = ("admin_thumb", "title")
      list_editable = product_list_editable
from __future__ import unicode_literals
from future.builtins import super, zip
from import_export import resources
from import_export.admin import ImportExportModelAdmin
from import_export.results import Error, Result, RowResult
try:
    from collections import OrderedDict
except ImportError:
    from django.utils.datastructures import SortedDict as OrderedDict
    
import tablib
import csv
import os
import shutil
import sys
import datetime
from optparse import make_option


"""
Admin classes for all the shop models.

Many attributes in here are controlled by the ``SHOP_USE_VARIATIONS``
setting which defaults to True. In this case, variations are managed in
the product change view, and are created given the ``ProductOption``
values selected.

A handful of fields (mostly those defined on the abstract ``Priced``
model) are duplicated across both the ``Product`` and
``ProductVariation`` models, with the latter being the definitive
source, and the former supporting denormalised data that can be
referenced when iterating through products, without having to
query the underlying variations.

When ``SHOP_USE_VARIATIONS`` is set to False, a single variation is
still stored against each product, to keep consistent with the overall
model design. Since from a user perspective there are no variations,
the inlines for variations provide a single inline for managing the
one variation per product, so in the product change view, a single set
of price fields are available via the one variation inline.

Also when ``SHOP_USE_VARIATIONS`` is set to False, the denormalised
price fields on the product model are presented as editable fields in
the product change list - if these form fields are used, the values
are then pushed back onto the one variation for the product.
"""

from copy import deepcopy

from django.contrib import admin
from django.contrib.admin.templatetags.admin_static import static
from django.db.models import ImageField
from django.utils.translation import ugettext_lazy as _

from mezzanine.conf import settings
from mezzanine.core.admin import (DisplayableAdmin,
                                  TabularDynamicInlineAdmin,
                                  BaseTranslationModelAdmin)
from mezzanine.pages.admin import PageAdmin
from mezzanine.core.models import CONTENT_STATUS_PUBLISHED

from cartridge.shop.fields import MoneyField
from cartridge.shop.forms import ProductAdminForm, ProductVariationAdminForm
from cartridge.shop.forms import ProductVariationAdminFormset
from cartridge.shop.forms import DiscountAdminForm, ImageWidget, MoneyWidget
from cartridge.shop.models import Category, Product, ProductImage
from cartridge.shop.models import ProductVariation, ProductOption, Order
from cartridge.shop.models import OrderItem, Sale, DiscountCode
from cartridge.shop.views import HAS_PDF

# images get copied from thie directory
LOCAL_IMAGE_DIR = "/tmp/orig"
# images get copied to this directory under STATIC_ROOT
IMAGE_SUFFIXES = [".jpg", ".png", ".JPG", ".jpeg", ".JPEG", ".tif", ".gif", ".GIF"]
EMPTY_IMAGE_ENTRIES = ["Please add", "N/A", ""]
DATE_FORMAT = "%Y-%m-%d"
TIME_FORMAT = "%H:%M"

# Here we define what column headings are used in the csv.
TITLE = _("Title")
CONTENT = _("Content")
DESCRIPTION = _("Description")
SKU = _("SKU")
IMAGE = _("Image")
CATEGORY = _("Category")
SUB_CATEGORY = _("Sub-Category")
# SIZE = _("Size")
NUM_IN_STOCK = _("Number in Stock")
UNIT_PRICE = _("Unit Price")
SALE_PRICE = _("Sale Price")
SALE_START_DATE = _("Sale Start Date")
SALE_START_TIME = _("Sale Start Time")
SALE_END_DATE = _("Sale End Date")
SALE_END_TIME = _("Sale End Time")

DATETIME_FORMAT = "%s %s" % (DATE_FORMAT, TIME_FORMAT)
SITE_MEDIA_IMAGE_DIR = _("product")
SITE_MEDIA_IMAGE_NDIR = _("media/product")
PRODUCT_IMAGE_DIR = os.path.join(str(settings.STATIC_ROOT), str(SITE_MEDIA_IMAGE_NDIR))


TYPE_CHOICES = dict()
for id, choice in settings.SHOP_OPTION_TYPE_CHOICES:
    TYPE_CHOICES[choice] = id

fieldnames = [TITLE, CONTENT, DESCRIPTION, CATEGORY, SUB_CATEGORY,
    SKU, IMAGE, NUM_IN_STOCK, UNIT_PRICE,
    SALE_PRICE, SALE_START_DATE, SALE_START_TIME, SALE_END_DATE, SALE_END_TIME]
# TODO: Make sure no options conflict with other fieldnames.
fieldnames += TYPE_CHOICES.keys()
csv_file = "/tmp/importcsv.csv"



# Lists of field names.
option_fields = [f.name for f in ProductVariation.option_fields()]
_flds = lambda s: [f.name for f in Order._meta.fields if f.name.startswith(s)]
billing_fields = _flds("billing_detail")
shipping_fields = _flds("shipping_detail")


################
#  CATEGORIES  #
################

# Categories fieldsets are extended from Page fieldsets, since
# categories are a Mezzanine Page type.
category_fieldsets = deepcopy(PageAdmin.fieldsets)
category_fieldsets[0][1]["fields"][3:3] = ["content", "products"]
category_fieldsets += ((_("Product filters"), {
    "fields": ("sale", ("price_min", "price_max"), "combined"),
    "classes": ("collapse-closed",)},),)
if settings.SHOP_CATEGORY_USE_FEATURED_IMAGE:
    category_fieldsets[0][1]["fields"].insert(3, "featured_image")

# Options are only used when variations are in use, so only provide
# them as filters for dynamic categories when this is the case.
if settings.SHOP_USE_VARIATIONS:
    category_fieldsets[-1][1]["fields"] = (("options",) +
                                        category_fieldsets[-1][1]["fields"])


class CategoryAdmin(PageAdmin):
    fieldsets = category_fieldsets
    formfield_overrides = {ImageField: {"widget": ImageWidget}}
    filter_horizontal = ("options", "products",)

################
#  VARIATIONS  #
################

# If variations aren't used, the variation inline should always
# provide a single inline for managing the single variation per
# product.
variation_fields = ["sku", "num_in_stock", "unit_price",
                    "sale_price", "sale_from", "sale_to", "image"]
if settings.SHOP_USE_VARIATIONS:
    variation_fields.insert(1, "default")
    variations_max_num = None
    variations_extra = 0
else:
    variations_max_num = 1
    variations_extra = 1


class ProductVariationAdmin(admin.TabularInline):
    verbose_name_plural = _("Current variations")
    model = ProductVariation
    fields = variation_fields
    max_num = variations_max_num
    extra = variations_extra
    formfield_overrides = {MoneyField: {"widget": MoneyWidget}}
    form = ProductVariationAdminForm
    formset = ProductVariationAdminFormset
    ordering = ["option%s" % i for i in settings.SHOP_OPTION_ADMIN_ORDER]


class ProductImageAdmin(TabularDynamicInlineAdmin):
    model = ProductImage
    formfield_overrides = {ImageField: {"widget": ImageWidget}}

##############
#  PRODUCTS  #
##############

product_fieldsets = deepcopy(DisplayableAdmin.fieldsets)
product_fieldsets[0][1]["fields"].insert(2, "available")
product_fieldsets[0][1]["fields"].extend(["content", "categories"])
product_fieldsets = list(product_fieldsets)

other_product_fields = []
if settings.SHOP_USE_RELATED_PRODUCTS:
    other_product_fields.append("related_products")
if settings.SHOP_USE_UPSELL_PRODUCTS:
    other_product_fields.append("upsell_products")
if len(other_product_fields) > 0:
    product_fieldsets.append((_("Other products"), {
        "classes": ("collapse-closed",),
        "fields": tuple(other_product_fields)}))

product_list_display = ["admin_thumb", "title", "status", "available",
                        "admin_link"]
product_list_editable = ["status", "available"]

# If variations are used, set up the product option fields for managing
# variations. If not, expose the denormalised price fields for a product
# in the change list view.
if settings.SHOP_USE_VARIATIONS:
    product_fieldsets.insert(1, (_("Create new variations"),
        {"classes": ("create-variations",), "fields": option_fields}))
else:
    extra_list_fields = ["sku", "unit_price", "sale_price", "num_in_stock"]
    product_list_display[4:4] = extra_list_fields
    product_list_editable.extend(extra_list_fields)


class ProductResource(resources.ModelResource):

    def _product_from_row(self, row):
        product, created = Product.objects.get_or_create(title=row[TITLE])
        product.content = row[CONTENT]
        product.description = row[DESCRIPTION]
        # TODO: set the 2 below from spreadsheet.
        product.status = CONTENT_STATUS_PUBLISHED
        product.available = True
        # TODO: allow arbitrary level/number of categories.
        print("sub_cat=%s" % row[SUB_CATEGORY])
        base_cat, created = Category.objects.get_or_create(title=row[CATEGORY])
        if row[SUB_CATEGORY]:
            sub_cat, created = Category.objects.get_or_create(
                title=row[SUB_CATEGORY],
                parent=base_cat)
            product.categories.add(sub_cat)
        shop_cat, created = Category.objects.get_or_create(title="Shop")
        product.categories.add(shop_cat)
        return product

    def _make_image(self, image_str, product):
        if image_str in EMPTY_IMAGE_ENTRIES:
            return None
        # try adding various image suffixes, if none given in original filename.
        root, suffix = os.path.splitext(image_str)
        if suffix not in IMAGE_SUFFIXES:
            raise CommandError("INCORRECT SUFFIX: %s" % image_str)
        image_path = os.path.join(LOCAL_IMAGE_DIR, image_str)
        if not os.path.exists(image_path):
            raise CommandError("NO FILE %s" % image_path)
        else:
            print("local orig image_path= %s PRODUCT_IMAGE_DIR= %s" % (image_path, PRODUCT_IMAGE_DIR))
        shutil.copy(image_path, PRODUCT_IMAGE_DIR)
        # shutil.copy(image_path, os.path.join(PRODUCT_IMAGE_DIR, "orig"))
        print("SITE_MEDIA_IMAGE_DIR=%s image_str=%s" % (SITE_MEDIA_IMAGE_DIR, image_str))
        image, created = ProductImage.objects.get_or_create(
            file="%s" % (image_str),
            description=image_str,  # TODO: handle column for this.
            product=product)
        return image


    def _make_date(self, date_str, time_str):
        date_string = '%s %s' % (date_str, time_str)
        date = datetime.datetime.strptime(date_string, DATETIME_FORMAT)
        return date


    def export(self, queryset=None):
        print(_("Exporting .."))
        data = tablib.Dataset()
        count = 0
        for pv in ProductVariation.objects.all():
            row = dict()
            row[TITLE] = pv.product.title
            row[CONTENT] = pv.product.content
            row[DESCRIPTION] = pv.product.description
            row[SKU] = pv.sku
            row[IMAGE] = pv.image
            # TODO: handle multiple categories, and multiple levels of categories
            cat = pv.product.categories.all()[0]
            if cat.parent:
                row[SUB_CATEGORY] = cat.title
                row[CATEGORY] = cat.parent.title
            else:
                row[CATEGORY] = cat.title
                row[SUB_CATEGORY] = ""

            for option in TYPE_CHOICES:
                row[option] = getattr(pv, "option%s" % TYPE_CHOICES[option])

            row[NUM_IN_STOCK] = pv.num_in_stock
            row[UNIT_PRICE] = pv.unit_price
            row[SALE_PRICE] = pv.sale_price
            try:
                row[SALE_START_DATE] = pv.sale_from.strftime(DATE_FORMAT)
                row[SALE_START_TIME] = pv.sale_from.strftime(TIME_FORMAT)
            except AttributeError:
                pass
            try:
                row[SALE_END_DATE] = pv.sale_to.strftime(DATE_FORMAT)
                row[SALE_END_TIME] = pv.sale_to.strftime(TIME_FORMAT)
            except AttributeError:
                pass
            try:
                row[SALE_END_DATE] = pv.sale_to.strftime(DATE_FORMAT)
                row[SALE_END_TIME] = pv.sale_to.strftime(TIME_FORMAT)
            except AttributeError:
                pass
            if count == 0:
                list_keys = [ k for k in row ]
                data.headers = list_keys
                count=1

            list_values = [ v for v in row.values() ]
            print(list_values)
            data.append(list_values)
        return data

    def import_data(self, dataset, dry_run=False, raise_errors=False,
                    use_transactions=None, **kwargs):
        filehandle = open(csv_file, 'w')
        filehandle.write(dataset.csv)
        filehandle.close()
        result = self.get_result_class()()
        result.diff_headers = self.get_diff_headers()
        result.totals = OrderedDict([(RowResult.IMPORT_TYPE_NEW, 0),
                                        (RowResult.IMPORT_TYPE_UPDATE, 0),
                                        (RowResult.IMPORT_TYPE_DELETE, 0),
                                        (RowResult.IMPORT_TYPE_SKIP, 0),
                                        (RowResult.IMPORT_TYPE_ERROR, 0),
                                        ('total', len(dataset))])
        result.totals['total'] = len(dataset)

        reader = csv.DictReader(open(csv_file))
        for row in reader:
            print(row)
            product = self._product_from_row(row)
            try:
                variation = ProductVariation.objects.create(
                    # strip whitespace
                    sku=row[SKU].replace(" ", ""),
                    product=product,
                )
            except IntegrityError:
                raise CommandError("Product with SKU exists! sku: %s" % row[SKU])
            if row[NUM_IN_STOCK]:
                variation.num_in_stock = row[NUM_IN_STOCK]
            if row[UNIT_PRICE]:
                variation.unit_price = row[UNIT_PRICE]
            if row[SALE_PRICE]:
                variation.sale_price = row[SALE_PRICE]
            #if row[SALE_START_DATE] and row[SALE_START_TIME]:
                #variation.sale_from = self._make_date(row[SALE_START_DATE],
                #                                    row[SALE_START_TIME])
            #if row[SALE_END_DATE] and row[SALE_END_TIME]:
                #variation.sale_to = self._make_date(row[SALE_END_DATE],
                #                                   row[SALE_END_TIME])
            for option in TYPE_CHOICES:
                if row[option]:
                    name = "option%s" % TYPE_CHOICES[option]
                    setattr(variation, name, row[option])
                    new_option, created = ProductOption.objects.get_or_create(
                        type=TYPE_CHOICES[option],  # TODO: set dynamically
                        name=row[option])
            variation.save()
            image = self._make_image(row[IMAGE], product)
            if image:
                variation.image = image
            product.variations.manage_empty()
            product.variations.set_default_images([])
            product.copy_default_variation()
            product.save()
        return result
    class Meta:
        model = Product


class ProductAdmin(ImportExportModelAdmin, DisplayableAdmin):

    class Media:
        js = (static("cartridge/js/admin/product_variations.js"),)
        css = {"all": (static("cartridge/css/admin/product.css"),)}

    resource_class = ProductResource
    list_display = product_list_display
    list_display_links = ("admin_thumb", "title")
    list_editable = product_list_editable
    list_filter = ("status", "available", "categories")
    filter_horizontal = ("categories",) + tuple(other_product_fields)
    search_fields = ("title", "content", "categories__title",
                     "variations__sku")
    inlines = (ProductImageAdmin, ProductVariationAdmin)
    form = ProductAdminForm
    fieldsets = product_fieldsets

    def save_model(self, request, obj, form, change):
        """
        Store the product object for creating variations in save_formset.
        """
        super(ProductAdmin, self).save_model(request, obj, form, change)
        self._product = obj

    def save_formset(self, request, form, formset, change):
        """

        Here be dragons. We want to perform these steps sequentially:

        - Save variations formset
        - Run the required variation manager methods:
          (create_from_options, manage_empty, etc)
        - Save the images formset

        The variations formset needs to be saved first for the manager
        methods to have access to the correct variations. The images
        formset needs to be run last, because if images are deleted
        that are selected for variations, the variations formset will
        raise errors when saving due to invalid image selections. This
        gets addressed in the set_default_images method.

        An additional problem is the actual ordering of the inlines,
        which are in the reverse order for achieving the above. To
        address this, we store the images formset as an attribute, and
        then call save on it after the other required steps have
        occurred.

        """

        # Store the images formset for later saving, otherwise save the
        # formset.
        if formset.model == ProductImage:
            self._images_formset = formset
        else:
            super(ProductAdmin, self).save_formset(request, form, formset,
                                                   change)

        # Run each of the variation manager methods if we're saving
        # the variations formset.
        if formset.model == ProductVariation:

            # Build up selected options for new variations.
            options = dict([(f, request.POST.getlist(f)) for f in option_fields
                             if request.POST.getlist(f)])
            # Create a list of image IDs that have been marked to delete.
            deleted_images = [request.POST.get(f.replace("-DELETE", "-id"))
                for f in request.POST
                if f.startswith("images-") and f.endswith("-DELETE")]

            # Create new variations for selected options.
            self._product.variations.create_from_options(options)
            # Create a default variation if there are none.
            self._product.variations.manage_empty()

            # Remove any images deleted just now from variations they're
            # assigned to, and set an image for any variations without one.
            self._product.variations.set_default_images(deleted_images)

            # Save the images formset stored previously.
            super(ProductAdmin, self).save_formset(request, form,
                                                 self._images_formset, change)

            # Run again to allow for no images existing previously, with
            # new images added which can be used as defaults for variations.
            self._product.variations.set_default_images(deleted_images)

            # Copy duplicate fields (``Priced`` fields) from the default
            # variation to the product.
            self._product.copy_default_variation()

            # Save every translated fields from ``ProductOption`` into
            # the required ``ProductVariation``
            if settings.USE_MODELTRANSLATION:
                from collections import OrderedDict
                from modeltranslation.utils import (build_localized_fieldname
                                                    as _loc)
                for opt_name in options:
                    for opt_value in options[opt_name]:
                        opt_obj = ProductOption.objects.get(type=opt_name[6:],
                                                            name=opt_value)
                        params = {opt_name: opt_value}
                        for var in self._product.variations.filter(**params):
                            for code in OrderedDict(settings.LANGUAGES):
                                setattr(var, _loc(opt_name, code),
                                        getattr(opt_obj, _loc('name', code)))
                            var.save()


class ProductOptionAdmin(BaseTranslationModelAdmin):
    ordering = ("type", "name")
    list_display = ("type", "name")
    list_display_links = ("type",)
    list_editable = ("name",)
    list_filter = ("type",)
    search_fields = ("type", "name")
    radio_fields = {"type": admin.HORIZONTAL}


class OrderItemInline(admin.TabularInline):
    verbose_name_plural = _("Items")
    model = OrderItem
    extra = 0
    formfield_overrides = {MoneyField: {"widget": MoneyWidget}}


def address_pairs(fields):
    """
    Zips address fields into pairs, appending the last field if the
    total is an odd number.
    """
    pairs = list(zip(fields[::2], fields[1::2]))
    if len(fields) % 2:
        pairs.append(fields[-1])
    return pairs


order_list_display = ("id", "billing_name", "total", "time", "status",
                      "transaction_id")
if HAS_PDF:
    order_list_display += ("invoice",)


class OrderAdmin(admin.ModelAdmin):

    class Media:
        css = {"all": (static("cartridge/css/admin/order.css"),)}

    ordering = ("status", "-id")
    list_display = order_list_display
    list_editable = ("status",)
    list_filter = ("status", "time")
    list_display_links = ("id", "billing_name",)
    search_fields = (["id", "status", "transaction_id"] +
                     billing_fields + shipping_fields)
    date_hierarchy = "time"
    radio_fields = {"status": admin.HORIZONTAL}
    inlines = (OrderItemInline,)
    formfield_overrides = {MoneyField: {"widget": MoneyWidget}}
    fieldsets = (
        (_("Billing details"), {"fields": address_pairs(billing_fields)}),
         (_("Shipping details"), {"fields": address_pairs(shipping_fields)}),
        (None, {"fields": ("additional_instructions", ("shipping_total",
            "shipping_type"), ('tax_total', 'tax_type'),
             ("discount_total", "discount_code"), "item_total",
            ("total", "status"), "transaction_id")}),
    )

    def change_view(self, *args, **kwargs):
        if kwargs.get("extra_context", None) is None:
            kwargs["extra_context"] = {}
        kwargs["extra_context"]["has_pdf"] = HAS_PDF
        return super(OrderAdmin, self).change_view(*args, **kwargs)


class SaleAdmin(admin.ModelAdmin):
    list_display = ("title", "active", "discount_deduct", "discount_percent",
        "discount_exact", "valid_from", "valid_to")
    list_editable = ("active", "discount_deduct", "discount_percent",
        "discount_exact", "valid_from", "valid_to")
    filter_horizontal = ("categories", "products")
    formfield_overrides = {MoneyField: {"widget": MoneyWidget}}
    form = DiscountAdminForm
    fieldsets = (
        (None, {"fields": ("title", "active")}),
        (_("Apply to product and/or products in categories"),
            {"fields": ("products", "categories")}),
        (_("Reduce unit price by"),
            {"fields": (("discount_deduct", "discount_percent",
            "discount_exact"),)}),
        (_("Sale period"), {"fields": (("valid_from", "valid_to"),)}),
    )


class DiscountCodeAdmin(admin.ModelAdmin):
    list_display = ("title", "active", "code", "discount_deduct",
        "discount_percent", "min_purchase", "free_shipping", "valid_from",
        "valid_to")
    list_editable = ("active", "code", "discount_deduct", "discount_percent",
        "min_purchase", "free_shipping", "valid_from", "valid_to")
    filter_horizontal = ("categories", "products")
    formfield_overrides = {MoneyField: {"widget": MoneyWidget}}
    form = DiscountAdminForm
    fieldsets = (
        (None, {"fields": ("title", "active", "code")}),
        (_("Apply to product and/or products in categories"),
            {"fields": ("products", "categories")}),
        (_("Reduce unit price by"),
            {"fields": (("discount_deduct", "discount_percent"),)}),
        (None, {"fields": (("min_purchase", "free_shipping"),)}),
        (_("Valid for"),
            {"fields": (("valid_from", "valid_to", "uses_remaining"),)}),
    )


admin.site.register(Category, CategoryAdmin)
admin.site.register(Product, ProductAdmin)
if settings.SHOP_USE_VARIATIONS:
    admin.site.register(ProductOption, ProductOptionAdmin)
admin.site.register(Order, OrderAdmin)
admin.site.register(Sale, SaleAdmin)
admin.site.register(DiscountCode, DiscountCodeAdmin)

Reply via email to