Пример #1
0
 def test_loading_classes_defined_in_both_local_and_izi_modules(self):
     with override_settings(INSTALLED_APPS=self.installed_apps):
         (Free, FixedPrice) = get_classes('shipping.methods',
                                          ('Free', 'FixedPrice'))
         self.assertEqual('tests._site.shipping.methods', Free.__module__)
         self.assertEqual('izi.apps.shipping.methods',
                          FixedPrice.__module__)
Пример #2
0
 def test_load_overriden_3rd_party_class_correctly(self):
     self.installed_apps.append('apps.myapp')
     with override_settings(INSTALLED_APPS=self.installed_apps):
         Cow, Goat = get_classes('myapp.models', ('Cow', 'Goat'),
                                 self.core_app_prefix)
         self.assertEqual('thirdparty_package.apps.myapp.models',
                          Cow.__module__)
         self.assertEqual('apps.myapp.models', Goat.__module__)
Пример #3
0
 def test_loading_classes_with_root_app(self):
     import tests._site.shipping
     path = dirname(dirname(tests._site.shipping.__file__))
     with temporary_python_path([path]):
         self.installed_apps[self.installed_apps.index(
             'tests._site.shipping')] = 'shipping'
         with override_settings(INSTALLED_APPS=self.installed_apps):
             (Free, ) = get_classes('shipping.methods', ('Free', ))
             self.assertEqual('shipping.methods', Free.__module__)
from django.test import TestCase

from izi.apps.dashboard.promotions import forms
from izi.core.loading import get_classes

RawHTML, PagePromotion = get_classes('promotions.models', ['RawHTML', 'PagePromotion'])


class TestPagePromotionForm(TestCase):

    def test_page_promotion_has_fields(self):
        promotion = RawHTML()
        promotion.save()
        instance = PagePromotion(content_object=promotion)
        data = {'position': 'page', 'page_url': '/'}
        form = forms.PagePromotionForm(data=data, instance=instance)
        self.assertTrue(form.is_valid())
        page_promotion = form.save()
        self.assertEqual(page_promotion.page_url, '/')
Пример #5
0
import logging

from django.db import IntegrityError
from django.db.models import F
from django.dispatch import receiver

from izi.apps.basket.signals import basket_addition
from izi.apps.catalogue.signals import product_viewed
from izi.apps.order.signals import order_placed
from izi.apps.search.signals import user_search
from izi.core.loading import get_classes

UserSearch, UserRecord, ProductRecord, UserProductView = get_classes(
    'analytics.models', ['UserSearch', 'UserRecord', 'ProductRecord',
                         'UserProductView'])

# Helpers

logger = logging.getLogger('izi.analytics')


def _update_counter(model, field_name, filter_kwargs, increment=1):
    """
    Efficiently updates a counter field by a given increment. Uses Django's
    update() call to fetch and update in one query.

    TODO: This has a race condition, we should use UPSERT here

    :param model: The model class of the recording model
    :param field_name: The name of the field to update
    :param filter_kwargs: Parameters to the ORM's filter() function to get the
Пример #6
0
 def test_loading_class_from_module_not_defined_in_local_app(self):
     with override_settings(INSTALLED_APPS=self.installed_apps):
         (Repository, ) = get_classes('shipping.repository',
                                      ('Repository', ))
         self.assertEqual('izi.apps.shipping.repository',
                          Repository.__module__)
Пример #7
0
 def test_loading_class_defined_in_local_module(self):
     with override_settings(INSTALLED_APPS=self.installed_apps):
         (Free, ) = get_classes('shipping.methods', ('Free', ))
         self.assertEqual('tests._site.shipping.methods', Free.__module__)
Пример #8
0
from django_tables2 import SingleTableMixin, SingleTableView

from izi.core.loading import get_classes, get_model
from izi.views.generic import ObjectLookupView

(ProductForm,
 ProductClassSelectForm,
 ProductSearchForm,
 ProductClassForm,
 CategoryForm,
 StockAlertSearchForm,
 AttributeOptionGroupForm) \
    = get_classes('dashboard.catalogue.forms',
                  ('ProductForm',
                   'ProductClassSelectForm',
                   'ProductSearchForm',
                   'ProductClassForm',
                   'CategoryForm',
                   'StockAlertSearchForm',
                   'AttributeOptionGroupForm'))
(StockRecordFormSet,
 ProductCategoryFormSet,
 ProductImageFormSet,
 ProductRecommendationFormSet,
 ProductAttributesFormSet,
 AttributeOptionFormSet) \
    = get_classes('dashboard.catalogue.formsets',
                  ('StockRecordFormSet',
                   'ProductCategoryFormSet',
                   'ProductImageFormSet',
                   'ProductRecommendationFormSet',
                   'ProductAttributesFormSet',
Пример #9
0
from izi.core.loading import get_class, get_classes

OrderReportGenerator = get_class('order.reports', 'OrderReportGenerator')
ProductReportGenerator, UserReportGenerator \
    = get_classes('analytics.reports', ['ProductReportGenerator',
                                        'UserReportGenerator'])
OpenBasketReportGenerator, SubmittedBasketReportGenerator \
    = get_classes('basket.reports', ['OpenBasketReportGenerator',
                                     'SubmittedBasketReportGenerator'])
OfferReportGenerator = get_class('offer.reports', 'OfferReportGenerator')
VoucherReportGenerator = get_class('voucher.reports', 'VoucherReportGenerator')


class GeneratorRepository(object):

    generators = [OrderReportGenerator,
                  ProductReportGenerator,
                  UserReportGenerator,
                  OpenBasketReportGenerator,
                  SubmittedBasketReportGenerator,
                  VoucherReportGenerator,
                  OfferReportGenerator]

    def get_report_generators(self):
        return self.generators

    def get_generator(self, code):
        for generator in self.generators:
            if generator.code == code:
                return generator
        return None
Пример #10
0
from django.shortcuts import get_object_or_404, redirect
from django.template.loader import render_to_string
from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.views import generic

from izi.apps.customer.utils import normalise_email
from izi.core.compat import get_user_model
from izi.core.loading import get_classes, get_model
from izi.views import sort_queryset

User = get_user_model()
Partner = get_model('partner', 'Partner')
(PartnerSearchForm, PartnerCreateForm, PartnerAddressForm, NewUserForm,
 UserEmailForm, ExistingUserForm) = get_classes('dashboard.partners.forms', [
     'PartnerSearchForm', 'PartnerCreateForm', 'PartnerAddressForm',
     'NewUserForm', 'UserEmailForm', 'ExistingUserForm'
 ])


class PartnerListView(generic.ListView):
    model = Partner
    context_object_name = 'partners'
    template_name = 'dashboard/partners/partner_list.html'
    form_class = PartnerSearchForm

    def get_queryset(self):
        qs = self.model._default_manager.all()
        qs = sort_queryset(qs, self.request, ['name'])

        self.description = _("All partners")
Пример #11
0
from django.forms.models import inlineformset_factory

from izi.core.loading import get_class, get_classes

HandPickedProductList, OrderedProduct \
    = get_classes('promotions.models',
                  ['HandPickedProductList', 'OrderedProduct'])
ProductSelect = get_class('dashboard.catalogue.widgets', 'ProductSelect')
OrderedProductForm = get_class('dashboard.promotions.forms',
                               'OrderedProductForm')

OrderedProductFormSet = inlineformset_factory(HandPickedProductList,
                                              OrderedProduct,
                                              form=OrderedProductForm,
                                              extra=2)
Пример #12
0
from django.contrib import messages
from django.contrib.contenttypes.models import ContentType
from django.db.models import Count
from django.http import HttpResponseRedirect
from django.shortcuts import HttpResponse
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views import generic

from izi.apps.promotions.conf import PROMOTION_CLASSES
from izi.core.loading import get_class, get_classes

SingleProduct, RawHTML, Image, MultiImage, AutomaticProductList, \
    PagePromotion, HandPickedProductList \
    = get_classes('promotions.models',
                  ['SingleProduct', 'RawHTML', 'Image', 'MultiImage',
                   'AutomaticProductList', 'PagePromotion',
                   'HandPickedProductList'])
SelectForm, RawHTMLForm, PagePromotionForm, HandPickedProductListForm, \
    SingleProductForm  \
    = get_classes('dashboard.promotions.forms',
                  ['PromotionTypeSelectForm', 'RawHTMLForm',
                   'PagePromotionForm', 'HandPickedProductListForm',
                   'SingleProductForm'])
OrderedProductFormSet = get_class('dashboard.promotions.formsets', 'OrderedProductFormSet')


class ListView(generic.TemplateView):
    template_name = 'dashboard/promotions/promotion_list.html'

    def get_context_data(self):
        # Need to load all promotions of all types and chain them together
Пример #13
0
from __future__ import unicode_literals

from decimal import Decimal as D

from django.test import TestCase
from django.test.client import Client
from django.urls import reverse
from django.utils.encoding import force_text
from mock import Mock, patch
from izi.apps.basket.models import Basket
from izi.apps.order.models import Order
from izi.core.loading import get_classes
from izi.test.factories import create_product
from purl import URL

Partner, StockRecord = get_classes('partner.models',
                                   ('Partner', 'StockRecord'))

(ProductClass, Product, ProductAttribute, ProductAttributeValue) = get_classes(
    'catalogue.models',
    ('ProductClass', 'Product', 'ProductAttribute', 'ProductAttributeValue'))


class MockedPayPalTests(TestCase):
    fixtures = ['countries.json']
    response_body = None

    def setUp(self):
        self.client = Client()
        with patch('requests.post') as post:
            self.patch_http_post(post)
            self.perform_action()
Пример #14
0
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.views.generic import DeleteView, FormView, ListView

from izi.core.loading import get_class, get_classes, get_model
from izi.views import sort_queryset

ConditionalOffer = get_model('offer', 'ConditionalOffer')
Condition = get_model('offer', 'Condition')
Range = get_model('offer', 'Range')
Product = get_model('catalogue', 'Product')
OrderDiscount = get_model('order', 'OrderDiscount')
Benefit = get_model('offer', 'Benefit')
MetaDataForm, ConditionForm, BenefitForm, RestrictionsForm, OfferSearchForm \
    = get_classes('dashboard.offers.forms',
                  ['MetaDataForm', 'ConditionForm', 'BenefitForm',
                   'RestrictionsForm', 'OfferSearchForm'])
OrderDiscountCSVFormatter = get_class('dashboard.offers.reports',
                                      'OrderDiscountCSVFormatter')


class OfferListView(ListView):
    model = ConditionalOffer
    context_object_name = 'offers'
    template_name = 'dashboard/offers/offer_list.html'
    form_class = OfferSearchForm
    paginate_by = settings.IZI_DASHBOARD_ITEMS_PER_PAGE

    def get_queryset(self):
        qs = self.model._default_manager.exclude(
            offer_type=ConditionalOffer.VOUCHER)
Пример #15
0
from decimal import Decimal as D

from django.core.exceptions import ImproperlyConfigured
from django.utils.translation import gettext_lazy as _

from izi.core.loading import get_classes

(Free, NoShippingRequired,
 TaxExclusiveOfferDiscount, TaxInclusiveOfferDiscount) \
    = get_classes('shipping.methods', ['Free', 'NoShippingRequired',
                                       'TaxExclusiveOfferDiscount', 'TaxInclusiveOfferDiscount'])


class Repository(object):
    """
    Repository class responsible for returning ShippingMethod
    objects for a given user, basket etc
    """

    # We default to just free shipping. Customise this class and override this
    # property to add your own shipping methods. This should be a list of
    # instantiated shipping methods.
    methods = (Free(), )

    # API

    def get_shipping_methods(self, basket, shipping_addr=None, **kwargs):
        """
        Return a list of all applicable shipping method instances for a given
        basket, address etc.
        """
Пример #16
0
from django.conf import settings
from django.contrib import messages
from django.shortcuts import get_object_or_404, redirect
from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, DetailView, ListView, View

from izi.apps.catalogue.reviews.signals import review_added
from izi.core.loading import get_classes, get_model
from izi.core.utils import redirect_to_referrer

ProductReviewForm, VoteForm, SortReviewsForm = get_classes(
    'catalogue.reviews.forms',
    ['ProductReviewForm', 'VoteForm', 'SortReviewsForm'])

Vote = get_model('reviews', 'vote')
ProductReview = get_model('reviews', 'ProductReview')
Product = get_model('catalogue', 'product')


class CreateProductReview(CreateView):
    template_name = "catalogue/reviews/review_form.html"
    model = ProductReview
    product_model = Product
    form_class = ProductReviewForm
    view_signal = review_added

    def dispatch(self, request, *args, **kwargs):
        self.product = get_object_or_404(self.product_model,
                                         pk=kwargs['product_pk'])
        # check permission to leave review
        if not self.product.is_review_permitted(request.user):
Пример #17
0
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views.generic import (DeleteView, DetailView, FormView, ListView,
                                  UpdateView)
from django.views.generic.detail import SingleObjectMixin
from django.views.generic.edit import FormMixin
from django_tables2 import SingleTableView

from izi.apps.customer.utils import normalise_email
from izi.core.compat import get_user_model
from izi.core.loading import get_class, get_classes, get_model
from izi.views.generic import BulkEditMixin

UserSearchForm, ProductAlertSearchForm, ProductAlertUpdateForm = get_classes(
    'dashboard.users.forms',
    ('UserSearchForm', 'ProductAlertSearchForm', 'ProductAlertUpdateForm'))
PasswordResetForm = get_class('customer.forms', 'PasswordResetForm')
UserTable = get_class('dashboard.users.tables', 'UserTable')
ProductAlert = get_model('customer', 'ProductAlert')
User = get_user_model()


class IndexView(BulkEditMixin, FormMixin, SingleTableView):
    template_name = 'dashboard/users/index.html'
    table_pagination = True
    model = User
    actions = (
        'make_active',
        'make_inactive',
    )
Пример #18
0
from django.db import models
from django.db.models import Sum
from django.utils.encoding import smart_text
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _

from izi.core.compat import AUTH_USER_MODEL
from izi.core.loading import get_class, get_classes
from izi.core.utils import get_default_currency
from izi.models.fields.slugfield import SlugField
from izi.templatetags.currency_filters import currency

OfferApplications = get_class('offer.results', 'OfferApplications')
Unavailable = get_class('partner.availability', 'Unavailable')
LineOfferConsumer = get_class('basket.utils', 'LineOfferConsumer')
OpenBasketManager, SavedBasketManager = get_classes(
    'basket.managers', ['OpenBasketManager', 'SavedBasketManager'])


class AbstractBasket(models.Model):
    """
    Basket object
    """
    # Baskets can be anonymously owned - hence this field is nullable.  When a
    # anon user signs in, their two baskets are merged.
    owner = models.ForeignKey(AUTH_USER_MODEL,
                              null=True,
                              related_name='baskets',
                              on_delete=models.CASCADE,
                              verbose_name=_("Owner"))

    # Basket statuses
Пример #19
0
 def test_load_izi_classes_correctly(self):
     Product, Category = get_classes('catalogue.models',
                                     ('Product', 'Category'))
     self.assertEqual('izi.apps.catalogue.models', Product.__module__)
     self.assertEqual('izi.apps.catalogue.models', Category.__module__)
Пример #20
0
import os
from decimal import Decimal as D

from django.db.transaction import atomic
from django.utils.translation import gettext_lazy as _

from izi.core.compat import UnicodeCSVReader
from izi.core.loading import get_class, get_classes

ImportingError = get_class('partner.exceptions', 'ImportingError')
Partner, StockRecord = get_classes('partner.models',
                                   ['Partner', 'StockRecord'])
ProductClass, Product, Category, ProductCategory = get_classes(
    'catalogue.models',
    ('ProductClass', 'Product', 'Category', 'ProductCategory'))
create_from_breadcrumbs = get_class('catalogue.categories',
                                    'create_from_breadcrumbs')


class CatalogueImporter(object):
    """
    CSV product importer used to built sandbox. Might not work very well
    for anything else.
    """

    _flush = False

    def __init__(self, logger, delimiter=",", flush=False):
        self.logger = logger
        self._delimiter = delimiter
        self._flush = flush
Пример #21
0
 def test_raise_exception_when_bad_appname_used(self):
     with self.assertRaises(AppNotFoundError):
         get_classes('fridge.models', ('Product', 'Category'))
Пример #22
0
from decimal import Decimal as D
from decimal import ROUND_UP

from django.utils.translation import gettext_lazy as _
from django.utils.translation import ungettext

from izi.core.loading import get_classes, get_model
from izi.templatetags.currency_filters import currency

Condition = get_model('offer', 'Condition')
range_anchor, unit_price = get_classes('offer.utils',
                                       ['range_anchor', 'unit_price'])

__all__ = ['CountCondition', 'CoverageCondition', 'ValueCondition']


class CountCondition(Condition):
    """
    An offer condition dependent on the NUMBER of matching items from the
    basket.
    """
    _description = _("Basket includes %(count)d item(s) from %(range)s")

    @property
    def name(self):
        return self._description % {
            'count': self.value,
            'range': str(self.range).lower()
        }

    @property
Пример #23
0
 def test_loading_class_which_is_not_defined_in_local_module(self):
     with override_settings(INSTALLED_APPS=self.installed_apps):
         (FixedPrice, ) = get_classes('shipping.methods', ('FixedPrice', ))
         self.assertEqual('izi.apps.shipping.methods',
                          FixedPrice.__module__)
Пример #24
0
from django.shortcuts import HttpResponse, get_object_or_404
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ungettext
from django.views.generic import (CreateView, DeleteView, ListView, UpdateView,
                                  View)

from izi.core.loading import get_classes, get_model
from izi.views.generic import BulkEditMixin

Range = get_model('offer', 'Range')
RangeProduct = get_model('offer', 'RangeProduct')
RangeProductFileUpload = get_model('offer', 'RangeProductFileUpload')
Product = get_model('catalogue', 'Product')
RangeForm, RangeProductForm = get_classes('dashboard.ranges.forms',
                                          ['RangeForm', 'RangeProductForm'])


class RangeListView(ListView):
    model = Range
    context_object_name = 'ranges'
    template_name = 'dashboard/ranges/range_list.html'
    paginate_by = settings.IZI_DASHBOARD_ITEMS_PER_PAGE


class RangeCreateView(CreateView):
    model = Range
    template_name = 'dashboard/ranges/range_form.html'
    form_class = RangeForm

    def get_success_url(self):
Пример #25
0
from django.db import models
from django.db.models.query import Q
from django.template.defaultfilters import date as date_filter
from django.urls import reverse
from django.utils.functional import cached_property
from django.utils.timezone import get_current_timezone, now
from django.utils.translation import gettext_lazy as _

from izi.core.compat import AUTH_USER_MODEL
from izi.core.decorators import deprecated
from izi.core.loading import get_class, get_classes, get_model
from izi.models import fields
from izi.templatetags.currency_filters import currency

ActiveOfferManager, BrowsableRangeManager \
    = get_classes('offer.managers', ['ActiveOfferManager', 'BrowsableRangeManager'])
ZERO_DISCOUNT = get_class('offer.results', 'ZERO_DISCOUNT')
load_proxy, unit_price = get_classes('offer.utils', ['load_proxy', 'unit_price'])


class BaseOfferMixin(models.Model):
    class Meta:
        abstract = True

    def proxy(self):
        """
        Return the proxy model
        """
        klassmap = self.proxy_map
        # Short-circuit logic if current class is already a proxy class.
        if self.__class__ in klassmap.values():
Пример #26
0
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.http import is_safe_url
from django.utils.translation import gettext_lazy as _
from django.views.generic import FormView, View
from extra_views import ModelFormSetView

from izi.apps.basket.signals import (
    basket_addition, voucher_addition, voucher_removal)
from izi.core import ajax
from izi.core.loading import get_class, get_classes, get_model
from izi.core.utils import redirect_to_referrer, safe_referrer

Applicator = get_class('offer.applicator', 'Applicator')
(BasketLineForm, AddToBasketForm, BasketVoucherForm, SavedLineForm) = get_classes(
    'basket.forms', ('BasketLineForm', 'AddToBasketForm',
                     'BasketVoucherForm', 'SavedLineForm'))
BasketLineFormSet, SavedLineFormSet = get_classes(
    'basket.formsets', ('BasketLineFormSet', 'SavedLineFormSet'))
Repository = get_class('shipping.repository', 'Repository')

OrderTotalCalculator = get_class(
    'checkout.calculators', 'OrderTotalCalculator')
BasketMessageGenerator = get_class('basket.utils', 'BasketMessageGenerator')


class BasketView(ModelFormSetView):
    model = get_model('basket', 'Line')
    basket_model = get_model('basket', 'Basket')
    formset_class = BasketLineFormSet
    form_class = BasketLineForm
Пример #27
0
from django.db.models.signals import post_save
from django.dispatch import receiver

from izi.core.loading import get_classes

StockRecord, StockAlert = get_classes('partner.models',
                                      ['StockRecord', 'StockAlert'])


@receiver(post_save, sender=StockRecord)
def update_stock_alerts(sender, instance, created, **kwargs):
    """
    Update low-stock alerts
    """
    if created or kwargs.get('raw', False):
        return
    stockrecord = instance
    try:
        alert = StockAlert.objects.get(stockrecord=stockrecord,
                                       status=StockAlert.OPEN)
    except StockAlert.DoesNotExist:
        alert = None

    if stockrecord.is_below_threshold and not alert:
        StockAlert.objects.create(stockrecord=stockrecord,
                                  threshold=stockrecord.low_stock_threshold)
    elif not stockrecord.is_below_threshold and alert:
        alert.close()
Пример #28
0
from django.contrib import messages
from django.core.exceptions import ValidationError
from django.http import HttpResponseRedirect
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views import generic
from django.views.generic import ListView

from izi.core.loading import get_classes, get_model
from izi.core.utils import slugify
from izi.core.validators import URLDoesNotExistValidator

FlatPage = get_model('flatpages', 'FlatPage')
Site = get_model('sites', 'Site')
PageSearchForm, PageUpdateForm = get_classes(
    'dashboard.pages.forms', ('PageSearchForm', 'PageUpdateForm'))


class PageListView(ListView):
    """
    View for listing all existing flatpages.
    """
    template_name = 'dashboard/pages/index.html'
    model = FlatPage
    form_class = PageSearchForm
    paginate_by = settings.IZI_DASHBOARD_ITEMS_PER_PAGE
    desc_template = '%(main_filter)s %(title_filter)s'

    def get_queryset(self):
        """
        Get queryset of all flatpages to be displayed. If a
Пример #29
0
from django.test.utils import override_settings
from django.conf import settings
from django.urls import clear_url_caches, reverse
from django.utils.http import urlquote

from izi.core.compat import get_user_model
from izi.core.loading import get_class, get_classes, get_model
from izi.apps.shipping import methods
from izi.test.testcases import WebTestCase
from izi.test import factories
from . import CheckoutMixin

GatewayForm = get_class('checkout.forms', 'GatewayForm')
CheckoutSessionData = get_class('checkout.utils', 'CheckoutSessionData')
RedirectRequired, UnableToTakePayment, PaymentError = get_classes(
    'payment.exceptions',
    ['RedirectRequired', 'UnableToTakePayment', 'PaymentError'])
UnableToPlaceOrder = get_class('order.exceptions', 'UnableToPlaceOrder')

Basket = get_model('basket', 'Basket')
Order = get_model('order', 'Order')
User = get_user_model()

# Python 3 compat
try:
    from imp import reload
except ImportError:
    pass


def reload_url_conf():
Пример #30
0
from django import shortcuts
from django.contrib import messages
from django.template.loader import render_to_string
from django.urls import reverse
from django.views import generic

from izi.core.loading import get_classes, get_model

WeightBandForm, WeightBasedForm = get_classes(
    'dashboard.shipping.forms', ['WeightBandForm', 'WeightBasedForm'])
WeightBased = get_model('shipping', 'WeightBased')
WeightBand = get_model('shipping', 'WeightBand')


class WeightBasedListView(generic.ListView):
    model = WeightBased
    template_name = "dashboard/shipping/weight_based_list.html"
    context_object_name = "methods"


class WeightBasedCreateView(generic.CreateView):
    model = WeightBased
    form_class = WeightBasedForm
    template_name = "dashboard/shipping/weight_based_form.html"

    def get_success_url(self):
        msg = render_to_string(
            'dashboard/shipping/messages/method_created.html',
            {'method': self.object})
        messages.success(self.request, msg, extra_tags='safe noicon')
        return reverse('dashboard:shipping-method-detail',