We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
are product class labels switched?
Hello,
I'm looking at the new product class feature.

About the labels ... should not be the other way around?
https://github.com/mirumee/saleor/blob/master/saleor/dashboard/product/forms.py#L53-L59

![schermata del 2016-12-29 17-05-55](https://cloud.githubusercontent.com/assets/650691/21547927/6e6023b4-cde9-11e6-92a3-b1b16fa41ab1.png)

![schermata del 2016-12-29 17-06-31](https://cloud.githubusercontent.com/assets/650691/21547913/592f5636-cde9-11e6-83b3-40160b51b2b5.png)





--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `saleor/dashboard/product/forms.py`
Content:
```
1 from __future__ import unicode_literals
2 
3 from django import forms
4 from django.db import transaction
5 from django.db.models import Count
6 from django.forms.models import ModelChoiceIterator, inlineformset_factory
7 from django.utils.encoding import smart_text
8 from django.utils.translation import pgettext_lazy
9 
10 from ...product.models import (AttributeChoiceValue, Product, ProductAttribute,
11                                ProductClass, ProductImage, ProductVariant,
12                                Stock, StockLocation, VariantImage)
13 from .widgets import ImagePreviewWidget
14 from ...search import index as search_index
15 
16 
17 class ProductClassSelectorForm(forms.Form):
18     MAX_RADIO_SELECT_ITEMS = 5
19 
20     def __init__(self, *args, **kwargs):
21         product_classes = kwargs.pop('product_classes', [])
22         super(ProductClassSelectorForm, self).__init__(*args, **kwargs)
23         choices = [(obj.pk, obj.name) for obj in product_classes]
24         if len(product_classes) > self.MAX_RADIO_SELECT_ITEMS:
25             widget = forms.Select
26         else:
27             widget = forms.RadioSelect
28         self.fields['product_cls'] = forms.ChoiceField(
29             label=pgettext_lazy('Product class form label', 'Product type'),
30             choices=choices, widget=widget)
31 
32 
33 class StockForm(forms.ModelForm):
34     class Meta:
35         model = Stock
36         exclude = ['quantity_allocated']
37 
38     def __init__(self, *args, **kwargs):
39         product = kwargs.pop('product')
40         super(StockForm, self).__init__(*args, **kwargs)
41         if not product.product_class.has_variants:
42             initial = product.variants.first()
43         else:
44             initial = None
45         self.fields['variant'] = forms.ModelChoiceField(
46             queryset=product.variants, initial=initial)
47 
48 
49 class ProductClassForm(forms.ModelForm):
50     class Meta:
51         model = ProductClass
52         exclude = []
53         labels = {
54             'product_attributes': pgettext_lazy(
55                 'Product class form label',
56                 'Attributes specific to each variant'),
57             'variant_attributes': pgettext_lazy(
58                 'Product class form label',
59                 'Attributes common to all variants')}
60 
61     def clean(self):
62         data = super(ProductClassForm, self).clean()
63         has_variants = self.cleaned_data['has_variants']
64         product_attr = set(self.cleaned_data['product_attributes'])
65         variant_attr = set(self.cleaned_data['variant_attributes'])
66         if not has_variants and len(variant_attr) > 0:
67             msg = pgettext_lazy(
68                 'Product Class Errors',
69                 'Product variants are disabled.')
70             self.add_error('variant_attributes', msg)
71         if len(product_attr & variant_attr) > 0:
72             msg = pgettext_lazy(
73                 'Product Class Errors',
74                 'A single attribute can\'t belong to both a product '
75                 'and its variant.')
76             self.add_error('variant_attributes', msg)
77 
78         if self.instance.pk:
79             variants_changed = not (self.fields['has_variants'].initial ==
80                                     has_variants)
81             if variants_changed:
82                 query = self.instance.products.all()
83                 query = query.annotate(variants_counter=Count('variants'))
84                 query = query.filter(variants_counter__gt=1)
85                 if query.exists():
86                     msg = pgettext_lazy(
87                         'Product Class Errors',
88                         'Some products of this type have more than '
89                         'one variant.')
90                     self.add_error('has_variants', msg)
91         return data
92 
93 
94 class ProductForm(forms.ModelForm):
95 
96     class Meta:
97         model = Product
98         exclude = ['attributes', 'product_class']
99 
100     def __init__(self, *args, **kwargs):
101         self.product_attributes = []
102         super(ProductForm, self).__init__(*args, **kwargs)
103         field = self.fields['name']
104         field.widget.attrs['placeholder'] = pgettext_lazy(
105             'Product form labels', 'Give your awesome product a name')
106         field = self.fields['categories']
107         field.widget.attrs['data-placeholder'] = pgettext_lazy(
108             'Product form labels', 'Search')
109         product_class = self.instance.product_class
110         self.product_attributes = product_class.product_attributes.all()
111         self.product_attributes = self.product_attributes.prefetch_related(
112             'values')
113         self.prepare_fields_for_attributes()
114 
115     def prepare_fields_for_attributes(self):
116         for attribute in self.product_attributes:
117             field_defaults = {
118                 'label': attribute.display,
119                 'required': False,
120                 'initial': self.instance.get_attribute(attribute.pk)}
121             if attribute.has_values():
122                 field = CachingModelChoiceField(
123                     queryset=attribute.values.all(), **field_defaults)
124             else:
125                 field = forms.CharField(**field_defaults)
126             self.fields[attribute.get_formfield_name()] = field
127 
128     def iter_attribute_fields(self):
129         for attr in self.product_attributes:
130             yield self[attr.get_formfield_name()]
131 
132     def save(self, commit=True):
133         attributes = {}
134         for attr in self.product_attributes:
135             value = self.cleaned_data.pop(attr.get_formfield_name())
136             if isinstance(value, AttributeChoiceValue):
137                 attributes[smart_text(attr.pk)] = smart_text(value.pk)
138             else:
139                 attributes[smart_text(attr.pk)] = value
140         self.instance.attributes = attributes
141         instance = super(ProductForm, self).save(commit=commit)
142         search_index.insert_or_update_object(instance)
143         return instance
144 
145 
146 class ProductVariantForm(forms.ModelForm):
147     class Meta:
148         model = ProductVariant
149         exclude = ['attributes', 'product', 'images']
150 
151     def __init__(self, *args, **kwargs):
152         super(ProductVariantForm, self).__init__(*args, **kwargs)
153         if self.instance.product.pk:
154             self.fields['price_override'].widget.attrs[
155                 'placeholder'] = self.instance.product.price.gross
156             self.fields['weight_override'].widget.attrs[
157                 'placeholder'] = self.instance.product.weight
158 
159 
160 class CachingModelChoiceIterator(ModelChoiceIterator):
161     def __iter__(self):
162         if self.field.empty_label is not None:
163             yield ('', self.field.empty_label)
164         for obj in self.queryset:
165             yield self.choice(obj)
166 
167 
168 class CachingModelChoiceField(forms.ModelChoiceField):
169     def _get_choices(self):
170         if hasattr(self, '_choices'):
171             return self._choices
172         return CachingModelChoiceIterator(self)
173     choices = property(_get_choices, forms.ChoiceField._set_choices)
174 
175 
176 class VariantAttributeForm(forms.ModelForm):
177     class Meta:
178         model = ProductVariant
179         fields = []
180 
181     def __init__(self, *args, **kwargs):
182         super(VariantAttributeForm, self).__init__(*args, **kwargs)
183         attrs = self.instance.product.product_class.variant_attributes.all()
184         self.available_attrs = attrs.prefetch_related('values')
185         for attr in self.available_attrs:
186             field_defaults = {'label': attr.display,
187                               'required': True,
188                               'initial': self.instance.get_attribute(attr.pk)}
189             if attr.has_values():
190                 field = CachingModelChoiceField(
191                     queryset=attr.values.all(), **field_defaults)
192             else:
193                 field = forms.CharField(**field_defaults)
194             self.fields[attr.get_formfield_name()] = field
195 
196     def save(self, commit=True):
197         attributes = {}
198         for attr in self.available_attrs:
199             value = self.cleaned_data.pop(attr.get_formfield_name())
200             if isinstance(value, AttributeChoiceValue):
201                 attributes[smart_text(attr.pk)] = smart_text(value.pk)
202             else:
203                 attributes[smart_text(attr.pk)] = value
204         self.instance.attributes = attributes
205         return super(VariantAttributeForm, self).save(commit=commit)
206 
207 
208 class VariantBulkDeleteForm(forms.Form):
209     items = forms.ModelMultipleChoiceField(queryset=ProductVariant.objects)
210 
211     def delete(self):
212         items = ProductVariant.objects.filter(
213             pk__in=self.cleaned_data['items'])
214         items.delete()
215 
216 
217 class StockBulkDeleteForm(forms.Form):
218     items = forms.ModelMultipleChoiceField(queryset=Stock.objects)
219 
220     def delete(self):
221         items = Stock.objects.filter(pk__in=self.cleaned_data['items'])
222         items.delete()
223 
224 
225 class ProductImageForm(forms.ModelForm):
226     variants = forms.ModelMultipleChoiceField(
227         queryset=ProductVariant.objects.none(),
228         widget=forms.CheckboxSelectMultiple, required=False)
229 
230     class Meta:
231         model = ProductImage
232         exclude = ('product', 'order')
233 
234     def __init__(self, *args, **kwargs):
235         super(ProductImageForm, self).__init__(*args, **kwargs)
236         show_variants = self.instance.product.product_class.has_variants
237         if self.instance.product and show_variants:
238             variants = self.fields['variants']
239             variants.queryset = self.instance.product.variants.all()
240             variants.initial = self.instance.variant_images.values_list(
241                 'variant', flat=True)
242         if self.instance.image:
243             self.fields['image'].widget = ImagePreviewWidget()
244 
245     @transaction.atomic
246     def save_variant_images(self, instance):
247         variant_images = []
248         # Clean up old mapping
249         instance.variant_images.all().delete()
250         for variant in self.cleaned_data['variants']:
251             variant_images.append(
252                 VariantImage(variant=variant, image=instance))
253         VariantImage.objects.bulk_create(variant_images)
254 
255     def save(self, commit=True):
256         instance = super(ProductImageForm, self).save(commit=commit)
257         self.save_variant_images(instance)
258         return instance
259 
260 
261 class ProductAttributeForm(forms.ModelForm):
262     class Meta:
263         model = ProductAttribute
264         exclude = []
265 
266 
267 class StockLocationForm(forms.ModelForm):
268     class Meta:
269         model = StockLocation
270         exclude = []
271 
272 
273 AttributeChoiceValueFormset = inlineformset_factory(
274     ProductAttribute, AttributeChoiceValue, exclude=(), extra=1)
275
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/saleor/dashboard/product/forms.py b/saleor/dashboard/product/forms.py
--- a/saleor/dashboard/product/forms.py
+++ b/saleor/dashboard/product/forms.py
@@ -51,10 +51,10 @@
         model = ProductClass
         exclude = []
         labels = {
-            'product_attributes': pgettext_lazy(
+            'variant_attributes': pgettext_lazy(
                 'Product class form label',
                 'Attributes specific to each variant'),
-            'variant_attributes': pgettext_lazy(
+            'product_attributes': pgettext_lazy(
                 'Product class form label',
                 'Attributes common to all variants')}

{"golden_diff": "diff --git a/saleor/dashboard/product/forms.py b/saleor/dashboard/product/forms.py\n--- a/saleor/dashboard/product/forms.py\n+++ b/saleor/dashboard/product/forms.py\n@@ -51,10 +51,10 @@\n         model = ProductClass\n         exclude = []\n         labels = {\n-            'product_attributes': pgettext_lazy(\n+            'variant_attributes': pgettext_lazy(\n                 'Product class form label',\n                 'Attributes specific to each variant'),\n-            'variant_attributes': pgettext_lazy(\n+            'product_attributes': pgettext_lazy(\n                 'Product class form label',\n                 'Attributes common to all variants')}\n", "issue": "are product class labels switched?\nHello,\r\nI'm looking at the new product class feature.\r\n\r\nAbout the labels ... should not be the other way around?\r\nhttps://github.com/mirumee/saleor/blob/master/saleor/dashboard/product/forms.py#L53-L59\r\n\r\n![schermata del 2016-12-29 17-05-55](https://cloud.githubusercontent.com/assets/650691/21547927/6e6023b4-cde9-11e6-92a3-b1b16fa41ab1.png)\r\n\r\n![schermata del 2016-12-29 17-06-31](https://cloud.githubusercontent.com/assets/650691/21547913/592f5636-cde9-11e6-83b3-40160b51b2b5.png)\r\n\r\n\r\n\r\n\n", "before_files": [{"content": "from __future__ import unicode_literals\n\nfrom django import forms\nfrom django.db import transaction\nfrom django.db.models import Count\nfrom django.forms.models import ModelChoiceIterator, inlineformset_factory\nfrom django.utils.encoding import smart_text\nfrom django.utils.translation import pgettext_lazy\n\nfrom ...product.models import (AttributeChoiceValue, Product, ProductAttribute,\n                               ProductClass, ProductImage, ProductVariant,\n                               Stock, StockLocation, VariantImage)\nfrom .widgets import ImagePreviewWidget\nfrom ...search import index as search_index\n\n\nclass ProductClassSelectorForm(forms.Form):\n    MAX_RADIO_SELECT_ITEMS = 5\n\n    def __init__(self, *args, **kwargs):\n        product_classes = kwargs.pop('product_classes', [])\n        super(ProductClassSelectorForm, self).__init__(*args, **kwargs)\n        choices = [(obj.pk, obj.name) for obj in product_classes]\n        if len(product_classes) > self.MAX_RADIO_SELECT_ITEMS:\n            widget = forms.Select\n        else:\n            widget = forms.RadioSelect\n        self.fields['product_cls'] = forms.ChoiceField(\n            label=pgettext_lazy('Product class form label', 'Product type'),\n            choices=choices, widget=widget)\n\n\nclass StockForm(forms.ModelForm):\n    class Meta:\n        model = Stock\n        exclude = ['quantity_allocated']\n\n    def __init__(self, *args, **kwargs):\n        product = kwargs.pop('product')\n        super(StockForm, self).__init__(*args, **kwargs)\n        if not product.product_class.has_variants:\n            initial = product.variants.first()\n        else:\n            initial = None\n        self.fields['variant'] = forms.ModelChoiceField(\n            queryset=product.variants, initial=initial)\n\n\nclass ProductClassForm(forms.ModelForm):\n    class Meta:\n        model = ProductClass\n        exclude = []\n        labels = {\n            'product_attributes': pgettext_lazy(\n                'Product class form label',\n                'Attributes specific to each variant'),\n            'variant_attributes': pgettext_lazy(\n                'Product class form label',\n                'Attributes common to all variants')}\n\n    def clean(self):\n        data = super(ProductClassForm, self).clean()\n        has_variants = self.cleaned_data['has_variants']\n        product_attr = set(self.cleaned_data['product_attributes'])\n        variant_attr = set(self.cleaned_data['variant_attributes'])\n        if not has_variants and len(variant_attr) > 0:\n            msg = pgettext_lazy(\n                'Product Class Errors',\n                'Product variants are disabled.')\n            self.add_error('variant_attributes', msg)\n        if len(product_attr & variant_attr) > 0:\n            msg = pgettext_lazy(\n                'Product Class Errors',\n                'A single attribute can\\'t belong to both a product '\n                'and its variant.')\n            self.add_error('variant_attributes', msg)\n\n        if self.instance.pk:\n            variants_changed = not (self.fields['has_variants'].initial ==\n                                    has_variants)\n            if variants_changed:\n                query = self.instance.products.all()\n                query = query.annotate(variants_counter=Count('variants'))\n                query = query.filter(variants_counter__gt=1)\n                if query.exists():\n                    msg = pgettext_lazy(\n                        'Product Class Errors',\n                        'Some products of this type have more than '\n                        'one variant.')\n                    self.add_error('has_variants', msg)\n        return data\n\n\nclass ProductForm(forms.ModelForm):\n\n    class Meta:\n        model = Product\n        exclude = ['attributes', 'product_class']\n\n    def __init__(self, *args, **kwargs):\n        self.product_attributes = []\n        super(ProductForm, self).__init__(*args, **kwargs)\n        field = self.fields['name']\n        field.widget.attrs['placeholder'] = pgettext_lazy(\n            'Product form labels', 'Give your awesome product a name')\n        field = self.fields['categories']\n        field.widget.attrs['data-placeholder'] = pgettext_lazy(\n            'Product form labels', 'Search')\n        product_class = self.instance.product_class\n        self.product_attributes = product_class.product_attributes.all()\n        self.product_attributes = self.product_attributes.prefetch_related(\n            'values')\n        self.prepare_fields_for_attributes()\n\n    def prepare_fields_for_attributes(self):\n        for attribute in self.product_attributes:\n            field_defaults = {\n                'label': attribute.display,\n                'required': False,\n                'initial': self.instance.get_attribute(attribute.pk)}\n            if attribute.has_values():\n                field = CachingModelChoiceField(\n                    queryset=attribute.values.all(), **field_defaults)\n            else:\n                field = forms.CharField(**field_defaults)\n            self.fields[attribute.get_formfield_name()] = field\n\n    def iter_attribute_fields(self):\n        for attr in self.product_attributes:\n            yield self[attr.get_formfield_name()]\n\n    def save(self, commit=True):\n        attributes = {}\n        for attr in self.product_attributes:\n            value = self.cleaned_data.pop(attr.get_formfield_name())\n            if isinstance(value, AttributeChoiceValue):\n                attributes[smart_text(attr.pk)] = smart_text(value.pk)\n            else:\n                attributes[smart_text(attr.pk)] = value\n        self.instance.attributes = attributes\n        instance = super(ProductForm, self).save(commit=commit)\n        search_index.insert_or_update_object(instance)\n        return instance\n\n\nclass ProductVariantForm(forms.ModelForm):\n    class Meta:\n        model = ProductVariant\n        exclude = ['attributes', 'product', 'images']\n\n    def __init__(self, *args, **kwargs):\n        super(ProductVariantForm, self).__init__(*args, **kwargs)\n        if self.instance.product.pk:\n            self.fields['price_override'].widget.attrs[\n                'placeholder'] = self.instance.product.price.gross\n            self.fields['weight_override'].widget.attrs[\n                'placeholder'] = self.instance.product.weight\n\n\nclass CachingModelChoiceIterator(ModelChoiceIterator):\n    def __iter__(self):\n        if self.field.empty_label is not None:\n            yield ('', self.field.empty_label)\n        for obj in self.queryset:\n            yield self.choice(obj)\n\n\nclass CachingModelChoiceField(forms.ModelChoiceField):\n    def _get_choices(self):\n        if hasattr(self, '_choices'):\n            return self._choices\n        return CachingModelChoiceIterator(self)\n    choices = property(_get_choices, forms.ChoiceField._set_choices)\n\n\nclass VariantAttributeForm(forms.ModelForm):\n    class Meta:\n        model = ProductVariant\n        fields = []\n\n    def __init__(self, *args, **kwargs):\n        super(VariantAttributeForm, self).__init__(*args, **kwargs)\n        attrs = self.instance.product.product_class.variant_attributes.all()\n        self.available_attrs = attrs.prefetch_related('values')\n        for attr in self.available_attrs:\n            field_defaults = {'label': attr.display,\n                              'required': True,\n                              'initial': self.instance.get_attribute(attr.pk)}\n            if attr.has_values():\n                field = CachingModelChoiceField(\n                    queryset=attr.values.all(), **field_defaults)\n            else:\n                field = forms.CharField(**field_defaults)\n            self.fields[attr.get_formfield_name()] = field\n\n    def save(self, commit=True):\n        attributes = {}\n        for attr in self.available_attrs:\n            value = self.cleaned_data.pop(attr.get_formfield_name())\n            if isinstance(value, AttributeChoiceValue):\n                attributes[smart_text(attr.pk)] = smart_text(value.pk)\n            else:\n                attributes[smart_text(attr.pk)] = value\n        self.instance.attributes = attributes\n        return super(VariantAttributeForm, self).save(commit=commit)\n\n\nclass VariantBulkDeleteForm(forms.Form):\n    items = forms.ModelMultipleChoiceField(queryset=ProductVariant.objects)\n\n    def delete(self):\n        items = ProductVariant.objects.filter(\n            pk__in=self.cleaned_data['items'])\n        items.delete()\n\n\nclass StockBulkDeleteForm(forms.Form):\n    items = forms.ModelMultipleChoiceField(queryset=Stock.objects)\n\n    def delete(self):\n        items = Stock.objects.filter(pk__in=self.cleaned_data['items'])\n        items.delete()\n\n\nclass ProductImageForm(forms.ModelForm):\n    variants = forms.ModelMultipleChoiceField(\n        queryset=ProductVariant.objects.none(),\n        widget=forms.CheckboxSelectMultiple, required=False)\n\n    class Meta:\n        model = ProductImage\n        exclude = ('product', 'order')\n\n    def __init__(self, *args, **kwargs):\n        super(ProductImageForm, self).__init__(*args, **kwargs)\n        show_variants = self.instance.product.product_class.has_variants\n        if self.instance.product and show_variants:\n            variants = self.fields['variants']\n            variants.queryset = self.instance.product.variants.all()\n            variants.initial = self.instance.variant_images.values_list(\n                'variant', flat=True)\n        if self.instance.image:\n            self.fields['image'].widget = ImagePreviewWidget()\n\n    @transaction.atomic\n    def save_variant_images(self, instance):\n        variant_images = []\n        # Clean up old mapping\n        instance.variant_images.all().delete()\n        for variant in self.cleaned_data['variants']:\n            variant_images.append(\n                VariantImage(variant=variant, image=instance))\n        VariantImage.objects.bulk_create(variant_images)\n\n    def save(self, commit=True):\n        instance = super(ProductImageForm, self).save(commit=commit)\n        self.save_variant_images(instance)\n        return instance\n\n\nclass ProductAttributeForm(forms.ModelForm):\n    class Meta:\n        model = ProductAttribute\n        exclude = []\n\n\nclass StockLocationForm(forms.ModelForm):\n    class Meta:\n        model = StockLocation\n        exclude = []\n\n\nAttributeChoiceValueFormset = inlineformset_factory(\n    ProductAttribute, AttributeChoiceValue, exclude=(), extra=1)\n", "path": "saleor/dashboard/product/forms.py"}], "after_files": [{"content": "from __future__ import unicode_literals\n\nfrom django import forms\nfrom django.db import transaction\nfrom django.db.models import Count\nfrom django.forms.models import ModelChoiceIterator, inlineformset_factory\nfrom django.utils.encoding import smart_text\nfrom django.utils.translation import pgettext_lazy\n\nfrom ...product.models import (AttributeChoiceValue, Product, ProductAttribute,\n                               ProductClass, ProductImage, ProductVariant,\n                               Stock, StockLocation, VariantImage)\nfrom .widgets import ImagePreviewWidget\nfrom ...search import index as search_index\n\n\nclass ProductClassSelectorForm(forms.Form):\n    MAX_RADIO_SELECT_ITEMS = 5\n\n    def __init__(self, *args, **kwargs):\n        product_classes = kwargs.pop('product_classes', [])\n        super(ProductClassSelectorForm, self).__init__(*args, **kwargs)\n        choices = [(obj.pk, obj.name) for obj in product_classes]\n        if len(product_classes) > self.MAX_RADIO_SELECT_ITEMS:\n            widget = forms.Select\n        else:\n            widget = forms.RadioSelect\n        self.fields['product_cls'] = forms.ChoiceField(\n            label=pgettext_lazy('Product class form label', 'Product type'),\n            choices=choices, widget=widget)\n\n\nclass StockForm(forms.ModelForm):\n    class Meta:\n        model = Stock\n        exclude = ['quantity_allocated']\n\n    def __init__(self, *args, **kwargs):\n        product = kwargs.pop('product')\n        super(StockForm, self).__init__(*args, **kwargs)\n        if not product.product_class.has_variants:\n            initial = product.variants.first()\n        else:\n            initial = None\n        self.fields['variant'] = forms.ModelChoiceField(\n            queryset=product.variants, initial=initial)\n\n\nclass ProductClassForm(forms.ModelForm):\n    class Meta:\n        model = ProductClass\n        exclude = []\n        labels = {\n            'variant_attributes': pgettext_lazy(\n                'Product class form label',\n                'Attributes specific to each variant'),\n            'product_attributes': pgettext_lazy(\n                'Product class form label',\n                'Attributes common to all variants')}\n\n    def clean(self):\n        data = super(ProductClassForm, self).clean()\n        has_variants = self.cleaned_data['has_variants']\n        product_attr = set(self.cleaned_data['product_attributes'])\n        variant_attr = set(self.cleaned_data['variant_attributes'])\n        if not has_variants and len(variant_attr) > 0:\n            msg = pgettext_lazy(\n                'Product Class Errors',\n                'Product variants are disabled.')\n            self.add_error('variant_attributes', msg)\n        if len(product_attr & variant_attr) > 0:\n            msg = pgettext_lazy(\n                'Product Class Errors',\n                'A single attribute can\\'t belong to both a product '\n                'and its variant.')\n            self.add_error('variant_attributes', msg)\n\n        if self.instance.pk:\n            variants_changed = not (self.fields['has_variants'].initial ==\n                                    has_variants)\n            if variants_changed:\n                query = self.instance.products.all()\n                query = query.annotate(variants_counter=Count('variants'))\n                query = query.filter(variants_counter__gt=1)\n                if query.exists():\n                    msg = pgettext_lazy(\n                        'Product Class Errors',\n                        'Some products of this type have more than '\n                        'one variant.')\n                    self.add_error('has_variants', msg)\n        return data\n\n\nclass ProductForm(forms.ModelForm):\n\n    class Meta:\n        model = Product\n        exclude = ['attributes', 'product_class']\n\n    def __init__(self, *args, **kwargs):\n        self.product_attributes = []\n        super(ProductForm, self).__init__(*args, **kwargs)\n        field = self.fields['name']\n        field.widget.attrs['placeholder'] = pgettext_lazy(\n            'Product form labels', 'Give your awesome product a name')\n        field = self.fields['categories']\n        field.widget.attrs['data-placeholder'] = pgettext_lazy(\n            'Product form labels', 'Search')\n        product_class = self.instance.product_class\n        self.product_attributes = product_class.product_attributes.all()\n        self.product_attributes = self.product_attributes.prefetch_related(\n            'values')\n        self.prepare_fields_for_attributes()\n\n    def prepare_fields_for_attributes(self):\n        for attribute in self.product_attributes:\n            field_defaults = {\n                'label': attribute.display,\n                'required': False,\n                'initial': self.instance.get_attribute(attribute.pk)}\n            if attribute.has_values():\n                field = CachingModelChoiceField(\n                    queryset=attribute.values.all(), **field_defaults)\n            else:\n                field = forms.CharField(**field_defaults)\n            self.fields[attribute.get_formfield_name()] = field\n\n    def iter_attribute_fields(self):\n        for attr in self.product_attributes:\n            yield self[attr.get_formfield_name()]\n\n    def save(self, commit=True):\n        attributes = {}\n        for attr in self.product_attributes:\n            value = self.cleaned_data.pop(attr.get_formfield_name())\n            if isinstance(value, AttributeChoiceValue):\n                attributes[smart_text(attr.pk)] = smart_text(value.pk)\n            else:\n                attributes[smart_text(attr.pk)] = value\n        self.instance.attributes = attributes\n        instance = super(ProductForm, self).save(commit=commit)\n        search_index.insert_or_update_object(instance)\n        return instance\n\n\nclass ProductVariantForm(forms.ModelForm):\n    class Meta:\n        model = ProductVariant\n        exclude = ['attributes', 'product', 'images']\n\n    def __init__(self, *args, **kwargs):\n        super(ProductVariantForm, self).__init__(*args, **kwargs)\n        if self.instance.product.pk:\n            self.fields['price_override'].widget.attrs[\n                'placeholder'] = self.instance.product.price.gross\n            self.fields['weight_override'].widget.attrs[\n                'placeholder'] = self.instance.product.weight\n\n\nclass CachingModelChoiceIterator(ModelChoiceIterator):\n    def __iter__(self):\n        if self.field.empty_label is not None:\n            yield ('', self.field.empty_label)\n        for obj in self.queryset:\n            yield self.choice(obj)\n\n\nclass CachingModelChoiceField(forms.ModelChoiceField):\n    def _get_choices(self):\n        if hasattr(self, '_choices'):\n            return self._choices\n        return CachingModelChoiceIterator(self)\n    choices = property(_get_choices, forms.ChoiceField._set_choices)\n\n\nclass VariantAttributeForm(forms.ModelForm):\n    class Meta:\n        model = ProductVariant\n        fields = []\n\n    def __init__(self, *args, **kwargs):\n        super(VariantAttributeForm, self).__init__(*args, **kwargs)\n        attrs = self.instance.product.product_class.variant_attributes.all()\n        self.available_attrs = attrs.prefetch_related('values')\n        for attr in self.available_attrs:\n            field_defaults = {'label': attr.display,\n                              'required': True,\n                              'initial': self.instance.get_attribute(attr.pk)}\n            if attr.has_values():\n                field = CachingModelChoiceField(\n                    queryset=attr.values.all(), **field_defaults)\n            else:\n                field = forms.CharField(**field_defaults)\n            self.fields[attr.get_formfield_name()] = field\n\n    def save(self, commit=True):\n        attributes = {}\n        for attr in self.available_attrs:\n            value = self.cleaned_data.pop(attr.get_formfield_name())\n            if isinstance(value, AttributeChoiceValue):\n                attributes[smart_text(attr.pk)] = smart_text(value.pk)\n            else:\n                attributes[smart_text(attr.pk)] = value\n        self.instance.attributes = attributes\n        return super(VariantAttributeForm, self).save(commit=commit)\n\n\nclass VariantBulkDeleteForm(forms.Form):\n    items = forms.ModelMultipleChoiceField(queryset=ProductVariant.objects)\n\n    def delete(self):\n        items = ProductVariant.objects.filter(\n            pk__in=self.cleaned_data['items'])\n        items.delete()\n\n\nclass StockBulkDeleteForm(forms.Form):\n    items = forms.ModelMultipleChoiceField(queryset=Stock.objects)\n\n    def delete(self):\n        items = Stock.objects.filter(pk__in=self.cleaned_data['items'])\n        items.delete()\n\n\nclass ProductImageForm(forms.ModelForm):\n    variants = forms.ModelMultipleChoiceField(\n        queryset=ProductVariant.objects.none(),\n        widget=forms.CheckboxSelectMultiple, required=False)\n\n    class Meta:\n        model = ProductImage\n        exclude = ('product', 'order')\n\n    def __init__(self, *args, **kwargs):\n        super(ProductImageForm, self).__init__(*args, **kwargs)\n        show_variants = self.instance.product.product_class.has_variants\n        if self.instance.product and show_variants:\n            variants = self.fields['variants']\n            variants.queryset = self.instance.product.variants.all()\n            variants.initial = self.instance.variant_images.values_list(\n                'variant', flat=True)\n        if self.instance.image:\n            self.fields['image'].widget = ImagePreviewWidget()\n\n    @transaction.atomic\n    def save_variant_images(self, instance):\n        variant_images = []\n        # Clean up old mapping\n        instance.variant_images.all().delete()\n        for variant in self.cleaned_data['variants']:\n            variant_images.append(\n                VariantImage(variant=variant, image=instance))\n        VariantImage.objects.bulk_create(variant_images)\n\n    def save(self, commit=True):\n        instance = super(ProductImageForm, self).save(commit=commit)\n        self.save_variant_images(instance)\n        return instance\n\n\nclass ProductAttributeForm(forms.ModelForm):\n    class Meta:\n        model = ProductAttribute\n        exclude = []\n\n\nclass StockLocationForm(forms.ModelForm):\n    class Meta:\n        model = StockLocation\n        exclude = []\n\n\nAttributeChoiceValueFormset = inlineformset_factory(\n    ProductAttribute, AttributeChoiceValue, exclude=(), extra=1)\n", "path": "saleor/dashboard/product/forms.py"}]}