Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

abstract_models.py 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. import zlib
  2. from django.db import models
  3. from django.utils.translation import ugettext_lazy as _
  4. class AbstractAddress(models.Model):
  5. """
  6. Superclass address object
  7. This is subclassed and extended to provide models for
  8. user, shipping and billing addresses.
  9. The only required fields are last_name, line1 and postcode.
  10. """
  11. # @todo: Need a way of making these choice lists configurable
  12. # per project
  13. MR, MISS, MRS, MS, DR = ('Mr', 'Miss', 'Mrs', 'Ms', 'Dr')
  14. TITLE_CHOICES = (
  15. (MR, _("Mr")),
  16. (MISS, _("Miss")),
  17. (MRS, _("Mrs")),
  18. (MS, _("Ms")),
  19. (DR, _("Dr")),
  20. )
  21. title = models.CharField(_("Title"), max_length=64, choices=TITLE_CHOICES, blank=True, null=True)
  22. first_name = models.CharField(_("First name"), max_length=255, blank=True, null=True)
  23. last_name = models.CharField(_("Last name"), max_length=255, blank=True)
  24. # We use quite a few lines of an address as they are often quite long and
  25. # it's easier to just hide the unnecessary ones than add extra ones.
  26. line1 = models.CharField(_("First line of address"), max_length=255)
  27. line2 = models.CharField(_("Second line of address"), max_length=255, blank=True, null=True)
  28. line3 = models.CharField(_("Third line of address"), max_length=255, blank=True, null=True)
  29. line4 = models.CharField(_("City"), max_length=255, blank=True, null=True)
  30. state = models.CharField(_("State/County"), max_length=255, blank=True, null=True)
  31. postcode = models.CharField(_("Post/Zip-code"), max_length=64)
  32. country = models.ForeignKey('address.Country')
  33. # A field only used for searching addresses - this contains all the relevant fields
  34. search_text = models.CharField(_("Search text"), max_length=1000)
  35. class Meta:
  36. abstract = True
  37. def save(self, *args, **kwargs):
  38. self._clean_fields()
  39. self._update_search_text()
  40. super(AbstractAddress, self).save(*args, **kwargs)
  41. def _clean_fields(self):
  42. """
  43. Clean up fields
  44. """
  45. for field in ['first_name', 'last_name', 'line1', 'line2', 'line3', 'line4', 'postcode']:
  46. if self.__dict__[field]:
  47. self.__dict__[field] = self.__dict__[field].strip()
  48. # Ensure postcodes are always uppercase
  49. if self.postcode:
  50. self.postcode = self.postcode.upper()
  51. def _update_search_text(self):
  52. search_fields = filter(lambda x: x, [self.first_name, self.last_name,
  53. self.line1, self.line2, self.line3, self.line4, self.state,
  54. self.postcode, self.country.name])
  55. self.search_text = ' '.join(search_fields)
  56. @property
  57. def summary(self):
  58. """
  59. Returns a single string summary of the address,
  60. separating fields using commas.
  61. """
  62. return u", ".join(self.active_address_fields())
  63. def populate_alternative_model(self, address_model):
  64. """
  65. For populating an address model using the matching fields
  66. from this one.
  67. This is used to convert a user address to a shipping address
  68. as part of the checkout process.
  69. """
  70. destination_field_names = [field.name for field in address_model._meta.fields]
  71. for field_name in [field.name for field in self._meta.fields]:
  72. if field_name in destination_field_names and field_name != 'id':
  73. setattr(address_model, field_name, getattr(self, field_name))
  74. def active_address_fields(self):
  75. u"""
  76. Returns the non-empty components of the address, but merging the
  77. title, first_name and last_name into a single line.
  78. """
  79. self._clean_fields()
  80. fields = filter(lambda x: x, [self.salutation(), self.line1, self.line2, self.line3,
  81. self.line4, self.postcode])
  82. if self.country:
  83. fields.append(self.country.name)
  84. return fields
  85. def salutation(self):
  86. u"""Returns the salutation"""
  87. return u" ".join([part for part in [self.title, self.first_name, self.last_name] if part])
  88. def name(self):
  89. """
  90. Returns the full name
  91. """
  92. return u" ".join([part for part in [self.first_name, self.last_name] if part])
  93. def __unicode__(self):
  94. return self.summary
  95. class AbstractCountry(models.Model):
  96. """
  97. International Organization for Standardization (ISO) 3166-1 Country list.
  98. """
  99. iso_3166_1_a2 = models.CharField(_('ISO 3166-1 alpha-2'), max_length=2, primary_key=True)
  100. iso_3166_1_a3 = models.CharField(_('ISO 3166-1 alpha-3'), max_length=3, null=True, db_index=True)
  101. iso_3166_1_numeric = models.PositiveSmallIntegerField(_('ISO 3166-1 numeric'), null=True, db_index=True)
  102. name = models.CharField(_('Official name (CAPS)'), max_length=128)
  103. printable_name = models.CharField(_('Country name'), max_length=128)
  104. is_highlighted = models.BooleanField(default=False, db_index=True)
  105. is_shipping_country = models.BooleanField(default=False, db_index=True)
  106. class Meta:
  107. abstract = True
  108. verbose_name = _('Country')
  109. verbose_name_plural = _('Countries')
  110. ordering = ('-is_highlighted', 'name',)
  111. def __unicode__(self):
  112. return self.printable_name
  113. class AbstractShippingAddress(AbstractAddress):
  114. u"""
  115. Shipping address.
  116. A shipping address should not be edited once the order has been placed -
  117. it should be read-only after that.
  118. """
  119. phone_number = models.CharField(max_length=32, blank=True, null=True)
  120. notes = models.TextField(blank=True, null=True, help_text="""Shipping notes""")
  121. class Meta:
  122. abstract = True
  123. verbose_name_plural = "shipping addresses"
  124. class AbstractUserAddress(AbstractShippingAddress):
  125. """
  126. A user address which forms an "AddressBook" for a user.
  127. We use a separate model to shipping and billing (even though there will be
  128. some data duplication) because we don't want shipping/billing addresses changed
  129. or deleted once an order has been placed. By having a separate model, we allow
  130. users the ability to add/edit/delete from their address book without affecting
  131. orders already placed.
  132. """
  133. user = models.ForeignKey('auth.User', related_name='addresses')
  134. # Customers can set defaults
  135. is_default_for_shipping = models.BooleanField(default=False)
  136. is_default_for_billing = models.BooleanField(default=False)
  137. # We keep track of the number of times an address has been used
  138. # as a shipping address so we can show the most popular ones
  139. # first at the checkout.
  140. num_orders = models.PositiveIntegerField(default=0)
  141. # A hash is kept to try and avoid duplicate addresses being added
  142. # to the address book.
  143. hash = models.CharField(max_length=255, db_index=True)
  144. date_created = models.DateTimeField(auto_now_add=True)
  145. def generate_hash(self):
  146. u"""Returns a hash of the address summary."""
  147. # We use an upper-case version of the summary
  148. return zlib.crc32(self.summary.strip().upper().encode('UTF8'))
  149. def save(self, *args, **kwargs):
  150. u"""Save a hash of the address fields"""
  151. # Save a hash of the address fields so we can check whether two
  152. # addresses are the same to avoid saving duplicates
  153. self.hash = self.generate_hash()
  154. super(AbstractUserAddress, self).save(*args, **kwargs)
  155. class Meta:
  156. abstract = True
  157. verbose_name_plural = "User addresses"
  158. ordering = ['-num_orders']
  159. class AbstractBillingAddress(AbstractAddress):
  160. class Meta:
  161. abstract = True
  162. verbose_name_plural = "Billing addresses"