Commits

Hynek Cernoch  committed 2060036

Fix dbsync with PostgreSQL. Closes first problem reported in #1256

- removed unnecessary field models classes from an old false fix

  • Participants
  • Parent commits f077c4f

Comments (0)

Files changed (5)

File satchmo/apps/payment/fields.py

-from django.db import models
-from payment.config import credit_choices, labelled_gateway_choices
-
-class CreditChoiceCharField(models.CharField):
-
-    def __init__(self, *args, **kwargs):
-        choices = kwargs.pop("choices", "__DYNAMIC__")
-        if choices == "__DYNAMIC__":
-            kwargs['choices'] = credit_choices()
-
-        super(CreditChoiceCharField, self).__init__(*args, **kwargs)
-
-class PaymentChoiceCharField(models.CharField):
-    
-    def __init__(self, *args, **kwargs):
-        choices = kwargs.pop("choices", "__DYNAMIC__")
-        if choices == "__DYNAMIC__":
-            kwargs['choices'] = labelled_gateway_choices()
-                    
-        super(PaymentChoiceCharField, self).__init__(*args, **kwargs)
-
-try:
-    # South introspection rules for our custom field.
-    from south.modelsinspector import add_introspection_rules, matching_details
-
-    # get the kwargs for a Field instance
-    # we're using Field, as CharField doesn't change __init__()
-    _args, kwargs = matching_details(models.Field())
-
-    add_introspection_rules([(
-        (CreditChoiceCharField, ),
-        [],
-        kwargs,
-    )], ['payment\.fields\.CreditChoiceCharField'])
-    add_introspection_rules([(
-        (PaymentChoiceCharField, ),
-        [],
-        kwargs,
-    )], ['payment\.fields\.PaymentChoiceCharField'])
-except ImportError:
-    pass

File satchmo/apps/payment/models.py

 from django.db import models
 from django.utils.translation import ugettext_lazy as _
 from livesettings import config_value, config_choice_values, SettingNotSet
-from payment.fields import PaymentChoiceCharField, CreditChoiceCharField
+from satchmo_utils.iterchoices import iterchoices_db
+import payment.config
 from satchmo_store.contact.models import Contact
 import base64
 import config
     description = models.CharField(_("Description"), max_length=20)
     active = models.BooleanField(_("Active"), 
         help_text=_("Should this be displayed as an option for the user?"))
-    optionName = PaymentChoiceCharField(_("Option Name"), max_length=20, 
+    optionName = models.CharField(_("Option Name"), max_length=20, choices=iterchoices_db(payment.config.labelled_gateway_choices),
         unique=True, 
         help_text=_("The class name as defined in payment.py"))
     sortOrder = models.IntegerField(_("Sort Order"))
     """
     orderpayment = models.ForeignKey('shop.OrderPayment', unique=True, 
         related_name="creditcards")
-    credit_type = CreditChoiceCharField(_("Credit Card Type"), max_length=16)
+    credit_type = models.CharField(_("Credit Card Type"), max_length=16, choices=iterchoices_db(payment.config.credit_choices))
     display_cc = models.CharField(_("CC Number (Last 4 digits)"),
         max_length=4, )
     encrypted_cc = models.CharField(_("Encrypted Credit Card"),

File satchmo/apps/satchmo_store/shop/models.py

 from l10n.models import Country
 from l10n.utils import moneyfmt
 from livesettings import ConfigurationSettings, config_value
-from payment.fields import PaymentChoiceCharField
 from product.models import Discount, Product, Price, get_product_quantity_adjustments
 from product.prices import PriceAdjustmentCalc, PriceAdjustment
 from satchmo_store.contact.models import Contact
 from satchmo_utils.fields import CurrencyField
 from satchmo_utils.numbers import trunc_decimal
-from shipping.fields import ShippingChoiceCharField
+import shipping.fields
+import payment.config
+from satchmo_utils.iterchoices import iterchoices_db
 from tax.utils import get_tax_processor
 import datetime
 import keyedcache
         max_length=50, blank=True, null=True)
     shipping_method = models.CharField(_("Shipping Method"),
         max_length=50, blank=True, null=True)
-    shipping_model = ShippingChoiceCharField(_("Shipping Models"),
+    shipping_model = models.CharField(_("Shipping Models"), choices=iterchoices_db(shipping.fields.shipping_choices),
         max_length=30, blank=True, null=True)
     shipping_cost = CurrencyField(_("Shipping Cost"),
         max_digits=18, decimal_places=10, blank=True, null=True)
         get_latest_by = 'time_stamp'
 
 class OrderPaymentBase(models.Model):
-    payment = PaymentChoiceCharField(_("Payment Method"),
+    payment = models.CharField(_("Payment Method"), choices=iterchoices_db(payment.config.labelled_gateway_choices),
         max_length=25, blank=True)
     amount = CurrencyField(_("amount"),
         max_digits=18, decimal_places=10, blank=True, null=True)

File satchmo/apps/satchmo_utils/iterchoices.py

+"""
+Module is intended as a helper to prevent cyclic dependencies between models
+and to prevent database queries before database tables are created.
+
+It is recommended this code is left alone or can be combined with other module
+which is not dependent on any database model nor already existing db connection
+for facilitating the normal order of loading modules.
+"""
+
+import logging
+import traceback
+
+def iterchoices(func):
+    """Iterator for lazy evaluating of choices for database models.
+
+    Usage:
+        class SomeNewModel(models.Model):
+           abc = XyzChoiceCharField('SomeModelName',... choices=iterchoices(get_choices)))
+    Function for evaluating items of 'choices' is not evaluated when class SomeNeModel is inicialized.
+    It is called by database model only one time when first needed, not earlier than when all odher models are initialised.
+    First possible usage is for Django commands internal attribute "requires_model_validation = True",
+    by e.g. dbsync, validate and typically commands changing structure of database.
+    """
+    for item in func():
+       yield item
+
+
+def iterchoices_db(func):
+    """Iterator for lazy evaluating of choices for database models, extended for functions which need database access to get results.
+
+    When model is thoroughly validated by database management commands (e.g. dbsync), 
+    all call to "func" are skipped to prevent possible database queries before database tables are created.
+    (Database connection could be broken without this with some db engines)
+    Other is similar to "iterchoices".
+    """
+    log = logging.getLogger('iterchoices')
+    # This test determines, for which Django manage commands should be skipped the call to "func"
+    # Skip for all commands which can be useful before dbsync. Call for runserver, runcgi, shell.
+    # For other commands is good either call or skip.
+    # I hope that this hack will remain functional until I make changes to Livesettings (and maybe enhancement to django.db)
+    # and until all users installs them. Then would not be important to skip anything.
+    if len(filter(lambda x:
+               x[0].find('/core/management/validation.') >= 0 and x[2] == 'get_validation_errors' and x[3].find('choices') >= 0 or
+               x[0].find('/core/management/base.') >= 0       and x[2] == 'execute'               and x[3].find('self.validate') >= 0 or
+               x[0].find('/core/commands/validate.') >= 0     and x[2] == 'handle_noargs'         and x[3].find('self.validate') >= 0,
+               traceback.extract_stack(limit=6))) < 2:
+       #
+       log.debug('Called model choices initialization function <%s>' % str(func).split()[1])
+       for item in func():
+           yield item
+    else:
+       log.info('Skipped model choices initialization function <%s> because of model validation (e.g. syncdb)' % str(func).split()[1])

File satchmo/apps/shipping/fields.py

-from django.db import models
 from livesettings import config_choice_values, SettingNotSet
 
 def shipping_choices():
         return config_choice_values('SHIPPING','MODULES')
     except SettingNotSet:
         return ()
-
-
-class ShippingChoiceCharField(models.CharField):
-
-    def __init__(self, *args, **kwargs):
-        choices = kwargs.pop("choices", "__DYNAMIC__")
-        if choices == "__DYNAMIC__":
-            kwargs['choices'] = shipping_choices()
-
-        super(ShippingChoiceCharField, self).__init__(*args, **kwargs)
-
-try:
-    # South introspection rules for our custom field.
-    from south.modelsinspector import add_introspection_rules, matching_details
-
-    # get the kwargs for a Field instance
-    # we're using Field, as CharField doesn't change __init__()
-    _args, kwargs = matching_details(models.Field())
-
-    add_introspection_rules([(
-        (ShippingChoiceCharField, ),
-        [],
-        kwargs,
-    )], ['shipping\.fields'])
-except ImportError:
-    pass