From 6712c8e8f0cc60fb5eb93a326206578aac723038 Mon Sep 17 00:00:00 2001 From: Adam Charnock Date: Sat, 9 Jan 2021 11:18:22 +0000 Subject: [PATCH 01/21] Bumping all requirements to be up-to-date Happy to re-add formatting later, I just need to know which tool you used to do it. Also, maybe we'll move to poetry. --- requirements/common.txt | 38 +++++++++++++++++++------------------- requirements/optionals.txt | 6 +++--- requirements/test.txt | 4 ++-- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/requirements/common.txt b/requirements/common.txt index 35521419..4267a0e0 100644 --- a/requirements/common.txt +++ b/requirements/common.txt @@ -3,32 +3,32 @@ # UNMAINTAINED means the package owner is no longer maintaining it # Core -Django>=1.11,<2.3 # ----------------------------------------------------------- (bumped 2019-05-28) -sqlparse>=0.2,<0.3 # (required for some old migrations) ----------------------- (bumped 2018-06-07) +Django = "^3.1.5" +sqlparse = "^0.4.1" # (required for some old migrations) # Django Utils -django-fsm>=2.3,<2.7 # -------------------------------------------------------- (bumped 2018-06-07) -django-filter>=1.1,<2.1 # ----------------------------------------------------- (bumped 2019-01-31) -django-livefield>=2.8,<3.3 # -------------------------------------------------- (bumped 2019-06-10) -django-jsonfield==1.0.1 # ---------------------------------------------------- (checked 2018-06-07) -django-model-utils>=3.0,<3.2 # ------------------------------------------------ (bumped 2018-06-07) -django-annoying>=0.10,<0.11 # (various Django helpers) ------------------------ (bumped 2018-06-07) -django-autocomplete-light>=3.2,<3.3 # ----------------------------------------- (bumped 2018-06-07) +django-fsm = "^2.7.1" +django-filter = "^2.4.0" +django-livefield = "^3.3.0" +django-jsonfield = "^1.4.1" +django-model-utils = "^4.1.1" +django-annoying = "^0.10.6" +django-autocomplete-light = "^3.8.1" # API -djangorestframework>=3.8,<3.10 # ---------------------------------------------- (bumped 2019-01-31) -djangorestframework-bulk<0.3 # ----------------------------------------------- (checked 2018-06-07) +djangorestframework = "^3.12.2" +djangorestframework-bulk = "^0.2.1" # I18n -pycountry>=16.11.08 # --------------------------------------------------------- (bumped 2018-06-07) -python-dateutil>=2.6,<2.8 # --------------------------------------------------- (bumped 2018-06-07) -pyvat>=1.3,<1.4 # ------------------------------------------------------------- (bumped 2018-06-07) +pycountry = "^20.7.3" +python-dateutil = "^2.8.1" +pyvat = "^1.3.13" # Crypto -cryptography>=1.9,<2.4 # ------------------------------------------------------ (bumped 2018-11-02) -PyJWT>=1.5,<1.7 # ------------------------------------------------------------- (bumped 2018-06-07) +cryptography = "^3.3.1" +PyJWT = "^2.0.0" # Other -furl>=1,<1.3 # (URL parsing and manipulation) --------------------------------- (bumped 2018-08-10) -xhtml2pdf>=0.2,<0.3 # (PDF rendering, python-dev is required) ----------------- (bumped 2018-06-07) -PyPDF2>=1.26,<2 # (PDF manipulation) ------------------------------------------ (bumped 2018-06-07) +furl = "^2.1.0" +xhtml2pdf = "^0.2.5" +PyPDF2 = "^1.26.0" diff --git a/requirements/optionals.txt b/requirements/optionals.txt index 1c68544e..7c84848b 100644 --- a/requirements/optionals.txt +++ b/requirements/optionals.txt @@ -1,3 +1,3 @@ -celery>=4.0,<4.2 # ------------------------------------------------------------ (bumped 2018-06-07) -redis>=2.10,<2.11 # ----------------------------------------------------------- (bumped 2018-06-07) -celery-once>=1.2,<2.1 # ------------------------------------------------------- (bumped 2018-06-07) +celery = "^5.0.5" +redis = "^3.5.3" +celery_once = "^3.0.1" diff --git a/requirements/test.txt b/requirements/test.txt index d0152028..54b4ba51 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -6,8 +6,8 @@ flake8==2.4.1 freezegun==0.3.8 coverage==3.7.1 django-coverage==1.2.4 -pytest==3.6.4 -pytest-django==3.4.5 +pytest==5.4.0 +pytest-django==4.1.0 factory-boy==2.5.2 pep8==1.7.0 Faker==0.7.17 From 9135e99b0ebba0610f818425df70ada104c4a0c6 Mon Sep 17 00:00:00 2001 From: Adam Charnock Date: Sat, 9 Jan 2021 11:19:31 +0000 Subject: [PATCH 02/21] Removing all python 2 compat imports, which are largely gone from django now anyway --- silver/fixtures/factories.py | 5 ++- silver/models/billing_entities/base.py | 2 -- silver/models/documents/base.py | 3 +- silver/models/documents/entries.py | 2 -- silver/models/payment_methods.py | 2 -- silver/models/plans.py | 3 -- silver/models/product_codes.py | 2 -- silver/models/subscriptions.py | 4 --- silver/models/transactions/transaction.py | 3 +- silver/tests/api/specs/customer.py | 32 ++++++++----------- silver/tests/api/specs/document_entry.py | 11 +++---- silver/tests/api/specs/invoice.py | 21 ++++++------ silver/tests/api/specs/provider.py | 25 +++++++-------- silver/tests/api/specs/transaction.py | 19 ++++++----- silver/tests/api/specs/utils.py | 2 -- silver/tests/api/test_invoice.py | 27 ++++++++-------- silver/tests/api/test_proforma.py | 23 +++++++------ silver/tests/commands/test_generate_docs.py | 3 +- .../tests/commands/test_generate_docs_args.py | 2 +- 19 files changed, 80 insertions(+), 111 deletions(-) diff --git a/silver/fixtures/factories.py b/silver/fixtures/factories.py index fd835833..66bbf2d7 100644 --- a/silver/fixtures/factories.py +++ b/silver/fixtures/factories.py @@ -22,7 +22,6 @@ from django.contrib.auth import get_user_model from django.utils import timezone -from django.utils.six import text_type from silver.models import (Provider, Plan, MeteredFeature, Customer, Subscription, Invoice, ProductCode, PDF, @@ -255,8 +254,8 @@ class DocumentEntryFactory(factory.django.DjangoModelFactory): class Meta: model = DocumentEntry - description = factory.Sequence(lambda n: text_type('Description{cnt}').format(cnt=n)) - unit = factory.Sequence(lambda n: text_type('Unit{cnt}').format(cnt=n)) + description = factory.Sequence(lambda n: 'Description{cnt}'.format(cnt=n)) + unit = factory.Sequence(lambda n: 'Unit{cnt}'.format(cnt=n)) quantity = factory.fuzzy.FuzzyDecimal(low=1.00, high=50000.00, precision=4) unit_price = factory.fuzzy.FuzzyDecimal(low=0.01, high=100.00, precision=4) product_code = factory.SubFactory(ProductCodeFactory) diff --git a/silver/models/billing_entities/base.py b/silver/models/billing_entities/base.py index f0e2713c..54c7f531 100644 --- a/silver/models/billing_entities/base.py +++ b/silver/models/billing_entities/base.py @@ -20,7 +20,6 @@ from django.conf import settings from django.db import models from django.utils.text import slugify -from django.utils.encoding import python_2_unicode_compatible from silver.utils.international import countries @@ -28,7 +27,6 @@ PAYMENT_DUE_DAYS = getattr(settings, 'SILVER_DEFAULT_DUE_DAYS', 5) -@python_2_unicode_compatible class BaseBillingEntity(LiveModel): company = models.CharField(max_length=128, blank=True, null=True) address_1 = models.CharField(max_length=128) diff --git a/silver/models/documents/base.py b/silver/models/documents/base.py index 88f19344..a9bfc5f3 100644 --- a/silver/models/documents/base.py +++ b/silver/models/documents/base.py @@ -34,7 +34,7 @@ from django.db.models import Max, ForeignKey, F from django.template.loader import select_template from django.utils import timezone -from django.utils.encoding import python_2_unicode_compatible, force_text +from django.utils.encoding import force_text from django.utils.text import slugify from django.utils.translation import ugettext_lazy as _ from django.utils.module_loading import import_string @@ -106,7 +106,6 @@ def get_billing_documents_kinds(): for subclass in BillingDocumentBase.__subclasses__()) -@python_2_unicode_compatible class BillingDocumentBase(models.Model): objects = BillingDocumentManager.from_queryset(BillingDocumentQuerySet)() diff --git a/silver/models/documents/entries.py b/silver/models/documents/entries.py index 8689555c..88306163 100644 --- a/silver/models/documents/entries.py +++ b/silver/models/documents/entries.py @@ -18,12 +18,10 @@ from django.core.validators import MinValueValidator from django.db import models -from django.utils.encoding import python_2_unicode_compatible from silver.utils.decorators import require_transaction_currency_and_xe_rate -@python_2_unicode_compatible class DocumentEntry(models.Model): description = models.CharField(max_length=1024) unit = models.CharField(max_length=1024, blank=True, null=True) diff --git a/silver/models/payment_methods.py b/silver/models/payment_methods.py index 5262e4ec..cc2a66f7 100644 --- a/silver/models/payment_methods.py +++ b/silver/models/payment_methods.py @@ -28,7 +28,6 @@ from django.db.models.signals import post_save, pre_save from django.dispatch import receiver from django.utils import timezone -from django.utils.encoding import python_2_unicode_compatible from silver import payment_processors from silver.models import Invoice, Proforma @@ -40,7 +39,6 @@ class PaymentMethodInvalid(Exception): pass -@python_2_unicode_compatible class PaymentMethod(models.Model): class PaymentProcessors: @classmethod diff --git a/silver/models/plans.py b/silver/models/plans.py index ab4a6284..85f59c9c 100644 --- a/silver/models/plans.py +++ b/silver/models/plans.py @@ -19,7 +19,6 @@ from django.core.exceptions import ValidationError from django.core.validators import MinValueValidator from django.db import models -from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ from silver.utils.international import currencies @@ -31,7 +30,6 @@ def get_queryset(self): return super(PlanManager, self).get_queryset().select_related('product_code') -@python_2_unicode_compatible class Plan(models.Model): objects = PlanManager() @@ -138,7 +136,6 @@ def provider_flow(self): return self.provider.flow -@python_2_unicode_compatible class MeteredFeature(models.Model): name = models.CharField( max_length=200, diff --git a/silver/models/product_codes.py b/silver/models/product_codes.py index ba395443..38bd2ee5 100644 --- a/silver/models/product_codes.py +++ b/silver/models/product_codes.py @@ -15,10 +15,8 @@ from __future__ import absolute_import, unicode_literals from django.db import models -from django.utils.encoding import python_2_unicode_compatible -@python_2_unicode_compatible class ProductCode(models.Model): value = models.CharField(max_length=128, unique=True) diff --git a/silver/models/subscriptions.py b/silver/models/subscriptions.py index c91b4855..767dc2a4 100644 --- a/silver/models/subscriptions.py +++ b/silver/models/subscriptions.py @@ -36,7 +36,6 @@ from django.template import TemplateDoesNotExist from django.template.loader import get_template, render_to_string from django.utils import timezone -from django.utils.encoding import python_2_unicode_compatible from django.utils.timezone import utc from django.utils.translation import ugettext_lazy as _ @@ -61,7 +60,6 @@ def field_template_path(field, provider=None): return 'billing_documents/{field}.html'.format(field=field) -@python_2_unicode_compatible class MeteredFeatureUnitsLog(models.Model): metered_feature = models.ForeignKey('MeteredFeature', related_name='consumed', on_delete=models.CASCADE) @@ -121,7 +119,6 @@ def __str__(self): return self.metered_feature.name -@python_2_unicode_compatible class Subscription(models.Model): class STATES(object): ACTIVE = 'active' @@ -1015,7 +1012,6 @@ def __str__(self): return u'%s (%s)' % (self.customer, self.plan.name) -@python_2_unicode_compatible class BillingLog(models.Model): subscription = models.ForeignKey('Subscription', on_delete=models.CASCADE, related_name='billing_logs') diff --git a/silver/models/transactions/transaction.py b/silver/models/transactions/transaction.py index f97b5a37..5a47bac3 100644 --- a/silver/models/transactions/transaction.py +++ b/silver/models/transactions/transaction.py @@ -30,7 +30,7 @@ from django.db.models.signals import post_save from django.dispatch import receiver from django.utils import timezone -from django.utils.encoding import python_2_unicode_compatible, force_text +from django.utils.encoding import force_text from django.utils.translation import ugettext_lazy as _ from silver.models import Invoice, Proforma @@ -41,7 +41,6 @@ logger = logging.getLogger(__name__) -@python_2_unicode_compatible class Transaction(AutoCleanModelMixin, models.Model): _provider = None diff --git a/silver/tests/api/specs/customer.py b/silver/tests/api/specs/customer.py index bca1ece5..1f74756b 100644 --- a/silver/tests/api/specs/customer.py +++ b/silver/tests/api/specs/customer.py @@ -1,27 +1,21 @@ -from decimal import Decimal - -from django.utils.six import text_type - -from silver.tests.api.specs.utils import text_type_or_none - def spec_archived_customer(customer): return { - 'company': text_type(customer.company), - 'email': text_type(customer.email), - 'address_1': text_type(customer.address_1), - 'address_2': text_type(customer.address_2), - 'city': text_type(customer.city), - 'country': text_type(customer.country), - 'state': text_type(customer.state), - 'zip_code': text_type(customer.zip_code), - 'extra': text_type(customer.extra), + 'company': customer.company, + 'email': customer.email, + 'address_1': customer.address_1, + 'address_2': customer.address_2, + 'city': customer.city, + 'country': customer.country, + 'state': customer.state, + 'zip_code': customer.zip_code, + 'extra': customer.extra, 'meta': customer.meta, - 'first_name': text_type(customer.first_name), - 'last_name': text_type(customer.last_name), - 'customer_reference': text_type(customer.customer_reference), + 'first_name': customer.first_name, + 'last_name': customer.last_name, + 'customer_reference': customer.customer_reference, 'consolidated_billing': bool(customer.consolidated_billing), 'payment_due_days': int(customer.payment_due_days), - 'sales_tax_number': text_type_or_none(customer.sales_tax_number), + 'sales_tax_number': customer.sales_tax_number, 'sales_tax_percent': "%.2f" % customer.sales_tax_percent } diff --git a/silver/tests/api/specs/document_entry.py b/silver/tests/api/specs/document_entry.py index 1a582504..9f7fd522 100644 --- a/silver/tests/api/specs/document_entry.py +++ b/silver/tests/api/specs/document_entry.py @@ -1,7 +1,6 @@ from datetime import date from decimal import Decimal -from django.utils.six import text_type from silver.tests.api.specs.utils import ResourceDefinition @@ -17,20 +16,20 @@ }, 'description': { 'required': False, - 'expected_input_types': text_type, + 'expected_input_types': str, 'output': lambda entry: entry.description }, 'unit': { 'required': False, - 'expected_input_types': text_type, + 'expected_input_types': str, 'output': lambda entry: entry.unit, }, 'unit_price': { - 'expected_input_types': (int, float, text_type), + 'expected_input_types': (int, float, str), 'output': lambda entry: "%.4f" % Decimal(entry.unit_price) }, 'quantity': { - 'expected_input_types': (int, float, text_type), + 'expected_input_types': (int, float, str), 'output': lambda entry: "%.4f" % Decimal(entry.quantity) }, 'total_before_tax': { @@ -60,7 +59,7 @@ }, 'product_code': { 'required': False, - 'expected_input_types': text_type, + 'expected_input_types': str, 'output': lambda entry: entry.product_code, } }) diff --git a/silver/tests/api/specs/invoice.py b/silver/tests/api/specs/invoice.py index 86cb8ea3..4e6c81d6 100644 --- a/silver/tests/api/specs/invoice.py +++ b/silver/tests/api/specs/invoice.py @@ -2,7 +2,6 @@ from decimal import Decimal from django.conf import settings -from django.utils.six import text_type from silver.tests.api.specs.customer import spec_archived_customer from silver.tests.api.specs.document_entry import spec_document_entry, document_entry_definition @@ -22,7 +21,7 @@ }, 'proforma': { 'required': False, - 'expected_input_types': text_type, + 'expected_input_types': str, 'output': lambda invoice: ( spec_proforma_url(invoice.related_document) if invoice.related_document else None ), @@ -47,14 +46,14 @@ ), }, 'customer': { - 'expected_input_types': text_type, + 'expected_input_types': str, 'output': lambda invoice: spec_customer_url(invoice.customer), 'assertions': [ lambda input, invoice, output: input == output ] }, 'provider': { - 'expected_input_types': text_type, + 'expected_input_types': str, 'output': lambda invoice: spec_provider_url(invoice.provider), 'assertions': [ lambda input, invoice, output: input == output @@ -62,7 +61,7 @@ }, 'series': { 'required': False, - 'expected_input_types': text_type, + 'expected_input_types': str, 'output': lambda invoice: invoice.provider.invoice_series, 'assertions': [ lambda input, invoice, output: input == output if input else True @@ -77,7 +76,7 @@ ] }, 'currency': { - 'expected_input_types': text_type, + 'expected_input_types': str, 'output': lambda invoice: invoice.currency, 'assertions': [ lambda input, invoice, output: input == output @@ -85,7 +84,7 @@ }, 'transaction_currency': { 'required': False, - 'expected_input_types': text_type, + 'expected_input_types': str, 'output': lambda invoice: invoice.transaction_currency or invoice.currency, 'assertions': [ lambda input, invoice, output: input == output if input else True @@ -105,7 +104,7 @@ }, 'transaction_xe_rate': { 'required': False, - 'expected_input_types': (int, float, text_type), + 'expected_input_types': (int, float, str), 'output': lambda invoice: ( "%.4f" % invoice.transaction_xe_rate if invoice.transaction_xe_rate else None ), @@ -119,7 +118,7 @@ }, 'state': { 'read_only': True, - 'output': lambda invoice: text_type(invoice.state), + 'output': lambda invoice: invoice.state, 'assertions': [ lambda output, **kw: output in ['draft', 'issued', 'canceled', 'paid'] ] @@ -205,7 +204,7 @@ }, 'sales_tax_percent': { 'required': False, - 'expected_input_types': (int, float, text_type), + 'expected_input_types': (int, float, str), 'output': lambda invoice: "%.2f" % invoice.sales_tax_percent, 'assertions': [ lambda input, invoice, output: ( @@ -216,7 +215,7 @@ }, 'sales_tax_name': { 'required': False, - 'expected_input_types': text_type, + 'expected_input_types': str, 'output': lambda invoice: invoice.sales_tax_name, 'assertions': [ lambda input, invoice, output: input == output if input else output == 'VAT' diff --git a/silver/tests/api/specs/provider.py b/silver/tests/api/specs/provider.py index 155e765a..e2c4a235 100644 --- a/silver/tests/api/specs/provider.py +++ b/silver/tests/api/specs/provider.py @@ -1,19 +1,18 @@ -from django.utils.six import text_type def spec_archived_provider(provider): return { - 'company': text_type(provider.company), - 'email': text_type(provider.email), - 'address_1': text_type(provider.address_1), - 'address_2': text_type(provider.address_2), - 'city': text_type(provider.city), - 'country': text_type(provider.country), - 'state': text_type(provider.state), - 'zip_code': text_type(provider.zip_code), - 'extra': text_type(provider.extra), + 'company': provider.company, + 'email': provider.email, + 'address_1': provider.address_1, + 'address_2': provider.address_2, + 'city': provider.city, + 'country': provider.country, + 'state': provider.state, + 'zip_code': provider.zip_code, + 'extra': provider.extra, 'meta': provider.meta, - 'name': text_type(provider.name), - 'invoice_series': text_type(provider.invoice_series), - 'proforma_series': text_type(provider.proforma_series), + 'name': provider.name, + 'invoice_series': provider.invoice_series, + 'proforma_series': provider.proforma_series, } diff --git a/silver/tests/api/specs/transaction.py b/silver/tests/api/specs/transaction.py index 3b97e88e..236621dd 100644 --- a/silver/tests/api/specs/transaction.py +++ b/silver/tests/api/specs/transaction.py @@ -1,7 +1,6 @@ from decimal import Decimal from django.utils import timezone -from django.utils.six import text_type from silver.tests.api.specs.url import ( spec_invoice_url, spec_customer_url, spec_provider_url, spec_proforma_url, spec_transaction_url, @@ -16,33 +15,33 @@ transaction_definition = ResourceDefinition("transaction", { 'id': { 'read_only': True, - 'output': lambda transaction: text_type(transaction.uuid), + 'output': lambda transaction: transaction.uuid, }, 'url': { 'read_only': True, 'output': lambda transaction: spec_transaction_url(transaction), }, 'customer': { - 'expected_input_types': text_type, + 'expected_input_types': str, 'output': lambda transaction: spec_customer_url(transaction.customer), 'assertions': [ lambda input, transaction, output: input == output ] }, 'provider': { - 'expected_input_types': text_type, + 'expected_input_types': str, 'output': lambda transaction: spec_provider_url(transaction.provider), 'assertions': [ lambda input, transaction, output: input == output ] }, 'currency': { - 'expected_input_types': text_type, + 'expected_input_types': str, 'output': lambda transaction: transaction.currency, }, 'amount': { 'required': False, - 'expected_input_types': (int, float, text_type), + 'expected_input_types': (int, float, str), 'output': lambda transaction: "%.2f" % Decimal(transaction.amount), 'assertions': [ lambda input, transaction, output: ( @@ -53,7 +52,7 @@ }, 'state': { 'read_only': True, - 'output': lambda transaction: text_type(transaction.state), + 'output': lambda transaction: transaction.state, 'assertions': [ lambda input, transaction, output: output in [ 'initial', 'pending', 'settled', 'failed', 'canceled', 'refunded' @@ -61,7 +60,7 @@ ] }, 'invoice': { - 'expected_input_types': text_type, + 'expected_input_types': str, 'output': lambda transaction: ( spec_invoice_url(transaction.invoice) if transaction.invoice else None ), @@ -72,7 +71,7 @@ ] }, 'proforma': { - 'expected_input_types': text_type, + 'expected_input_types': str, 'output': lambda transaction: ( spec_proforma_url(transaction.proforma) if transaction.proforma else None ), @@ -120,7 +119,7 @@ ) }, 'payment_method': { - 'expected_input_types': text_type, + 'expected_input_types': str, 'output': lambda transaction: spec_payment_method_url(transaction.payment_method), 'assertions': [ lambda input, transaction, output: input == output diff --git a/silver/tests/api/specs/utils.py b/silver/tests/api/specs/utils.py index 42ec3d9f..c6f003ce 100644 --- a/silver/tests/api/specs/utils.py +++ b/silver/tests/api/specs/utils.py @@ -1,6 +1,5 @@ import inspect -from django.utils.six import text_type datetime_to_str = lambda input_date: input_date.isoformat()[:-6] + 'Z' @@ -9,7 +8,6 @@ decimal_string_or_none = lambda input_decimal: ( None if input_decimal is None else "%.2f" % input_decimal ) -text_type_or_none = lambda input: text_type(input) if input else None class ResourceDefinition(object): diff --git a/silver/tests/api/test_invoice.py b/silver/tests/api/test_invoice.py index b3db7436..ca1f4f06 100644 --- a/silver/tests/api/test_invoice.py +++ b/silver/tests/api/test_invoice.py @@ -28,7 +28,6 @@ from django.db.models.signals import pre_save from django.utils import timezone -from django.utils.six import text_type from django.conf import settings from silver.models import Invoice, Transaction, DocumentEntry @@ -53,7 +52,7 @@ def test_post_invoice_without_invoice_entries(authenticated_api_client, customer 'customer': customer_url, 'series': None, 'number': None, - 'currency': text_type('RON'), + 'currency': 'RON', 'invoice_entries': [] } @@ -78,10 +77,10 @@ def test_post_invoice_with_invoice_entries(authenticated_api_client): 'customer': customer_url, 'series': None, 'number': None, - 'currency': text_type('RON'), + 'currency': 'RON', 'transaction_xe_rate': 1, 'invoice_entries': [{ - "description": text_type("Page views"), + "description": "Page views", "unit_price": 10.0, "quantity": 20}] } @@ -112,10 +111,10 @@ def test_post_invoice_with_invoice_entries_without_transaction_xe_rate( 'customer': customer_url, 'series': None, 'number': None, - 'currency': text_type('RON'), - 'transaction_currency': text_type(transaction_currency), + 'currency': 'RON', + 'transaction_currency': transaction_currency, 'invoice_entries': [{ - "description": text_type("Page views"), + "description": "Page views", "unit_price": 10.0, "quantity": 20}] } @@ -190,7 +189,7 @@ def test_delete_invoice(authenticated_api_client): def test_add_single_invoice_entry(authenticated_api_client, invoice): url = reverse('invoice-entry-create', kwargs={'document_pk': invoice.pk}) request_data = { - "description": text_type("Page views"), + "description": "Page views", "unit_price": 10.0, "quantity": 20 } @@ -222,9 +221,9 @@ def test_try_to_get_invoice_entries(authenticated_api_client, invoice): def test_add_multiple_invoice_entries(authenticated_api_client, invoice): url = reverse('invoice-entry-create', kwargs={'document_pk': invoice.pk}) request_data = { - "description": text_type("Page views"), + "description": "Page views", "unit_price": 10.0, - "quantity": text_type('20.0'), + "quantity": '20.0', } entries_count = 10 @@ -248,7 +247,7 @@ def test_delete_invoice_entry(authenticated_api_client): url = reverse('invoice-entry-create', kwargs={'document_pk': invoice.pk}) request_data = { - "description": text_type("Page views"), + "description": "Page views", "unit_price": 10.0, "quantity": 20 } @@ -274,7 +273,7 @@ def test_add_invoice_entry_in_issued_state(authenticated_api_client): url = reverse('invoice-entry-create', kwargs={'document_pk': invoice.pk}) request_data = { - "description": text_type("Page views"), + "description": "Page views", "unit_price": 10.0, "quantity": 20 } @@ -298,7 +297,7 @@ def test_add_invoice_entry_in_canceled_state(authenticated_api_client): url = reverse('invoice-entry-create', kwargs={'document_pk': invoice.pk}) request_data = { - "description": text_type("Page views"), + "description": "Page views", "unit_price": 10.0, "quantity": 20 } @@ -322,7 +321,7 @@ def test_add_invoice_entry_in_paid_state(authenticated_api_client): url = reverse('invoice-entry-create', kwargs={'document_pk': invoice.pk}) request_data = { - "description": text_type("Page views"), + "description": "Page views", "unit_price": 10.0, "quantity": 20 } diff --git a/silver/tests/api/test_proforma.py b/silver/tests/api/test_proforma.py index fd2d9e82..9f11efd1 100644 --- a/silver/tests/api/test_proforma.py +++ b/silver/tests/api/test_proforma.py @@ -26,7 +26,6 @@ from django.conf import settings from django.utils import timezone -from django.utils.six import text_type from silver.models import Invoice, Proforma, PDF from silver.fixtures.factories import (AdminUserFactory, CustomerFactory, @@ -56,7 +55,7 @@ def test_post_proforma_without_proforma_entries(self): data = { 'provider': provider_url, 'customer': customer_url, - 'currency': text_type('RON'), + 'currency': 'RON', 'proforma_entries': [] } @@ -80,7 +79,7 @@ def test_post_proforma_without_proforma_entries(self): "cancel_date": None, "sales_tax_name": "VAT", "sales_tax_percent": "1.00", - "currency": text_type("RON"), + "currency": "RON", "transaction_currency": proforma.transaction_currency, "transaction_xe_rate": (str(proforma.transaction_xe_rate) if proforma.transaction_xe_rate else None), @@ -108,10 +107,10 @@ def test_post_proforma_with_proforma_entries(self): 'customer': customer_url, 'series': None, 'number': None, - 'currency': text_type('RON'), + 'currency': 'RON', 'transaction_xe_rate': 1, 'proforma_entries': [{ - "description": text_type("Page views"), + "description": "Page views", "unit_price": 10.0, "quantity": 20 }] @@ -174,7 +173,7 @@ def test_get_proforma(self, mocked_settings): "cancel_date": None, "sales_tax_name": "VAT", "sales_tax_percent": '1.00', - "currency": text_type("RON"), + "currency": "RON", "transaction_currency": proforma.transaction_currency, "transaction_xe_rate": ("%.4f" % proforma.transaction_xe_rate if proforma.transaction_xe_rate else None), @@ -200,7 +199,7 @@ def test_add_single_proforma_entry(self): url = reverse('proforma-entry-create', kwargs={'document_pk': proforma.pk}) request_data = { - "description": text_type("Page views"), + "description": "Page views", "unit_price": 10.0, "quantity": 20 } @@ -232,7 +231,7 @@ def test_add_multiple_proforma_entries(self): url = reverse('proforma-entry-create', kwargs={'document_pk': proforma.pk}) request_data = { - "description": text_type("Page views"), + "description": "Page views", "unit_price": 10.0, "quantity": 20 } @@ -258,7 +257,7 @@ def test_delete_proforma_entry(self): url = reverse('proforma-entry-create', kwargs={'document_pk': proforma.pk}) entry_data = { - "description": text_type("Page views"), + "description": "Page views", "unit_price": 10.0, "quantity": 20 } @@ -283,7 +282,7 @@ def test_add_proforma_entry_in_issued_state(self): url = reverse('proforma-entry-create', kwargs={'document_pk': proforma.pk}) entry_data = { - "description": text_type("Page views"), + "description": "Page views", "unit_price": 10.0, "quantity": 20 } @@ -306,7 +305,7 @@ def test_add_proforma_entry_in_canceled_state(self): url = reverse('proforma-entry-create', kwargs={'document_pk': proforma.pk}) entry_data = { - "description": text_type("Page views"), + "description": "Page views", "unit_price": 10.0, "quantity": 20 } @@ -329,7 +328,7 @@ def test_add_proforma_entry_in_paid_state(self): url = reverse('proforma-entry-create', kwargs={'document_pk': proforma.pk}) entry_data = { - "description": text_type("Page views"), + "description": "Page views", "unit_price": 10.0, "quantity": 20 } diff --git a/silver/tests/commands/test_generate_docs.py b/silver/tests/commands/test_generate_docs.py index 258e5e1b..453ad343 100644 --- a/silver/tests/commands/test_generate_docs.py +++ b/silver/tests/commands/test_generate_docs.py @@ -17,11 +17,12 @@ import datetime as dt from decimal import Decimal +from io import StringIO + from mock import patch, PropertyMock, MagicMock from django.core.management import call_command from django.test import TestCase -from django.utils.six import StringIO from silver.management.commands.generate_docs import date as generate_docs_date from silver.models import (Proforma, DocumentEntry, Invoice, Subscription, Customer, Plan, diff --git a/silver/tests/commands/test_generate_docs_args.py b/silver/tests/commands/test_generate_docs_args.py index 01317f21..dd84b402 100644 --- a/silver/tests/commands/test_generate_docs_args.py +++ b/silver/tests/commands/test_generate_docs_args.py @@ -16,10 +16,10 @@ from __future__ import absolute_import from decimal import Decimal +from io import StringIO from django.core.management import call_command from django.test import TestCase -from django.utils.six import StringIO from silver.management.commands.generate_docs import date as generate_docs_date from silver.models import (Plan) From 56ec7a9a96e32f072e16745ab7dd89e55fde0bc0 Mon Sep 17 00:00:00 2001 From: Adam Charnock Date: Sat, 9 Jan 2021 11:22:12 +0000 Subject: [PATCH 03/21] Fixing RemovedInDjango40Warning: Simple replace of force_text() -> force_str() Warning was: RemovedInDjango40Warning: force_text() is deprecated in favor of force_str(). --- silver/admin.py | 8 ++++---- silver/api/views/subscription_views.py | 4 ++-- silver/models/documents/base.py | 4 ++-- silver/models/transactions/transaction.py | 4 ++-- silver/payment_processors/base.py | 4 ++-- silver/tests/admin/test_invoice.py | 4 ++-- silver/tests/admin/test_proforma.py | 4 ++-- silver/tests/api/test_documents.py | 8 ++++---- silver/tests/api/test_payments.py | 14 +++++++------- silver/tests/api/test_transactions.py | 14 +++++++------- silver/utils/payments.py | 6 +++--- 11 files changed, 37 insertions(+), 37 deletions(-) diff --git a/silver/admin.py b/silver/admin.py index def271cb..eae51a73 100644 --- a/silver/admin.py +++ b/silver/admin.py @@ -43,7 +43,7 @@ from django.shortcuts import render from django.urls import reverse from django.utils import timezone -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.utils.html import escape, conditional_escape from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ @@ -284,7 +284,7 @@ def perform_action(self, request, action, queryset): user_id=request.user.id, content_type_id=ContentType.objects.get_for_model(entry).pk, object_id=entry.id, - object_repr=force_text(entry), + object_repr=force_str(entry), action_flag=CHANGE, change_message='{action} action initiated by user.'.format( action=action.replace('_', ' ').strip().capitalize() @@ -778,7 +778,7 @@ def _call_method_on_queryset(self, request, method, queryset, action): user_id=request.user.id, content_type_id=ContentType.objects.get_for_model(document).pk, object_id=document.id, - object_repr=force_text(document), + object_repr=force_str(document), action_flag=CHANGE, change_message='{action} action initiated by user.'.format( action=action.replace('_', ' ').strip().capitalize() @@ -788,7 +788,7 @@ def _call_method_on_queryset(self, request, method, queryset, action): results[document]['result'] = mark_safe(error) results[document]['success'] = False except ValueError as error: - results[document]['result'] = force_text(error) + results[document]['result'] = force_str(error) results[document]['success'] = False except AttributeError: results[document]['success'] = False diff --git a/silver/api/views/subscription_views.py b/silver/api/views/subscription_views.py index 9a6ec9f8..38973b1e 100644 --- a/silver/api/views/subscription_views.py +++ b/silver/api/views/subscription_views.py @@ -23,7 +23,7 @@ from django_filters.rest_framework import DjangoFilterBackend from django.utils import timezone -from django.utils.encoding import force_text +from django.utils.encoding import force_str from rest_framework import generics, permissions, status from rest_framework.generics import get_object_or_404 @@ -74,7 +74,7 @@ def post(self, request, *args, **kwargs): customer_pk = self.kwargs.get('customer_pk', None) url = reverse('customer-detail', kwargs={'customer_pk': customer_pk}, request=request) - request.data.update({force_text('customer'): force_text(url)}) + request.data.update({force_str('customer'): force_str(url)}) return super(SubscriptionList, self).post(request, *args, **kwargs) diff --git a/silver/models/documents/base.py b/silver/models/documents/base.py index a9bfc5f3..113b6b68 100644 --- a/silver/models/documents/base.py +++ b/silver/models/documents/base.py @@ -34,7 +34,7 @@ from django.db.models import Max, ForeignKey, F from django.template.loader import select_template from django.utils import timezone -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.utils.text import slugify from django.utils.translation import ugettext_lazy as _ from django.utils.module_loading import import_string @@ -59,7 +59,7 @@ def documents_pdf_path(document, filename): path = '{prefix}{company}/{doc_name}/{date}/{filename}'.format( - company=slugify(force_text( + company=slugify(force_str( document.provider.company or document.provider.name)), date=document.issue_date.strftime('%Y/%m'), doc_name=('%ss' % document.__class__.__name__).lower(), diff --git a/silver/models/transactions/transaction.py b/silver/models/transactions/transaction.py index 5a47bac3..1c87c06f 100644 --- a/silver/models/transactions/transaction.py +++ b/silver/models/transactions/transaction.py @@ -30,7 +30,7 @@ from django.db.models.signals import post_save from django.dispatch import receiver from django.utils import timezone -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.utils.translation import ugettext_lazy as _ from silver.models import Invoice, Proforma @@ -287,7 +287,7 @@ def update_document_state(self): self.document.pay() def __str__(self): - return force_text(self.uuid) + return force_str(self.uuid) @receiver(post_transition) diff --git a/silver/payment_processors/base.py b/silver/payment_processors/base.py index 911a6296..ff6b366b 100644 --- a/silver/payment_processors/base.py +++ b/silver/payment_processors/base.py @@ -17,7 +17,7 @@ from django.conf import settings from django.template.loader import select_template from django.utils.deconstruct import deconstructible -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.utils.module_loading import import_string from django.utils.text import slugify @@ -107,7 +107,7 @@ def __repr__(self): return self.name def __str__(self): - return force_text(self.name) + return force_str(self.name) def __eq__(self, other): return self.__class__ is other.__class__ diff --git a/silver/tests/admin/test_invoice.py b/silver/tests/admin/test_invoice.py index 49c2f54d..ddefd878 100644 --- a/silver/tests/admin/test_invoice.py +++ b/silver/tests/admin/test_invoice.py @@ -24,7 +24,7 @@ from django.contrib.contenttypes.models import ContentType from django.urls import reverse from django.test import TestCase, Client -from django.utils.encoding import force_text +from django.utils.encoding import force_str from silver.fixtures.factories import InvoiceFactory @@ -77,7 +77,7 @@ def test_actions_log_entries(self): user_id=self.user.pk, content_type_id=ContentType.objects.get_for_model(invoice).pk, object_id=invoice.pk, - object_repr=force_text(invoice), + object_repr=force_str(invoice), action_flag=CHANGE, change_message='{action} action initiated by user.'.format( action=action.capitalize().replace('_', ' ') diff --git a/silver/tests/admin/test_proforma.py b/silver/tests/admin/test_proforma.py index ae9ff9b8..9381349e 100644 --- a/silver/tests/admin/test_proforma.py +++ b/silver/tests/admin/test_proforma.py @@ -21,7 +21,7 @@ from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.urls import reverse -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.test import TestCase, Client from django_fsm import TransitionNotAllowed @@ -76,7 +76,7 @@ def test_actions_log_entries(self): user_id=self.user.pk, content_type_id=ContentType.objects.get_for_model(proforma).pk, object_id=proforma.pk, - object_repr=force_text(proforma), + object_repr=force_str(proforma), action_flag=CHANGE, change_message='{action} action initiated by user.'.format( action=action.capitalize().replace('_', ' ') diff --git a/silver/tests/api/test_documents.py b/silver/tests/api/test_documents.py index d04e6334..33a39b63 100644 --- a/silver/tests/api/test_documents.py +++ b/silver/tests/api/test_documents.py @@ -20,7 +20,7 @@ from freezegun import freeze_time from django.test import override_settings -from django.utils.encoding import force_text +from django.utils.encoding import force_str from rest_framework import status from rest_framework.reverse import reverse @@ -44,7 +44,7 @@ def setUp(self): self.client.force_authenticate(user=admin_user) def _get_expected_data(self, document, transactions=None): - kind = force_text(document.kind.lower()) + kind = force_str(document.kind.lower()) transactions = [{ u'id': u'%s' % transaction.uuid, u'url': build_absolute_test_url(reverse('transaction-detail', @@ -84,8 +84,8 @@ def _get_expected_data(self, document, transactions=None): [document.provider.id])), u'customer': build_absolute_test_url(reverse('customer-detail', [document.customer.id])), - u'due_date': force_text(document.due_date) if document.due_date else None, - u'issue_date': force_text(document.issue_date) if document.issue_date else None, + u'due_date': force_str(document.due_date) if document.due_date else None, + u'issue_date': force_str(document.issue_date) if document.issue_date else None, u'paid_date': document.paid_date, u'cancel_date': document.cancel_date, u'sales_tax_name': document.sales_tax_name, diff --git a/silver/tests/api/test_payments.py b/silver/tests/api/test_payments.py index 5a502c87..100e31bb 100644 --- a/silver/tests/api/test_payments.py +++ b/silver/tests/api/test_payments.py @@ -20,7 +20,7 @@ from django.utils import timezone from django.template.loader import render_to_string from django.test import override_settings -from django.utils.encoding import force_text +from django.utils.encoding import force_str from rest_framework import status from rest_framework.test import APITestCase @@ -47,7 +47,7 @@ def test_pay_transaction_view_expired(self): response = self.client.get(url) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(force_text(response.content), + self.assertEqual(force_str(response.content), render_to_string('transactions/expired_payment.html', { 'document': transaction.document, })) @@ -57,7 +57,7 @@ def test_pay_transaction_view_invalid_state(self): response = self.client.get(get_payment_url(transaction, None)) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(force_text(response.content), + self.assertEqual(force_str(response.content), render_to_string('transactions/complete_payment.html', { 'transaction': transaction, 'document': transaction.document, @@ -70,7 +70,7 @@ def test_pay_transaction_view_not_consumable_transaction(self): response = self.client.get(get_payment_url(transaction, None)) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(force_text(response.content), + self.assertEqual(force_str(response.content), render_to_string('transactions/expired_payment.html', { 'document': transaction.document, })) @@ -88,7 +88,7 @@ def get_view(processor, transaction, request): response = self.client.get(get_payment_url(transaction, None)) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(force_text(response.content), + self.assertEqual(force_str(response.content), render_to_string('transactions/expired_payment.html', { 'document': transaction.document, })) @@ -106,7 +106,7 @@ def get_view(processor, transaction, request): response = self.client.get(get_payment_url(transaction, None)) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(force_text(response.content), + self.assertEqual(force_str(response.content), render_to_string('transactions/expired_payment.html', { 'document': transaction.document, })) @@ -130,7 +130,7 @@ def test_complete_payment_view_without_return_url(self): response = self.client.get(get_payment_complete_url(transaction, None)) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(force_text(response.content), + self.assertEqual(force_str(response.content), render_to_string('transactions/complete_payment.html', { 'expired': False, 'transaction': transaction, diff --git a/silver/tests/api/test_transactions.py b/silver/tests/api/test_transactions.py index e5459a40..629109e5 100644 --- a/silver/tests/api/test_transactions.py +++ b/silver/tests/api/test_transactions.py @@ -22,7 +22,7 @@ from django.utils import timezone from django.test import override_settings -from django.utils.encoding import force_text +from django.utils.encoding import force_str from rest_framework import status from rest_framework.reverse import reverse as _reverse @@ -132,7 +132,7 @@ def test_add_transaction(self): self.assertEqual(response.data['valid_until'][:-1], valid_until.isoformat()) self.assertEqual(response.data['can_be_consumed'], True) self.assertEqual(response.data['amount'], - force_text(invoice.total_in_transaction_currency)) + force_str(invoice.total_in_transaction_currency)) self.assertEqual(response.data['invoice'], invoice_url) self.assertEqual(response.data['proforma'], proforma_url) self.assertEqual(response.data['currency'], currency) @@ -301,7 +301,7 @@ def test_add_transaction_without_currency_and_amount(self): self.assertEqual(response.data['valid_until'][:-1], valid_until.isoformat()) self.assertEqual(response.data['can_be_consumed'], True) self.assertEqual(response.data['amount'], - force_text(invoice.total_in_transaction_currency)) + force_str(invoice.total_in_transaction_currency)) self.assertEqual(response.data['invoice'], invoice_url) self.assertEqual(response.data['proforma'], proforma_url) self.assertEqual(response.data['currency'], invoice.transaction_currency) @@ -665,15 +665,15 @@ def _transaction_data(self, transaction): mocked_token.return_value = 'token' return OrderedDict([ - ('id', force_text(transaction.uuid)), + ('id', force_str(transaction.uuid)), ('url', reverse('transaction-detail', kwargs={'customer_pk': customer.id, 'transaction_uuid': transaction.uuid})), ('customer', reverse('customer-detail', args=[customer.pk])), ('provider', reverse('provider-detail', args=[provider.pk])), - ('amount', force_text(Decimal('0.00') + transaction.amount)), - ('currency', force_text(transaction.currency)), - ('state', force_text(transaction.state)), + ('amount', force_str(Decimal('0.00') + transaction.amount)), + ('currency', force_str(transaction.currency)), + ('state', force_str(transaction.state)), ('proforma', reverse('proforma-detail', args=[proforma.pk])), ('invoice', reverse('invoice-detail', args=[invoice.pk])), ('can_be_consumed', transaction.can_be_consumed), diff --git a/silver/utils/payments.py b/silver/utils/payments.py index 9b09a4c4..2df48eca 100644 --- a/silver/utils/payments.py +++ b/silver/utils/payments.py @@ -23,15 +23,15 @@ from furl import furl from django.conf import settings -from django.utils.encoding import force_text +from django.utils.encoding import force_str from rest_framework.reverse import reverse def _get_jwt_token(transaction): valid_until = datetime.utcnow() + settings.SILVER_PAYMENT_TOKEN_EXPIRATION - data = {'transaction': force_text(transaction.uuid), 'exp': valid_until} - return force_text(jwt.encode(data, settings.PAYMENT_METHOD_SECRET)) + data = {'transaction': force_str(transaction.uuid), 'exp': valid_until} + return force_str(jwt.encode(data, settings.PAYMENT_METHOD_SECRET)) def get_payment_url(transaction, request): From a9317029770a544d19ded84b006b5b8a882c4273 Mon Sep 17 00:00:00 2001 From: Adam Charnock Date: Sat, 9 Jan 2021 11:23:43 +0000 Subject: [PATCH 04/21] Fixing DRF MigrationNotice. filter_class -> filterset_class Warning was: MigrationNotice: `XXX.filter_class` attribute should be renamed `filterset_class`. See: https://django-filter.readthedocs.io/en/master/guide/migration.html --- silver/api/views/billing_entities_views.py | 4 ++-- silver/api/views/documents_views.py | 6 +++--- silver/api/views/payment_method_views.py | 2 +- silver/api/views/plan_views.py | 2 +- silver/api/views/subscription_views.py | 4 ++-- silver/api/views/transaction_views.py | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/silver/api/views/billing_entities_views.py b/silver/api/views/billing_entities_views.py index 4c9b547c..1984b45a 100644 --- a/silver/api/views/billing_entities_views.py +++ b/silver/api/views/billing_entities_views.py @@ -32,7 +32,7 @@ class CustomerList(generics.ListCreateAPIView): serializer_class = CustomerSerializer queryset = Customer.objects.all() filter_backends = (DjangoFilterBackend,) - filter_class = CustomerFilter + filterset_class = CustomerFilter class CustomerDetail(generics.RetrieveUpdateDestroyAPIView): @@ -53,7 +53,7 @@ class ProviderListCreate(ListBulkCreateAPIView): serializer_class = ProviderSerializer queryset = Provider.objects.all() filter_backends = (DjangoFilterBackend,) - filter_class = ProviderFilter + filterset_class = ProviderFilter class ProviderRetrieveUpdateDestroy(generics.RetrieveUpdateDestroyAPIView): diff --git a/silver/api/views/documents_views.py b/silver/api/views/documents_views.py index c9e710f1..1d311599 100644 --- a/silver/api/views/documents_views.py +++ b/silver/api/views/documents_views.py @@ -40,7 +40,7 @@ class InvoiceListCreate(generics.ListCreateAPIView): .select_related('related_document')\ .prefetch_related('invoice_transactions') filter_backends = (DjangoFilterBackend,) - filter_class = InvoiceFilter + filterset_class = InvoiceFilter class InvoiceRetrieveUpdate(generics.RetrieveUpdateAPIView): @@ -231,7 +231,7 @@ class ProformaListCreate(generics.ListCreateAPIView): .select_related('related_document')\ .prefetch_related('proforma_transactions') filter_backends = (DjangoFilterBackend,) - filter_class = ProformaFilter + filterset_class = ProformaFilter class ProformaRetrieveUpdate(generics.RetrieveUpdateAPIView): @@ -362,7 +362,7 @@ def put(self, request, *args, **kwargs): class DocumentList(ListAPIView): permission_classes = (permissions.IsAuthenticated,) serializer_class = DocumentSerializer - filter_class = BillingDocumentFilter + filterset_class = BillingDocumentFilter filter_backends = (filters.OrderingFilter, DjangoFilterBackend) ordering_fields = ('due_date', ) ordering = ('-due_date', '-number') diff --git a/silver/api/views/payment_method_views.py b/silver/api/views/payment_method_views.py index a07a8ce0..2d3dc970 100644 --- a/silver/api/views/payment_method_views.py +++ b/silver/api/views/payment_method_views.py @@ -56,7 +56,7 @@ class PaymentMethodList(ListCreateAPIView): permission_classes = (permissions.IsAuthenticated,) serializer_class = PaymentMethodSerializer filter_backends = (DjangoFilterBackend,) - filter_class = PaymentMethodFilter + filterset_class = PaymentMethodFilter def get_queryset(self): return PaymentMethod.objects.filter(customer=self.customer) diff --git a/silver/api/views/plan_views.py b/silver/api/views/plan_views.py index 95614ca8..cacb457b 100644 --- a/silver/api/views/plan_views.py +++ b/silver/api/views/plan_views.py @@ -33,7 +33,7 @@ class PlanList(generics.ListCreateAPIView): serializer_class = PlanSerializer queryset = Plan.objects.all().prefetch_related('metered_features') filter_backends = (DjangoFilterBackend,) - filter_class = PlanFilter + filterset_class = PlanFilter class PlanDetail(generics.RetrieveDestroyAPIView): diff --git a/silver/api/views/subscription_views.py b/silver/api/views/subscription_views.py index 38973b1e..e6dd7369 100644 --- a/silver/api/views/subscription_views.py +++ b/silver/api/views/subscription_views.py @@ -46,7 +46,7 @@ class MeteredFeatureList(generics.ListCreateAPIView): serializer_class = MeteredFeatureSerializer queryset = MeteredFeature.objects.all() filter_backends = (DjangoFilterBackend,) - filter_class = MeteredFeaturesFilter + filterset_class = MeteredFeaturesFilter class MeteredFeatureDetail(generics.RetrieveAPIView): @@ -63,7 +63,7 @@ class SubscriptionList(generics.ListCreateAPIView): permission_classes = (permissions.IsAuthenticated,) serializer_class = SubscriptionSerializer filter_backends = (DjangoFilterBackend,) - filter_class = SubscriptionFilter + filterset_class = SubscriptionFilter def get_queryset(self): customer_pk = self.kwargs.get('customer_pk', None) diff --git a/silver/api/views/transaction_views.py b/silver/api/views/transaction_views.py index ed5a8e48..b0ec5d03 100644 --- a/silver/api/views/transaction_views.py +++ b/silver/api/views/transaction_views.py @@ -35,7 +35,7 @@ class TransactionList(ListCreateAPIView): permission_classes = (permissions.IsAuthenticated,) serializer_class = TransactionSerializer filter_backends = (DjangoFilterBackend,) - filter_class = TransactionFilter + filterset_class = TransactionFilter def get_queryset(self): customer_pk = self.kwargs.get('customer_pk', None) From c85cba892f206a9f2ab7960de3365716c0a6cd19 Mon Sep 17 00:00:00 2001 From: Adam Charnock Date: Sat, 9 Jan 2021 11:26:17 +0000 Subject: [PATCH 05/21] Fixing RemovedInDjango40Warning. url -> re_path Warning was: RemovedInDjango40Warning: django.conf.urls.url() is deprecated in favor of django.urls.re_path(). --- silver/api/urls.py | 168 ++++++++++++++++++++++----------------------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/silver/api/urls.py b/silver/api/urls.py index 1f396e1b..afa7db2c 100644 --- a/silver/api/urls.py +++ b/silver/api/urls.py @@ -14,7 +14,7 @@ from __future__ import absolute_import -from django.conf.urls import url +from django.conf.urls import re_path from silver import views as silver_views from silver.api.views import billing_entities_views, documents_views, payment_method_views, \ @@ -22,97 +22,97 @@ urlpatterns = [ - url(r'^customers/$', - billing_entities_views.CustomerList.as_view(), name='customer-list'), - url(r'^customers/(?P[0-9]+)/$', - billing_entities_views.CustomerDetail.as_view(), name='customer-detail'), + re_path(r'^customers/$', + billing_entities_views.CustomerList.as_view(), name='customer-list'), + re_path(r'^customers/(?P[0-9]+)/$', + billing_entities_views.CustomerDetail.as_view(), name='customer-detail'), - url(r'^customers/(?P[0-9]+)/subscriptions/$', - subscription_views.SubscriptionList.as_view(), name='subscription-list'), - url(r'^customers/(?P[0-9]+)/subscriptions/(?P[0-9]+)/$', - subscription_views.SubscriptionDetail.as_view(), name='subscription-detail'), - url(r'^customers/(?P[0-9]+)/subscriptions/(?P[0-9]+)/metered-features/(?P([^/])+)/$', - subscription_views.MeteredFeatureUnitsLogDetail.as_view(), name='mf-log-units'), - url(r'^customers/(?P[0-9]+)/subscriptions/(?P[0-9]+)/activate/$', - subscription_views.SubscriptionActivate.as_view(), name='sub-activate'), - url(r'^customers/(?P[0-9]+)/subscriptions/(?P[0-9]+)/cancel/$', - subscription_views.SubscriptionCancel.as_view(), name='sub-cancel'), - url(r'^customers/(?P[0-9]+)/subscriptions/(?P[0-9]+)/reactivate/$', - subscription_views.SubscriptionReactivate.as_view(), name='sub-reactivate'), + re_path(r'^customers/(?P[0-9]+)/subscriptions/$', + subscription_views.SubscriptionList.as_view(), name='subscription-list'), + re_path(r'^customers/(?P[0-9]+)/subscriptions/(?P[0-9]+)/$', + subscription_views.SubscriptionDetail.as_view(), name='subscription-detail'), + re_path(r'^customers/(?P[0-9]+)/subscriptions/(?P[0-9]+)/metered-features/(?P([^/])+)/$', + subscription_views.MeteredFeatureUnitsLogDetail.as_view(), name='mf-log-units'), + re_path(r'^customers/(?P[0-9]+)/subscriptions/(?P[0-9]+)/activate/$', + subscription_views.SubscriptionActivate.as_view(), name='sub-activate'), + re_path(r'^customers/(?P[0-9]+)/subscriptions/(?P[0-9]+)/cancel/$', + subscription_views.SubscriptionCancel.as_view(), name='sub-cancel'), + re_path(r'^customers/(?P[0-9]+)/subscriptions/(?P[0-9]+)/reactivate/$', + subscription_views.SubscriptionReactivate.as_view(), name='sub-reactivate'), - url(r'^customers/(?P[0-9]+)/payment_methods/$', - payment_method_views.PaymentMethodList.as_view(), name='payment-method-list'), - url(r'^customers/(?P[0-9]+)/payment_methods/(?P[0-9]+)/$', - payment_method_views.PaymentMethodDetail.as_view(), name='payment-method-detail'), - url(r'^customers/(?P[0-9]+)/payment_methods/(?P[0-9]+)/(?P(cancel))_request/$', - payment_method_views.PaymentMethodAction.as_view(), name='payment-method-action'), + re_path(r'^customers/(?P[0-9]+)/payment_methods/$', + payment_method_views.PaymentMethodList.as_view(), name='payment-method-list'), + re_path(r'^customers/(?P[0-9]+)/payment_methods/(?P[0-9]+)/$', + payment_method_views.PaymentMethodDetail.as_view(), name='payment-method-detail'), + re_path(r'^customers/(?P[0-9]+)/payment_methods/(?P[0-9]+)/(?P(cancel))_request/$', + payment_method_views.PaymentMethodAction.as_view(), name='payment-method-action'), - url(r'^customers/(?P[0-9]+)/payment_methods/(?P[0-9]+)/transactions/$', - transaction_views.TransactionList.as_view(), name='payment-method-transaction-list'), - url(r'^customers/(?P[0-9]+)/transactions/$', - transaction_views.TransactionList.as_view(), name='transaction-list'), - url(r'^customers/(?P[0-9]+)/transactions/(?P[0-9a-z-]+)/$', - transaction_views.TransactionDetail.as_view(), name='transaction-detail'), - url(r'^customers/(?P[0-9]+)/transactions/(?P[0-9a-z-]+)/(?P(cancel))_request/$', - transaction_views.TransactionAction.as_view(), name='transaction-action'), + re_path(r'^customers/(?P[0-9]+)/payment_methods/(?P[0-9]+)/transactions/$', + transaction_views.TransactionList.as_view(), name='payment-method-transaction-list'), + re_path(r'^customers/(?P[0-9]+)/transactions/$', + transaction_views.TransactionList.as_view(), name='transaction-list'), + re_path(r'^customers/(?P[0-9]+)/transactions/(?P[0-9a-z-]+)/$', + transaction_views.TransactionDetail.as_view(), name='transaction-detail'), + re_path(r'^customers/(?P[0-9]+)/transactions/(?P[0-9a-z-]+)/(?P(cancel))_request/$', + transaction_views.TransactionAction.as_view(), name='transaction-action'), - url(r'^payment_processors/$', - payment_method_views.PaymentProcessorList.as_view(), name='payment-processor-list'), - url(r'^payment_processors/(?P[a-zA-Z\-\_]+)/$', - payment_method_views.PaymentProcessorDetail.as_view(), name='payment-processor-detail'), + re_path(r'^payment_processors/$', + payment_method_views.PaymentProcessorList.as_view(), name='payment-processor-list'), + re_path(r'^payment_processors/(?P[a-zA-Z\-\_]+)/$', + payment_method_views.PaymentProcessorDetail.as_view(), name='payment-processor-detail'), - url(r'^plans/$', - plan_views.PlanList.as_view(), name='plan-list'), - url(r'^plans/(?P[0-9]+)/$', - plan_views.PlanDetail.as_view(), name='plan-detail'), - url(r'plans/(?P[0-9]+)/metered-features/$', - plan_views.PlanMeteredFeatures.as_view(), name='plans-metered-features'), + re_path(r'^plans/$', + plan_views.PlanList.as_view(), name='plan-list'), + re_path(r'^plans/(?P[0-9]+)/$', + plan_views.PlanDetail.as_view(), name='plan-detail'), + re_path(r'plans/(?P[0-9]+)/metered-features/$', + plan_views.PlanMeteredFeatures.as_view(), name='plans-metered-features'), - url(r'^metered-features/$', - subscription_views.MeteredFeatureList.as_view(), name='metered-feature-list'), + re_path(r'^metered-features/$', + subscription_views.MeteredFeatureList.as_view(), name='metered-feature-list'), - url(r'^providers/$', - billing_entities_views.ProviderListCreate.as_view(), name='provider-list'), - url(r'^providers/(?P[0-9]+)/$', - billing_entities_views.ProviderRetrieveUpdateDestroy.as_view(), name='provider-detail'), + re_path(r'^providers/$', + billing_entities_views.ProviderListCreate.as_view(), name='provider-list'), + re_path(r'^providers/(?P[0-9]+)/$', + billing_entities_views.ProviderRetrieveUpdateDestroy.as_view(), name='provider-detail'), - url(r'^product-codes/$', - product_code_views.ProductCodeListCreate.as_view(), name='productcode-list'), - url(r'^product-codes/(?P[0-9]+)/$', - product_code_views.ProductCodeRetrieveUpdate.as_view(), name='productcode-detail'), + re_path(r'^product-codes/$', + product_code_views.ProductCodeListCreate.as_view(), name='productcode-list'), + re_path(r'^product-codes/(?P[0-9]+)/$', + product_code_views.ProductCodeRetrieveUpdate.as_view(), name='productcode-detail'), - url(r'^invoices/$', - documents_views.InvoiceListCreate.as_view(), name='invoice-list'), - url(r'^invoices/(?P[0-9]+)/$', - documents_views.InvoiceRetrieveUpdate.as_view(), name='invoice-detail'), - url(r'^invoices/(?P[0-9]+)/entries/$', - documents_views.InvoiceEntryCreate.as_view(), name='invoice-entry-create'), - url(r'^invoices/(?P[0-9]+)/entries/(?P[0-9]+)/$', - documents_views.InvoiceEntryUpdateDestroy.as_view(), name='invoice-entry-update'), - url(r'^invoices/(?P[0-9]+)/state/$', - documents_views.InvoiceStateHandler.as_view(), name='invoice-state'), - url(r'^invoices/(?P\d+).pdf$', - silver_views.invoice_pdf, name='invoice-pdf'), + re_path(r'^invoices/$', + documents_views.InvoiceListCreate.as_view(), name='invoice-list'), + re_path(r'^invoices/(?P[0-9]+)/$', + documents_views.InvoiceRetrieveUpdate.as_view(), name='invoice-detail'), + re_path(r'^invoices/(?P[0-9]+)/entries/$', + documents_views.InvoiceEntryCreate.as_view(), name='invoice-entry-create'), + re_path(r'^invoices/(?P[0-9]+)/entries/(?P[0-9]+)/$', + documents_views.InvoiceEntryUpdateDestroy.as_view(), name='invoice-entry-update'), + re_path(r'^invoices/(?P[0-9]+)/state/$', + documents_views.InvoiceStateHandler.as_view(), name='invoice-state'), + re_path(r'^invoices/(?P\d+).pdf$', + silver_views.invoice_pdf, name='invoice-pdf'), - url(r'^proformas/$', - documents_views.ProformaListCreate.as_view(), name='proforma-list'), - url(r'^proformas/(?P[0-9]+)/$', - documents_views.ProformaRetrieveUpdate.as_view(), name='proforma-detail'), - url(r'^proformas/(?P[0-9]+)/entries/$', - documents_views.ProformaEntryCreate.as_view(), name='proforma-entry-create'), - url(r'^proformas/(?P[0-9]+)/entries/(?P[0-9]+)/$', - documents_views.ProformaEntryUpdateDestroy.as_view(), - name='proforma-entry-update'), - url(r'^proformas/(?P[0-9]+)/state/$', - documents_views.ProformaStateHandler.as_view(), name='proforma-state'), - url(r'^proformas/(?P[0-9]+)/invoice/$', - documents_views.ProformaInvoiceRetrieveCreate.as_view(), - name='proforma-invoice'), - url(r'^proformas/(?P\d+).pdf$', - silver_views.proforma_pdf, name='proforma-pdf'), - url(r'^pdfs/(?P[0-9]+)/$', - documents_views.PDFRetrieve.as_view(), - name='pdf'), - url(r'^documents/$', - documents_views.DocumentList.as_view(), name='document-list') + re_path(r'^proformas/$', + documents_views.ProformaListCreate.as_view(), name='proforma-list'), + re_path(r'^proformas/(?P[0-9]+)/$', + documents_views.ProformaRetrieveUpdate.as_view(), name='proforma-detail'), + re_path(r'^proformas/(?P[0-9]+)/entries/$', + documents_views.ProformaEntryCreate.as_view(), name='proforma-entry-create'), + re_path(r'^proformas/(?P[0-9]+)/entries/(?P[0-9]+)/$', + documents_views.ProformaEntryUpdateDestroy.as_view(), + name='proforma-entry-update'), + re_path(r'^proformas/(?P[0-9]+)/state/$', + documents_views.ProformaStateHandler.as_view(), name='proforma-state'), + re_path(r'^proformas/(?P[0-9]+)/invoice/$', + documents_views.ProformaInvoiceRetrieveCreate.as_view(), + name='proforma-invoice'), + re_path(r'^proformas/(?P\d+).pdf$', + silver_views.proforma_pdf, name='proforma-pdf'), + re_path(r'^pdfs/(?P[0-9]+)/$', + documents_views.PDFRetrieve.as_view(), + name='pdf'), + re_path(r'^documents/$', + documents_views.DocumentList.as_view(), name='document-list') ] From b79e9672f1c7c82daa4350ba126359b1b52ef15b Mon Sep 17 00:00:00 2001 From: Adam Charnock Date: Sat, 9 Jan 2021 11:27:04 +0000 Subject: [PATCH 06/21] Fixing RemovedInDjango40Warning. ugettext_lazy -> gettext_lazy Warning was: RemovedInDjango40Warning: django.utils.translation.ugettext_lazy() is deprecated in favor of django.utils.translation.gettext_lazy(). --- silver/admin.py | 2 +- silver/models/billing_entities/provider.py | 2 +- silver/models/documents/base.py | 2 +- silver/models/plans.py | 2 +- silver/models/subscriptions.py | 2 +- silver/models/transactions/transaction.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/silver/admin.py b/silver/admin.py index eae51a73..34c07b7b 100644 --- a/silver/admin.py +++ b/silver/admin.py @@ -46,7 +46,7 @@ from django.utils.encoding import force_str from django.utils.html import escape, conditional_escape from django.utils.safestring import mark_safe -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from silver.documents_generator import DocumentsGenerator from silver.models import ( diff --git a/silver/models/billing_entities/provider.py b/silver/models/billing_entities/provider.py index 476e3f72..1973f753 100644 --- a/silver/models/billing_entities/provider.py +++ b/silver/models/billing_entities/provider.py @@ -24,7 +24,7 @@ from django.dispatch import receiver from django.urls import reverse from django.utils.html import escape -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from silver.models.billing_entities.base import BaseBillingEntity diff --git a/silver/models/documents/base.py b/silver/models/documents/base.py index 113b6b68..d04c3ec1 100644 --- a/silver/models/documents/base.py +++ b/silver/models/documents/base.py @@ -36,7 +36,7 @@ from django.utils import timezone from django.utils.encoding import force_str from django.utils.text import slugify -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from django.utils.module_loading import import_string from silver.currencies import CurrencyConverter, RateNotFound diff --git a/silver/models/plans.py b/silver/models/plans.py index 85f59c9c..07ec60bc 100644 --- a/silver/models/plans.py +++ b/silver/models/plans.py @@ -19,7 +19,7 @@ from django.core.exceptions import ValidationError from django.core.validators import MinValueValidator from django.db import models -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from silver.utils.international import currencies from silver.utils.models import UnsavedForeignKey diff --git a/silver/models/subscriptions.py b/silver/models/subscriptions.py index 767dc2a4..4353a4d1 100644 --- a/silver/models/subscriptions.py +++ b/silver/models/subscriptions.py @@ -37,7 +37,7 @@ from django.template.loader import get_template, render_to_string from django.utils import timezone from django.utils.timezone import utc -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from silver.models.billing_entities import Customer from silver.models.documents import DocumentEntry diff --git a/silver/models/transactions/transaction.py b/silver/models/transactions/transaction.py index 1c87c06f..644c1b0f 100644 --- a/silver/models/transactions/transaction.py +++ b/silver/models/transactions/transaction.py @@ -31,7 +31,7 @@ from django.dispatch import receiver from django.utils import timezone from django.utils.encoding import force_str -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from silver.models import Invoice, Proforma from silver.models.transactions.codes import FAIL_CODES, REFUND_CODES, CANCEL_CODES From 2f48782f824ec5692f6922b3d1841da563eef311 Mon Sep 17 00:00:00 2001 From: Adam Charnock Date: Sat, 9 Jan 2021 11:28:33 +0000 Subject: [PATCH 07/21] Fixing RemovedInDjango40Warning. url -> re_path (second round of this fix) Warning was: RemovedInDjango40Warning: django.conf.urls.url() is deprecated in favor of django.urls.re_path(). --- silver/urls.py | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/silver/urls.py b/silver/urls.py index f210e7d8..7b6073e7 100644 --- a/silver/urls.py +++ b/silver/urls.py @@ -17,7 +17,7 @@ from __future__ import absolute_import -from django.conf.urls import include, url +from django.conf.urls import include, re_path from django.contrib import admin from silver.views import (pay_transaction_view, complete_payment_view, @@ -30,26 +30,26 @@ urlpatterns = [ - url(r'^admin/', admin.site.urls), - url(r'^api-auth/', include('rest_framework.urls', + re_path(r'^admin/', admin.site.urls), + re_path(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), - url(r'', include('silver.api.urls')), - - url(r'pay/(?P[0-9a-zA-Z-_\.]+)/$', - pay_transaction_view, name='payment'), - url(r'pay/(?P[0-9a-zA-Z-_\.]+)/complete$', - complete_payment_view, name='payment-complete'), - - url(r'^autocomplete/invoices/$', - InvoiceAutocomplete.as_view(), name='autocomplete-invoice'), - url(r'^autocomplete/proformas/$', - ProformaAutocomplete.as_view(), name='autocomplete-proforma'), - url(r'^autocomplete/payment-method/$', - PaymentMethodAutocomplete.as_view(), name='autocomplete-payment-method'), - url(r'^autocomplete/plan/$', - PlanAutocomplete.as_view(), name='autocomplete-plan'), - url(r'^autocomplete/customer/$', - CustomerAutocomplete.as_view(), name='autocomplete-customer'), - url(r'^autocomplete/provider/$', - ProviderAutocomplete.as_view(), name='autocomplete-provider'), + re_path(r'', include('silver.api.urls')), + + re_path(r'pay/(?P[0-9a-zA-Z-_\.]+)/$', + pay_transaction_view, name='payment'), + re_path(r'pay/(?P[0-9a-zA-Z-_\.]+)/complete$', + complete_payment_view, name='payment-complete'), + + re_path(r'^autocomplete/invoices/$', + InvoiceAutocomplete.as_view(), name='autocomplete-invoice'), + re_path(r'^autocomplete/proformas/$', + ProformaAutocomplete.as_view(), name='autocomplete-proforma'), + re_path(r'^autocomplete/payment-method/$', + PaymentMethodAutocomplete.as_view(), name='autocomplete-payment-method'), + re_path(r'^autocomplete/plan/$', + PlanAutocomplete.as_view(), name='autocomplete-plan'), + re_path(r'^autocomplete/customer/$', + CustomerAutocomplete.as_view(), name='autocomplete-customer'), + re_path(r'^autocomplete/provider/$', + ProviderAutocomplete.as_view(), name='autocomplete-provider'), ] From e5a7f753868662a02583a30942b501ecce83d91d Mon Sep 17 00:00:00 2001 From: Adam Charnock Date: Sat, 9 Jan 2021 11:30:47 +0000 Subject: [PATCH 08/21] The staticfiles template tag library has been renamed to static. Updating uses to reflect this --- silver/templates/billing_documents/document_pdf_base.html | 2 +- silver/templates/forms/transaction_form.html | 2 +- silver/templates/transactions/complete_payment.html | 2 +- silver/templates/transactions/expired_payment.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/silver/templates/billing_documents/document_pdf_base.html b/silver/templates/billing_documents/document_pdf_base.html index 1be542e6..5e48f801 100644 --- a/silver/templates/billing_documents/document_pdf_base.html +++ b/silver/templates/billing_documents/document_pdf_base.html @@ -1,4 +1,4 @@ -{% load staticfiles %} +{% load static %} diff --git a/silver/templates/forms/transaction_form.html b/silver/templates/forms/transaction_form.html index 865c2e88..ec5219ec 100644 --- a/silver/templates/forms/transaction_form.html +++ b/silver/templates/forms/transaction_form.html @@ -1,4 +1,4 @@ -{% load staticfiles %} +{% load static %} Payment for {{ document.kind }} {{ document.series }}-{{ document.number }} diff --git a/silver/templates/transactions/complete_payment.html b/silver/templates/transactions/complete_payment.html index 923a8baa..9e4bb71f 100644 --- a/silver/templates/transactions/complete_payment.html +++ b/silver/templates/transactions/complete_payment.html @@ -1,4 +1,4 @@ -{% load staticfiles %} +{% load static %} diff --git a/silver/templates/transactions/expired_payment.html b/silver/templates/transactions/expired_payment.html index ccdc8ded..6e325c6d 100644 --- a/silver/templates/transactions/expired_payment.html +++ b/silver/templates/transactions/expired_payment.html @@ -1,4 +1,4 @@ -{% load staticfiles %} +{% load static %} <html> <head> <title> From b8737714a9dccb12220e90fded29a00ec400bbc4 Mon Sep 17 00:00:00 2001 From: Adam Charnock <adam@adamcharnock.com> Date: Sat, 9 Jan 2021 11:39:39 +0000 Subject: [PATCH 09/21] Specifying `algorithms` param to jwt.decode() JWT library now requires the `algorithms` parameter to be specified when decoding. Silver uses the default algorithm when encoding (HS256), so I specified that for the decoding. --- silver/api/serializers/transaction_serializers.py | 3 ++- silver/utils/decorators.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/silver/api/serializers/transaction_serializers.py b/silver/api/serializers/transaction_serializers.py index 4a6bdfe1..62bbe0c3 100644 --- a/silver/api/serializers/transaction_serializers.py +++ b/silver/api/serializers/transaction_serializers.py @@ -50,7 +50,8 @@ def get_url(self, obj, view_name, request, format): def get_object(self, view_name, view_args, view_kwargs): try: transaction_uuid = jwt.decode(view_kwargs['token'], - settings.PAYMENT_METHOD_SECRET)['transaction'] + settings.PAYMENT_METHOD_SECRET, + algorithms=['HS256'])['transaction'] return self.queryset.get(uuid=transaction_uuid) except (jwt.ExpiredSignatureError, jwt.DecodeError, jwt.InvalidTokenError): return None diff --git a/silver/utils/decorators.py b/silver/utils/decorators.py index bc83c3f2..c7bc3d65 100644 --- a/silver/utils/decorators.py +++ b/silver/utils/decorators.py @@ -28,10 +28,12 @@ def decorator(request, token): try: expired = False transaction_uuid = jwt.decode(token, - settings.PAYMENT_METHOD_SECRET)['transaction'] + settings.PAYMENT_METHOD_SECRET, + algorithms=['HS256'])['transaction'] except jwt.ExpiredSignatureError: expired = True transaction_uuid = jwt.decode(token, settings.PAYMENT_METHOD_SECRET, + algorithms=['HS256'], options={'verify_exp': False})['transaction'] try: From cd0289ccebe70140eec68bccf1440d7a15c9fbf5 Mon Sep 17 00:00:00 2001 From: Adam Charnock <adam@adamcharnock.com> Date: Sat, 9 Jan 2021 11:54:32 +0000 Subject: [PATCH 10/21] fail_code was too short for its choices, which is a fatal error in django 3. Making it longer. --- silver/migrations/0054_auto_20210109_1153.py | 19 +++++++++++++++++++ silver/models/transactions/transaction.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 silver/migrations/0054_auto_20210109_1153.py diff --git a/silver/migrations/0054_auto_20210109_1153.py b/silver/migrations/0054_auto_20210109_1153.py new file mode 100644 index 00000000..8c50884e --- /dev/null +++ b/silver/migrations/0054_auto_20210109_1153.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1.5 on 2021-01-09 11:53 + +from django.db import migrations, models +import django_fsm + + +class Migration(migrations.Migration): + + dependencies = [ + ('silver', '0053_auto_20191028_1254'), + ] + + operations = [ + migrations.AlterField( + model_name='transaction', + name='fail_code', + field=models.CharField(blank=True, choices=[('default', 'default'), ('insufficient_funds', 'insufficient_funds'), ('expired_payment_method', 'expired_payment_method'), ('expired_card', 'expired_card'), ('invalid_payment_method', 'invalid_payment_method'), ('invalid_card', 'invalid_card'), ('limit_exceeded', 'limit_exceeded'), ('transaction_declined', 'transaction_declined'), ('transaction_declined_by_bank', 'transaction_declined_by_bank'), ('transaction_hard_declined', 'transaction_hard_declined'), ('transaction_hard_declined_by_bank', 'transaction_hard_declined_by_bank')], max_length=64, null=True), + ), + ] diff --git a/silver/models/transactions/transaction.py b/silver/models/transactions/transaction.py index 644c1b0f..f768ece4 100644 --- a/silver/models/transactions/transaction.py +++ b/silver/models/transactions/transaction.py @@ -95,7 +95,7 @@ def as_choices(cls): updated_at = AutoDateTimeField(default=timezone.now) fail_code = models.CharField( - choices=[(code, code) for code in FAIL_CODES.keys()], max_length=32, + choices=[(code, code) for code in FAIL_CODES.keys()], max_length=64, null=True, blank=True ) refund_code = models.CharField( From 5045e385a9a00fe7cc14f5a55f240160938d3112 Mon Sep 17 00:00:00 2001 From: Adam Charnock <adam@adamcharnock.com> Date: Sat, 9 Jan 2021 11:56:16 +0000 Subject: [PATCH 11/21] Removing third-party use of json field as it is now bundled with django --- requirements/common.txt | 1 - .../billing_entities_serializers.py | 3 ++- .../api/serializers/documents_serializers.py | 5 +++-- .../serializers/subscriptions_serializers.py | 3 ++- silver/migrations/0001_initial.py | 9 ++++----- silver/migrations/0003_auto_20150417_0634.py | 9 ++++----- silver/migrations/0032_auto_20170201_1342.py | 9 ++++----- silver/migrations/0037_auto_20170719_1159.py | 18 +++++++++--------- silver/migrations/0043_auto_20171113_1048.py | 4 ++-- silver/migrations/0047_auto_20180507_1319.py | 2 +- silver/models/billing_entities/base.py | 5 +++-- silver/models/documents/base.py | 7 ++++--- silver/models/payment_methods.py | 5 +++-- silver/models/subscriptions.py | 5 +++-- silver/models/transactions/transaction.py | 6 +++--- 15 files changed, 47 insertions(+), 44 deletions(-) diff --git a/requirements/common.txt b/requirements/common.txt index 4267a0e0..081b3b3a 100644 --- a/requirements/common.txt +++ b/requirements/common.txt @@ -10,7 +10,6 @@ sqlparse = "^0.4.1" # (required for some old migrations) django-fsm = "^2.7.1" django-filter = "^2.4.0" django-livefield = "^3.3.0" -django-jsonfield = "^1.4.1" django-model-utils = "^4.1.1" django-annoying = "^0.10.6" django-autocomplete-light = "^3.8.1" diff --git a/silver/api/serializers/billing_entities_serializers.py b/silver/api/serializers/billing_entities_serializers.py index f04039a4..ea98d05c 100644 --- a/silver/api/serializers/billing_entities_serializers.py +++ b/silver/api/serializers/billing_entities_serializers.py @@ -14,6 +14,7 @@ from __future__ import absolute_import +from django.core.serializers.json import DjangoJSONEncoder from rest_framework import serializers from rest_framework.fields import JSONField from rest_framework.relations import HyperlinkedRelatedField @@ -85,7 +86,7 @@ class CustomerSerializer(serializers.HyperlinkedModelSerializer): view_name='transaction-list', source='*', lookup_url_kwarg='customer_pk' ) - meta = JSONField(required=False) + meta = JSONField(required=False, encoder=DjangoJSONEncoder) url = CustomerUrl(view_name='customer-detail', read_only=True, source='*') class Meta: diff --git a/silver/api/serializers/documents_serializers.py b/silver/api/serializers/documents_serializers.py index 87f9b571..3494a575 100644 --- a/silver/api/serializers/documents_serializers.py +++ b/silver/api/serializers/documents_serializers.py @@ -14,6 +14,7 @@ from __future__ import absolute_import +from django.core.serializers.json import DjangoJSONEncoder from rest_framework import serializers from rest_framework.fields import JSONField, DecimalField @@ -117,8 +118,8 @@ class InvoiceSerializer(AutoCleanSerializerMixin, customer = CustomerUrl(view_name='customer-detail', queryset=Customer.objects.all()) transactions = TransactionSerializer(many=True, read_only=True) - archived_customer = JSONField(read_only=True) - archived_provider = JSONField(read_only=True) + archived_customer = JSONField(read_only=True, encoder=DjangoJSONEncoder) + archived_provider = JSONField(read_only=True, encoder=DjangoJSONEncoder) total_in_transaction_currency = serializers.DecimalField( max_digits=None, decimal_places=2, coerce_to_string=True, read_only=True, ) diff --git a/silver/api/serializers/subscriptions_serializers.py b/silver/api/serializers/subscriptions_serializers.py index dcd4030c..fed3f017 100644 --- a/silver/api/serializers/subscriptions_serializers.py +++ b/silver/api/serializers/subscriptions_serializers.py @@ -14,6 +14,7 @@ from __future__ import absolute_import +from django.core.serializers.json import DjangoJSONEncoder from rest_framework import serializers from rest_framework.fields import JSONField from rest_framework.reverse import reverse @@ -67,7 +68,7 @@ class SubscriptionSerializer(serializers.HyperlinkedModelSerializer): url = SubscriptionUrl(view_name='subscription-detail', source='*', queryset=Subscription.objects.all(), required=False) updateable_buckets = serializers.ReadOnlyField() - meta = JSONField(required=False) + meta = JSONField(required=False, encoder=DjangoJSONEncoder) class Meta: model = Subscription diff --git a/silver/migrations/0001_initial.py b/silver/migrations/0001_initial.py index a5ecc561..d4c9ea9e 100644 --- a/silver/migrations/0001_initial.py +++ b/silver/migrations/0001_initial.py @@ -19,7 +19,6 @@ from django.db import models, migrations import django.db.models.deletion import django_fsm -import jsonfield.fields import livefield.fields import silver.models import django.core.validators @@ -112,9 +111,9 @@ class Migration(migrations.Migration): serialize=False, auto_created=True, primary_key=True)), ('number', models.IntegerField(null=True, blank=True)), ('archived_customer', - jsonfield.fields.JSONField(default=dict)), + models.JSONField(default=dict)), ('archived_provider', - jsonfield.fields.JSONField(default=dict)), + models.JSONField(default=dict)), ('due_date', models.DateField(null=True, blank=True)), ('issue_date', models.DateField(null=True, blank=True)), ('paid_date', models.DateField(null=True, blank=True)), @@ -212,9 +211,9 @@ class Migration(migrations.Migration): serialize=False, auto_created=True, primary_key=True)), ('number', models.IntegerField(null=True, blank=True)), ('archived_customer', - jsonfield.fields.JSONField(default=dict)), + models.JSONField(default=dict)), ('archived_provider', - jsonfield.fields.JSONField(default=dict)), + models.JSONField(default=dict)), ('due_date', models.DateField(null=True, blank=True)), ('issue_date', models.DateField(null=True, blank=True)), ('paid_date', models.DateField(null=True, blank=True)), diff --git a/silver/migrations/0003_auto_20150417_0634.py b/silver/migrations/0003_auto_20150417_0634.py index d824d2db..ca24b3aa 100644 --- a/silver/migrations/0003_auto_20150417_0634.py +++ b/silver/migrations/0003_auto_20150417_0634.py @@ -16,8 +16,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import migrations -import jsonfield.fields +from django.db import migrations, models class Migration(migrations.Migration): @@ -30,16 +29,16 @@ class Migration(migrations.Migration): migrations.AddField( model_name='customer', name='meta', - field=jsonfield.fields.JSONField(null=True, blank=True), + field=models.JSONField(null=True, blank=True), ), migrations.AddField( model_name='provider', name='meta', - field=jsonfield.fields.JSONField(null=True, blank=True), + field=models.JSONField(null=True, blank=True), ), migrations.AddField( model_name='subscription', name='meta', - field=jsonfield.fields.JSONField(null=True, blank=True), + field=models.JSONField(null=True, blank=True), ), ] diff --git a/silver/migrations/0032_auto_20170201_1342.py b/silver/migrations/0032_auto_20170201_1342.py index 8a821d10..de886d53 100644 --- a/silver/migrations/0032_auto_20170201_1342.py +++ b/silver/migrations/0032_auto_20170201_1342.py @@ -4,7 +4,6 @@ from django.db import migrations, models import django.db.models.deletion import django_fsm -import jsonfield.fields from decimal import Decimal import silver.utils.models import django.utils.timezone @@ -64,7 +63,7 @@ def customer_name_split_reverse(apps, schema_editor): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('payment_processor', models.CharField(max_length=256, choices=[(b'manual', b'manual')])), ('added_at', models.DateTimeField(default=django.utils.timezone.now)), - ('data', jsonfield.fields.JSONField(default={}, null=True, blank=True)), + ('data', models.JSONField(default={}, null=True, blank=True)), ('verified', models.BooleanField(default=False)), ('canceled', models.BooleanField(default=False)), ], @@ -76,7 +75,7 @@ def customer_name_split_reverse(apps, schema_editor): ('amount', models.DecimalField(max_digits=12, decimal_places=2, validators=[django.core.validators.MinValueValidator(Decimal('0.00'))])), ('currency', models.CharField(help_text=b'The currency used for billing.', max_length=4, choices=[('AED', 'UAE Dirham'), ('AFN', 'Afghani'), ('ALL', 'Lek'), ('AMD', 'Armenian Dram'), ('ANG', 'Netherlands Antillean Guilder'), ('AOA', 'Kwanza'), ('ARS', 'Argentine Peso'), ('AUD', 'Australian Dollar'), ('AWG', 'Aruban Florin'), ('AZN', 'Azerbaijanian Manat'), ('BAM', 'Convertible Mark'), ('BBD', 'Barbados Dollar'), ('BDT', 'Taka'), ('BGN', 'Bulgarian Lev'), ('BHD', 'Bahraini Dinar'), ('BIF', 'Burundi Franc'), ('BMD', 'Bermudian Dollar'), ('BND', 'Brunei Dollar'), ('BOB', 'Boliviano'), ('BRL', 'Brazilian Real'), ('BSD', 'Bahamian Dollar'), ('BTN', 'Ngultrum'), ('BWP', 'Pula'), ('BYR', 'Belarusian Ruble'), ('BZD', 'Belize Dollar'), ('CAD', 'Canadian Dollar'), ('CDF', 'Congolese Franc'), ('CHF', 'Swiss Franc'), ('CLP', 'Chilean Peso'), ('CNY', 'Yuan Renminbi'), ('COP', 'Colombian Peso'), ('CRC', 'Costa Rican Colon'), ('CUC', 'Peso Convertible'), ('CUP', 'Cuban Peso'), ('CVE', 'Cabo Verde Escudo'), ('CZK', 'Czech Koruna'), ('DJF', 'Djibouti Franc'), ('DKK', 'Danish Krone'), ('DOP', 'Dominican Peso'), ('DZD', 'Algerian Dinar'), ('EGP', 'Egyptian Pound'), ('ERN', 'Nakfa'), ('ETB', 'Ethiopian Birr'), ('EUR', 'Euro'), ('FJD', 'Fiji Dollar'), ('FKP', 'Falkland Islands Pound'), ('GBP', 'Pound Sterling'), ('GEL', 'Lari'), ('GHS', 'Ghana Cedi'), ('GIP', 'Gibraltar Pound'), ('GMD', 'Dalasi'), ('GNF', 'Guinea Franc'), ('GTQ', 'Quetzal'), ('GYD', 'Guyana Dollar'), ('HKD', 'Hong Kong Dollar'), ('HNL', 'Lempira'), ('HRK', 'Kuna'), ('HTG', 'Gourde'), ('HUF', 'Forint'), ('IDR', 'Rupiah'), ('ILS', 'New Israeli Sheqel'), ('INR', 'Indian Rupee'), ('IQD', 'Iraqi Dinar'), ('IRR', 'Iranian Rial'), ('ISK', 'Iceland Krona'), ('JMD', 'Jamaican Dollar'), ('JOD', 'Jordanian Dinar'), ('JPY', 'Yen'), ('KES', 'Kenyan Shilling'), ('KGS', 'Som'), ('KHR', 'Riel'), ('KMF', 'Comoro Franc'), ('KPW', 'North Korean Won'), ('KRW', 'Won'), ('KWD', 'Kuwaiti Dinar'), ('KYD', 'Cayman Islands Dollar'), ('KZT', 'Tenge'), ('LAK', 'Kip'), ('LBP', 'Lebanese Pound'), ('LKR', 'Sri Lanka Rupee'), ('LRD', 'Liberian Dollar'), ('LSL', 'Loti'), ('LYD', 'Libyan Dinar'), ('MAD', 'Moroccan Dirham'), ('MDL', 'Moldovan Leu'), ('MGA', 'Malagasy Ariary'), ('MKD', 'Denar'), ('MMK', 'Kyat'), ('MNT', 'Tugrik'), ('MOP', 'Pataca'), ('MRO', 'Ouguiya'), ('MUR', 'Mauritius Rupee'), ('MVR', 'Rufiyaa'), ('MWK', 'Malawi Kwacha'), ('MXN', 'Mexican Peso'), ('MYR', 'Malaysian Ringgit'), ('MZN', 'Mozambique Metical'), ('NAD', 'Namibia Dollar'), ('NGN', 'Naira'), ('NIO', 'Cordoba Oro'), ('NOK', 'Norwegian Krone'), ('NPR', 'Nepalese Rupee'), ('NZD', 'New Zealand Dollar'), ('OMR', 'Rial Omani'), ('PAB', 'Balboa'), ('PEN', 'Sol'), ('PGK', 'Kina'), ('PHP', 'Philippine Peso'), ('PKR', 'Pakistan Rupee'), ('PLN', 'Zloty'), ('PYG', 'Guarani'), ('QAR', 'Qatari Rial'), ('RON', 'Romanian Leu'), ('RSD', 'Serbian Dinar'), ('RUB', 'Russian Ruble'), ('RWF', 'Rwanda Franc'), ('SAR', 'Saudi Riyal'), ('SBD', 'Solomon Islands Dollar'), ('SCR', 'Seychelles Rupee'), ('SDG', 'Sudanese Pound'), ('SEK', 'Swedish Krona'), ('SGD', 'Singapore Dollar'), ('SHP', 'Saint Helena Pound'), ('SLL', 'Leone'), ('SOS', 'Somali Shilling'), ('SRD', 'Surinam Dollar'), ('SSP', 'South Sudanese Pound'), ('STD', 'Dobra'), ('SVC', 'El Salvador Colon'), ('SYP', 'Syrian Pound'), ('SZL', 'Lilangeni'), ('THB', 'Baht'), ('TJS', 'Somoni'), ('TMT', 'Turkmenistan New Manat'), ('TND', 'Tunisian Dinar'), ('TOP', 'Pa\u2019anga'), ('TRY', 'Turkish Lira'), ('TTD', 'Trinidad and Tobago Dollar'), ('TWD', 'New Taiwan Dollar'), ('TZS', 'Tanzanian Shilling'), ('UAH', 'Hryvnia'), ('UGX', 'Uganda Shilling'), ('USD', 'US Dollar'), ('UYU', 'Peso Uruguayo'), ('UZS', 'Uzbekistan Sum'), ('VEF', 'Bol\xedvar'), ('VND', 'Dong'), ('VUV', 'Vatu'), ('WST', 'Tala'), ('XAF', 'CFA Franc BEAC'), ('XAG', 'Silver'), ('XAU', 'Gold'), ('XBA', 'Bond Markets Unit European Composite Unit (EURCO)'), ('XBB', 'Bond Markets Unit European Monetary Unit (E.M.U.-6)'), ('XBC', 'Bond Markets Unit European Unit of Account 9 (E.U.A.-9)'), ('XBD', 'Bond Markets Unit European Unit of Account 17 (E.U.A.-17)'), ('XCD', 'East Caribbean Dollar'), ('XDR', 'SDR (Special Drawing Right)'), ('XOF', 'CFA Franc BCEAO'), ('XPD', 'Palladium'), ('XPF', 'CFP Franc'), ('XPT', 'Platinum'), ('XSU', 'Sucre'), ('XTS', 'Codes specifically reserved for testing purposes'), ('XUA', 'ADB Unit of Account'), ('XXX', 'The codes assigned for transactions where no currency is involved'), ('YER', 'Yemeni Rial'), ('ZAR', 'Rand'), ('ZMW', 'Zambian Kwacha'), ('ZWL', 'Zimbabwe Dollar')])), ('external_reference', models.CharField(max_length=256, null=True, blank=True)), - ('data', jsonfield.fields.JSONField(default={}, null=True, blank=True)), + ('data', models.JSONField(default={}, null=True, blank=True)), ('state', django_fsm.FSMField(default=b'initial', max_length=8, choices=[(b'canceled', 'Canceled'), (b'refunded', 'Refunded'), (b'initial', 'Initial'), (b'failed', 'Failed'), (b'settled', 'Settled'), (b'pending', 'Pending')])), ('uuid', models.UUIDField(default=uuid.uuid4)), ('valid_until', models.DateTimeField(null=True, blank=True)), @@ -134,7 +133,7 @@ def customer_name_split_reverse(apps, schema_editor): migrations.AlterField( model_name='customer', name='meta', - field=jsonfield.fields.JSONField(default={}, null=True, blank=True), + field=models.JSONField(default={}, null=True, blank=True), ), migrations.AlterField( model_name='invoice', @@ -184,7 +183,7 @@ def customer_name_split_reverse(apps, schema_editor): migrations.AlterField( model_name='provider', name='meta', - field=jsonfield.fields.JSONField(default={}, null=True, blank=True), + field=models.JSONField(default={}, null=True, blank=True), ), migrations.AlterField( model_name='provider', diff --git a/silver/migrations/0037_auto_20170719_1159.py b/silver/migrations/0037_auto_20170719_1159.py index 406a837d..ef0cf639 100644 --- a/silver/migrations/0037_auto_20170719_1159.py +++ b/silver/migrations/0037_auto_20170719_1159.py @@ -31,17 +31,17 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='customer', name='meta', - field=annoying.fields.JSONField(blank=True, default={}, null=True), + field=models.JSONField(blank=True, default={}, null=True), ), migrations.AlterField( model_name='invoice', name='archived_customer', - field=annoying.fields.JSONField(blank=True, default=dict, null=True), + field=models.JSONField(blank=True, default=dict, null=True), ), migrations.AlterField( model_name='invoice', name='archived_provider', - field=annoying.fields.JSONField(blank=True, default=dict, null=True), + field=models.JSONField(blank=True, default=dict, null=True), ), migrations.AlterField( model_name='invoice', @@ -56,7 +56,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='paymentmethod', name='data', - field=annoying.fields.JSONField(blank=True, default={}, null=True), + field=models.JSONField(blank=True, default={}, null=True), ), migrations.AlterField( model_name='plan', @@ -66,12 +66,12 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='proforma', name='archived_customer', - field=annoying.fields.JSONField(blank=True, default=dict, null=True), + field=models.JSONField(blank=True, default=dict, null=True), ), migrations.AlterField( model_name='proforma', name='archived_provider', - field=annoying.fields.JSONField(blank=True, default=dict, null=True), + field=models.JSONField(blank=True, default=dict, null=True), ), migrations.AlterField( model_name='proforma', @@ -91,12 +91,12 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='provider', name='meta', - field=annoying.fields.JSONField(blank=True, default={}, null=True), + field=models.JSONField(blank=True, default={}, null=True), ), migrations.AlterField( model_name='subscription', name='meta', - field=annoying.fields.JSONField(blank=True, null=True), + field=models.JSONField(blank=True, null=True), ), migrations.AlterField( model_name='transaction', @@ -106,6 +106,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='transaction', name='data', - field=annoying.fields.JSONField(blank=True, default={}, null=True), + field=models.JSONField(blank=True, default={}, null=True), ), ] diff --git a/silver/migrations/0043_auto_20171113_1048.py b/silver/migrations/0043_auto_20171113_1048.py index 6c235ff2..b0d9dc20 100644 --- a/silver/migrations/0043_auto_20171113_1048.py +++ b/silver/migrations/0043_auto_20171113_1048.py @@ -90,8 +90,8 @@ class Migration(migrations.Migration): ('kind', models.CharField(db_index=True, max_length=8, verbose_name=silver.models.documents.base.get_billing_documents_kinds)), ('series', models.CharField(blank=True, db_index=True, max_length=20, null=True)), ('number', models.IntegerField(blank=True, db_index=True, null=True)), - ('archived_customer', annoying.fields.JSONField(blank=True, default=dict, null=True)), - ('archived_provider', annoying.fields.JSONField(blank=True, default=dict, null=True)), + ('archived_customer', models.JSONField(blank=True, default=dict, null=True)), + ('archived_provider', models.JSONField(blank=True, default=dict, null=True)), ('due_date', models.DateField(blank=True, null=True)), ('issue_date', models.DateField(blank=True, db_index=True, null=True)), ('paid_date', models.DateField(blank=True, null=True)), diff --git a/silver/migrations/0047_auto_20180507_1319.py b/silver/migrations/0047_auto_20180507_1319.py index 77d22329..0abc7c99 100644 --- a/silver/migrations/0047_auto_20180507_1319.py +++ b/silver/migrations/0047_auto_20180507_1319.py @@ -16,6 +16,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='subscription', name='meta', - field=annoying.fields.JSONField(blank=True, default={}, null=True), + field=models.JSONField(blank=True, default={}, null=True), ), ] diff --git a/silver/models/billing_entities/base.py b/silver/models/billing_entities/base.py index 54c7f531..79b2ba8e 100644 --- a/silver/models/billing_entities/base.py +++ b/silver/models/billing_entities/base.py @@ -14,7 +14,8 @@ from __future__ import absolute_import, unicode_literals -from annoying.fields import JSONField +from django.core.serializers.json import DjangoJSONEncoder +from django.db.models import JSONField from livefield import LiveModel from django.conf import settings @@ -42,7 +43,7 @@ class BaseBillingEntity(LiveModel): help_text='Extra information to display on the invoice ' '(markdown formatted).' ) - meta = JSONField(blank=True, null=True, default={}) + meta = JSONField(blank=True, null=True, default={}, encoder=DjangoJSONEncoder) class Meta: abstract = True diff --git a/silver/models/documents/base.py b/silver/models/documents/base.py index d04c3ec1..88185ce9 100644 --- a/silver/models/documents/base.py +++ b/silver/models/documents/base.py @@ -18,7 +18,8 @@ from datetime import datetime, timedelta from decimal import Decimal -from annoying.fields import JSONField +from django.core.serializers.json import DjangoJSONEncoder +from django.db.models import JSONField from django_fsm import FSMField, transition, TransitionNotAllowed, post_transition from model_utils import Choices @@ -131,8 +132,8 @@ class STATES(object): number = models.IntegerField(blank=True, null=True, db_index=True) customer = models.ForeignKey('Customer', on_delete=models.CASCADE) provider = models.ForeignKey('Provider', on_delete=models.CASCADE) - archived_customer = JSONField(default=dict, null=True, blank=True) - archived_provider = JSONField(default=dict, null=True, blank=True) + archived_customer = JSONField(default=dict, null=True, blank=True, encoder=DjangoJSONEncoder) + archived_provider = JSONField(default=dict, null=True, blank=True, encoder=DjangoJSONEncoder) due_date = models.DateField(null=True, blank=True) issue_date = models.DateField(null=True, blank=True, db_index=True) paid_date = models.DateField(null=True, blank=True) diff --git a/silver/models/payment_methods.py b/silver/models/payment_methods.py index cc2a66f7..91568fa8 100644 --- a/silver/models/payment_methods.py +++ b/silver/models/payment_methods.py @@ -16,9 +16,10 @@ from itertools import chain -from annoying.fields import JSONField from annoying.functions import get_object_or_None from cryptography.fernet import InvalidToken, Fernet +from django.core.serializers.json import DjangoJSONEncoder +from django.db.models import JSONField from django_fsm import TransitionNotAllowed from model_utils.managers import InheritanceManager @@ -54,7 +55,7 @@ def as_list(cls): blank=False, null=False, max_length=256) customer = models.ForeignKey(Customer, models.CASCADE) added_at = models.DateTimeField(default=timezone.now) - data = JSONField(blank=True, null=True, default={}) + data = JSONField(blank=True, null=True, default={}, encoder=DjangoJSONEncoder) verified = models.BooleanField(default=False) canceled = models.BooleanField(default=False) diff --git a/silver/models/subscriptions.py b/silver/models/subscriptions.py index 4353a4d1..b145970e 100644 --- a/silver/models/subscriptions.py +++ b/silver/models/subscriptions.py @@ -21,9 +21,10 @@ from decimal import Decimal from functools import reduce -from annoying.fields import JSONField from annoying.functions import get_object_or_None from dateutil import rrule +from django.core.serializers.json import DjangoJSONEncoder +from django.db.models import JSONField from django_fsm import FSMField, transition, TransitionNotAllowed from model_utils import Choices @@ -178,7 +179,7 @@ class CANCEL_OPTIONS(object): choices=STATE_CHOICES, max_length=12, default=STATES.INACTIVE, help_text='The state the subscription is in.' ) - meta = JSONField(blank=True, null=True, default={}) + meta = JSONField(blank=True, null=True, default={}, encoder=DjangoJSONEncoder) def clean(self): errors = dict() diff --git a/silver/models/transactions/transaction.py b/silver/models/transactions/transaction.py index f768ece4..d3b8f544 100644 --- a/silver/models/transactions/transaction.py +++ b/silver/models/transactions/transaction.py @@ -19,14 +19,14 @@ from decimal import Decimal -from annoying.fields import JSONField from annoying.functions import get_object_or_None +from django.core.serializers.json import DjangoJSONEncoder from django_fsm import FSMField, post_transition, transition from django.core.exceptions import ValidationError from django.core.validators import MinValueValidator from django.db import models, transaction -from django.db.models import Q +from django.db.models import Q, JSONField from django.db.models.signals import post_save from django.dispatch import receiver from django.utils import timezone @@ -77,7 +77,7 @@ def as_choices(cls): ) external_reference = models.CharField(max_length=256, null=True, blank=True) - data = JSONField(default={}, null=True, blank=True) + data = JSONField(default={}, null=True, blank=True, encoder=DjangoJSONEncoder) state = FSMField(max_length=8, choices=States.as_choices(), default=States.Initial) From 56cb974dbc900033ec74c70a3eae56523d1158ae Mon Sep 17 00:00:00 2001 From: Adam Charnock <adam@adamcharnock.com> Date: Sat, 9 Jan 2021 11:59:00 +0000 Subject: [PATCH 12/21] Fixing mutable default values for JSONField --- silver/models/billing_entities/base.py | 2 +- silver/models/payment_methods.py | 2 +- silver/models/subscriptions.py | 2 +- silver/models/transactions/transaction.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/silver/models/billing_entities/base.py b/silver/models/billing_entities/base.py index 79b2ba8e..5ac9fed7 100644 --- a/silver/models/billing_entities/base.py +++ b/silver/models/billing_entities/base.py @@ -43,7 +43,7 @@ class BaseBillingEntity(LiveModel): help_text='Extra information to display on the invoice ' '(markdown formatted).' ) - meta = JSONField(blank=True, null=True, default={}, encoder=DjangoJSONEncoder) + meta = JSONField(blank=True, null=True, default=dict, encoder=DjangoJSONEncoder) class Meta: abstract = True diff --git a/silver/models/payment_methods.py b/silver/models/payment_methods.py index 91568fa8..3427057d 100644 --- a/silver/models/payment_methods.py +++ b/silver/models/payment_methods.py @@ -55,7 +55,7 @@ def as_list(cls): blank=False, null=False, max_length=256) customer = models.ForeignKey(Customer, models.CASCADE) added_at = models.DateTimeField(default=timezone.now) - data = JSONField(blank=True, null=True, default={}, encoder=DjangoJSONEncoder) + data = JSONField(blank=True, null=True, default=dict, encoder=DjangoJSONEncoder) verified = models.BooleanField(default=False) canceled = models.BooleanField(default=False) diff --git a/silver/models/subscriptions.py b/silver/models/subscriptions.py index b145970e..4afb2f5f 100644 --- a/silver/models/subscriptions.py +++ b/silver/models/subscriptions.py @@ -179,7 +179,7 @@ class CANCEL_OPTIONS(object): choices=STATE_CHOICES, max_length=12, default=STATES.INACTIVE, help_text='The state the subscription is in.' ) - meta = JSONField(blank=True, null=True, default={}, encoder=DjangoJSONEncoder) + meta = JSONField(blank=True, null=True, default=dict, encoder=DjangoJSONEncoder) def clean(self): errors = dict() diff --git a/silver/models/transactions/transaction.py b/silver/models/transactions/transaction.py index d3b8f544..3395f8ee 100644 --- a/silver/models/transactions/transaction.py +++ b/silver/models/transactions/transaction.py @@ -77,7 +77,7 @@ def as_choices(cls): ) external_reference = models.CharField(max_length=256, null=True, blank=True) - data = JSONField(default={}, null=True, blank=True, encoder=DjangoJSONEncoder) + data = JSONField(default=dict, null=True, blank=True, encoder=DjangoJSONEncoder) state = FSMField(max_length=8, choices=States.as_choices(), default=States.Initial) From 0974aa75f7e95be41a96ace196d96810672d3acd Mon Sep 17 00:00:00 2001 From: Adam Charnock <adam@adamcharnock.com> Date: Sat, 9 Jan 2021 12:02:21 +0000 Subject: [PATCH 13/21] Fixing deprecation warning: NullBooleanField -> BooleanField NullBooleanField is deprecated. Support for it (except in historical migrations) will be removed in Django 4.0. HINT: Use BooleanField(null=True) instead. Adding migration for this and previous change to JSONField default values --- silver/models/plans.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/silver/models/plans.py b/silver/models/plans.py index 07ec60bc..1d333c27 100644 --- a/silver/models/plans.py +++ b/silver/models/plans.py @@ -72,15 +72,18 @@ class INTERVALS(object): 'customer to this plan.', verbose_name='Trial days' ) - generate_documents_on_trial_end = models.NullBooleanField( + generate_documents_on_trial_end = models.BooleanField( + null=True, help_text="If this is set to True, then billing documents will be generated when the " "subscription trial ends, instead of waiting for the end of the billing cycle." ) - separate_cycles_during_trial = models.NullBooleanField( + separate_cycles_during_trial = models.BooleanField( + null=True, help_text="If this is set to True, then the trial period cycle will be split if it spans " "across multiple billing intervals." ) - prebill_plan = models.NullBooleanField( + prebill_plan = models.BooleanField( + null=True, help_text="If this is set to True, then the plan base amount will be billed at the" "beginning of the billing cycle rather than after the end." ) From e006d1a659cffa9bc19a3a8d3c12c5c22f5531f9 Mon Sep 17 00:00:00 2001 From: Adam Charnock <adam@adamcharnock.com> Date: Sat, 9 Jan 2021 12:03:21 +0000 Subject: [PATCH 14/21] Fixing warning regarding missing template context processor: ?: (admin.W411) 'django.template.context_processors.request' must be enabled in DjangoTemplates (TEMPLATES) in order to use the admin navigation sidebar. --- settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/settings.py b/settings.py index 1d2c4961..7c419335 100644 --- a/settings.py +++ b/settings.py @@ -92,7 +92,8 @@ "django.template.context_processors.media", "django.template.context_processors.static", "django.template.context_processors.tz", - "django.contrib.messages.context_processors.messages" + "django.template.context_processors.request", + "django.contrib.messages.context_processors.messages", ) } } From 818649dd0133e62de90a447e656fada1e20d0299 Mon Sep 17 00:00:00 2001 From: Adam Charnock <adam@adamcharnock.com> Date: Sat, 9 Jan 2021 12:16:40 +0000 Subject: [PATCH 15/21] Adding the BooleanField and JSONField migration which I missed in an earlier commit --- silver/migrations/0055_auto_20210109_1200.py | 53 ++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 silver/migrations/0055_auto_20210109_1200.py diff --git a/silver/migrations/0055_auto_20210109_1200.py b/silver/migrations/0055_auto_20210109_1200.py new file mode 100644 index 00000000..e4c81a11 --- /dev/null +++ b/silver/migrations/0055_auto_20210109_1200.py @@ -0,0 +1,53 @@ +# Generated by Django 3.1.5 on 2021-01-09 12:00 + +import django.core.serializers.json +from django.db import migrations, models +import django_fsm + + +class Migration(migrations.Migration): + + dependencies = [ + ('silver', '0054_auto_20210109_1153'), + ] + + operations = [ + migrations.DeleteModel( + name='Document', + ), + migrations.AlterField( + model_name='billingdocumentbase', + name='archived_customer', + field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder, null=True), + ), + migrations.AlterField( + model_name='billingdocumentbase', + name='archived_provider', + field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder, null=True), + ), + migrations.AlterField( + model_name='customer', + name='meta', + field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder, null=True), + ), + migrations.AlterField( + model_name='paymentmethod', + name='data', + field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder, null=True), + ), + migrations.AlterField( + model_name='plan', + name='generate_documents_on_trial_end', + field=models.BooleanField(help_text='If this is set to True, then billing documents will be generated when the subscription trial ends, instead of waiting for the end of the billing cycle.', null=True), + ), + migrations.AlterField( + model_name='plan', + name='prebill_plan', + field=models.BooleanField(help_text='If this is set to True, then the plan base amount will be billed at thebeginning of the billing cycle rather than after the end.', null=True), + ), + migrations.AlterField( + model_name='plan', + name='separate_cycles_during_trial', + field=models.BooleanField(help_text='If this is set to True, then the trial period cycle will be split if it spans across multiple billing intervals.', null=True), + ), + ] From f6fadb810b417f4536bf46a2695e0c3ee7d1ee5a Mon Sep 17 00:00:00 2001 From: Adam Charnock <adam@adamcharnock.com> Date: Sat, 9 Jan 2021 12:17:08 +0000 Subject: [PATCH 16/21] Fixing comparison error in tests. This could probably be done better --- silver/tests/api/test_invoice.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/silver/tests/api/test_invoice.py b/silver/tests/api/test_invoice.py index ca1f4f06..4d9ed58c 100644 --- a/silver/tests/api/test_invoice.py +++ b/silver/tests/api/test_invoice.py @@ -17,6 +17,7 @@ import json from datetime import timedelta from decimal import Decimal +from uuid import UUID import pytest from six.moves import range @@ -175,6 +176,13 @@ def test_get_invoice(authenticated_api_client, settings, issued_invoice): response = authenticated_api_client.get(url, format='json') assert response.status_code == status.HTTP_200_OK, response.data + + # Cast IDs to UUID so the comparison check doesn't fail + data = response.data + data['transactions'][0]['id'] = UUID(data['transactions'][0]['id']) + data['transactions'][1]['id'] = UUID(data['transactions'][1]['id']) + data['transactions'][2]['id'] = UUID(data['transactions'][2]['id']) + invoice_definition.check_response(invoice, response_data=response.data) From 19c3b929f5cbb0f72c05ca10c68526def471eb5c Mon Sep 17 00:00:00 2001 From: Adam Charnock <adam@adamcharnock.com> Date: Sat, 9 Jan 2021 12:17:45 +0000 Subject: [PATCH 17/21] Proforma resource now returning JSON objects rather than stringified json objects. Note added to CHANGELOG --- CHANGELOG.md | 9 ++++++++- silver/tests/api/test_proforma.py | 8 ++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e814d922..4c14ec71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,14 @@ # Changelog ## Unrealeased changes -_Nothing yet_ + +- Adding Django 3 support +- Updating all dependencies to latest versions +- Dropping Python 2 compatibility +- Proforma API fields `archived_provider` and `archived_customer` now correctly return JSON objects rather than + stringified JSON objects + + ## 0.10.1 Fixed issue in autocomplete views where user.is_authenticated is no longer a function call and instead an attribute diff --git a/silver/tests/api/test_proforma.py b/silver/tests/api/test_proforma.py index 9f11efd1..1f4f985d 100644 --- a/silver/tests/api/test_proforma.py +++ b/silver/tests/api/test_proforma.py @@ -71,8 +71,8 @@ def test_post_proforma_without_proforma_entries(self): "number": None, "provider": provider_url, "customer": customer_url, - "archived_provider": '{}', - "archived_customer": '{}', + "archived_provider": {}, + "archived_customer": {}, "due_date": None, "issue_date": None, "paid_date": None, @@ -165,8 +165,8 @@ def test_get_proforma(self, mocked_settings): "number": proforma.number, "provider": provider_url, "customer": customer_url, - "archived_provider": '{}', - "archived_customer": '{}', + "archived_provider": {}, + "archived_customer": {}, "due_date": None, "issue_date": None, "paid_date": None, From 3bcb5ca3335afe28d7c3a786fd0ea92afedf84da Mon Sep 17 00:00:00 2001 From: Adam Charnock <adam@adamcharnock.com> Date: Sat, 9 Jan 2021 12:18:41 +0000 Subject: [PATCH 18/21] DeprecationWarning fix: assertEquals -> assertEqual --- silver/tests/unit/test_payments_util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/silver/tests/unit/test_payments_util.py b/silver/tests/unit/test_payments_util.py index 0a5d0112..51cd7de6 100644 --- a/silver/tests/unit/test_payments_util.py +++ b/silver/tests/unit/test_payments_util.py @@ -61,7 +61,7 @@ def test_get_transaction_from_token(self): mocked_view = MagicMock() token = _get_jwt_token(transaction) - self.assertEquals(get_transaction_from_token(mocked_view)(None, token), + self.assertEqual(get_transaction_from_token(mocked_view)(None, token), mocked_view()) mocked_view.has_calls([call(None, transaction, False), call()]) @@ -73,6 +73,6 @@ def test_get_transaction_from_expired_token(self): mocked_datetime.utcnow.return_value = datetime.utcnow() - timedelta(days=2 * 365) token = _get_jwt_token(transaction) - self.assertEquals(get_transaction_from_token(mocked_view)(None, token), + self.assertEqual(get_transaction_from_token(mocked_view)(None, token), mocked_view()) mocked_view.has_calls([call(None, transaction, True), call()]) From 792e0f9dfea59f0af3cfd69016899688e4792d5b Mon Sep 17 00:00:00 2001 From: Adam Charnock <adam@adamcharnock.com> Date: Sun, 10 Jan 2021 12:05:09 +0000 Subject: [PATCH 19/21] Fixing malformatted requirements files --- requirements/common.txt | 36 ++++++++++++++++++------------------ requirements/optionals.txt | 6 +++--- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/requirements/common.txt b/requirements/common.txt index 081b3b3a..c0638caf 100644 --- a/requirements/common.txt +++ b/requirements/common.txt @@ -3,31 +3,31 @@ # UNMAINTAINED means the package owner is no longer maintaining it # Core -Django = "^3.1.5" -sqlparse = "^0.4.1" # (required for some old migrations) +Django~=3.1.5 +sqlparse~=0.4.1 # (required for some old migrations) # Django Utils -django-fsm = "^2.7.1" -django-filter = "^2.4.0" -django-livefield = "^3.3.0" -django-model-utils = "^4.1.1" -django-annoying = "^0.10.6" -django-autocomplete-light = "^3.8.1" +django-fsm~=2.7.1 +django-filter~=2.4.0 +django-livefield~=3.3.0 +django-model-utils~=4.1.1 +django-annoying~=0.10.6 +django-autocomplete-light~=3.8.1 # API -djangorestframework = "^3.12.2" -djangorestframework-bulk = "^0.2.1" +djangorestframework~=3.12.2 +djangorestframework-bulk~=0.2.1 # I18n -pycountry = "^20.7.3" -python-dateutil = "^2.8.1" -pyvat = "^1.3.13" +pycountry~=20.7.3 +python-dateutil~=2.8.1 +pyvat~=1.3.13 # Crypto -cryptography = "^3.3.1" -PyJWT = "^2.0.0" +cryptography~=3.3.1 +PyJWT~=2.0.0 # Other -furl = "^2.1.0" -xhtml2pdf = "^0.2.5" -PyPDF2 = "^1.26.0" +furl~=2.1.0 +xhtml2pdf~=0.2.5 +PyPDF2~=1.26.0 diff --git a/requirements/optionals.txt b/requirements/optionals.txt index 7c84848b..91c52ff3 100644 --- a/requirements/optionals.txt +++ b/requirements/optionals.txt @@ -1,3 +1,3 @@ -celery = "^5.0.5" -redis = "^3.5.3" -celery_once = "^3.0.1" +celery~=5.0.5 +redis~=3.5.3 +celery_once~=3.0.1 From af0104ddf2391ced4c9a575f4ceb3da78b4a335d Mon Sep 17 00:00:00 2001 From: Adam Charnock <adam@adamcharnock.com> Date: Sun, 10 Jan 2021 12:10:09 +0000 Subject: [PATCH 20/21] Fixing pep8 issues --- silver/tests/api/specs/utils.py | 1 - silver/tests/unit/test_payments_util.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/silver/tests/api/specs/utils.py b/silver/tests/api/specs/utils.py index c6f003ce..ba418f58 100644 --- a/silver/tests/api/specs/utils.py +++ b/silver/tests/api/specs/utils.py @@ -1,7 +1,6 @@ import inspect - datetime_to_str = lambda input_date: input_date.isoformat()[:-6] + 'Z' datetime_to_str_or_none = lambda input_date: datetime_to_str(input_date) if input_date else None date_to_str = lambda input_date: input_date.isoformat() diff --git a/silver/tests/unit/test_payments_util.py b/silver/tests/unit/test_payments_util.py index 51cd7de6..2ca4836b 100644 --- a/silver/tests/unit/test_payments_util.py +++ b/silver/tests/unit/test_payments_util.py @@ -62,7 +62,7 @@ def test_get_transaction_from_token(self): token = _get_jwt_token(transaction) self.assertEqual(get_transaction_from_token(mocked_view)(None, token), - mocked_view()) + mocked_view()) mocked_view.has_calls([call(None, transaction, False), call()]) def test_get_transaction_from_expired_token(self): @@ -74,5 +74,5 @@ def test_get_transaction_from_expired_token(self): token = _get_jwt_token(transaction) self.assertEqual(get_transaction_from_token(mocked_view)(None, token), - mocked_view()) + mocked_view()) mocked_view.has_calls([call(None, transaction, True), call()]) From e77477b73d3034514cf96b8bd253b8a2ffbfb6e9 Mon Sep 17 00:00:00 2001 From: Adam Charnock <adam@adamcharnock.com> Date: Sun, 10 Jan 2021 13:41:38 +0000 Subject: [PATCH 21/21] Creating separate urls py to be used when running silver as a standalone app --- settings.py | 2 +- silver/urls.py | 3 --- silver/urls_project.py | 12 ++++++++++++ 3 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 silver/urls_project.py diff --git a/settings.py b/settings.py index 7c419335..5cf4d4b4 100644 --- a/settings.py +++ b/settings.py @@ -67,7 +67,7 @@ INSTALLED_APPS = EXTERNAL_APPS + INTERNAL_APPS -ROOT_URLCONF = 'silver.urls' +ROOT_URLCONF = 'silver.urls_project' PROJECT_ROOT = os.path.dirname(__file__) FIXTURE_DIRS = ( diff --git a/silver/urls.py b/silver/urls.py index 7b6073e7..47664140 100644 --- a/silver/urls.py +++ b/silver/urls.py @@ -18,7 +18,6 @@ from __future__ import absolute_import from django.conf.urls import include, re_path -from django.contrib import admin from silver.views import (pay_transaction_view, complete_payment_view, InvoiceAutocomplete, ProformaAutocomplete, @@ -26,11 +25,9 @@ CustomerAutocomplete, ProviderAutocomplete) -admin.autodiscover() urlpatterns = [ - re_path(r'^admin/', admin.site.urls), re_path(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), re_path(r'', include('silver.api.urls')), diff --git a/silver/urls_project.py b/silver/urls_project.py new file mode 100644 index 00000000..c7d068f2 --- /dev/null +++ b/silver/urls_project.py @@ -0,0 +1,12 @@ +from django.urls import re_path +from django.contrib import admin + +from silver.urls import urlpatterns + +admin.autodiscover() + +# This urls.py file should be used when running silver as a standalone +# project (e.g. when running silver's tests) +urlpatterns = [ + re_path(r'^admin/', admin.site.urls), +] + urlpatterns