Преглед на файлове

Replace countries.json fixture by management command

I started looking at this because on the mailing list, having the UK as
only shipping country led to confusion. This is mostly due to the
shipping address form hiding the country field if there's only one
country enabled, but all the validation then requiring UK postcodes and
phone numbers.

It's bothered me for a while that we're using a fixture to populate the
countries, which is at risk of becoming stale.
pycountry offers an excellent data source for a list of countries, so I
polished an existing management command to use it to populate the
country database.

This commit has two immediate effects:
* New setups will use a more current country database
* By default, all countries will be marked as shipping countries

pycountry also ships with localised names of the countries, which should
allow us to populate the database with localised country names.
master
Maik Hoepfel преди 11 години
родител
ревизия
97c3c4d405

+ 5
- 2
Makefile Целия файл

18
 	sites/sandbox/manage.py loaddata sites/sandbox/fixtures/variants.json
18
 	sites/sandbox/manage.py loaddata sites/sandbox/fixtures/variants.json
19
 	sites/sandbox/manage.py oscar_import_catalogue sites/sandbox/fixtures/*.csv
19
 	sites/sandbox/manage.py oscar_import_catalogue sites/sandbox/fixtures/*.csv
20
 	sites/sandbox/manage.py oscar_import_catalogue_images sites/sandbox/fixtures/images.tar.gz
20
 	sites/sandbox/manage.py oscar_import_catalogue_images sites/sandbox/fixtures/images.tar.gz
21
-	sites/sandbox/manage.py loaddata countries.json sites/_fixtures/pages.json sites/_fixtures/auth.json sites/_fixtures/ranges.json sites/_fixtures/offers.json
21
+	sites/sandbox/manage.py oscar_populate_countries
22
+	sites/sandbox/manage.py loaddata sites/_fixtures/pages.json sites/_fixtures/auth.json sites/_fixtures/ranges.json sites/_fixtures/offers.json
22
 	sites/sandbox/manage.py clear_index --noinput
23
 	sites/sandbox/manage.py clear_index --noinput
23
 	sites/sandbox/manage.py update_index catalogue
24
 	sites/sandbox/manage.py update_index catalogue
24
 
25
 
36
 	sites/demo/manage.py syncdb --noinput
37
 	sites/demo/manage.py syncdb --noinput
37
 	sites/demo/manage.py migrate
38
 	sites/demo/manage.py migrate
38
 	# Import some core fixtures
39
 	# Import some core fixtures
39
-	sites/demo/manage.py loaddata countries.json sites/_fixtures/pages.json
40
+	sites/demo/manage.py oscar_populate_countries
41
+	sites/demo/manage.py loaddata sites/_fixtures/pages.json
40
 	# Create catalogue (create product classes from fixture than import CSV files)
42
 	# Create catalogue (create product classes from fixture than import CSV files)
41
 	sites/demo/manage.py loaddata sites/_fixtures/auth.json sites/demo/fixtures/offers.json
43
 	sites/demo/manage.py loaddata sites/_fixtures/auth.json sites/demo/fixtures/offers.json
42
 	sites/demo/manage.py loaddata sites/demo/fixtures/product-classes.json sites/demo/fixtures/product-attributes.json sites/demo/fixtures/shipping-event-types.json
44
 	sites/demo/manage.py loaddata sites/demo/fixtures/product-classes.json sites/demo/fixtures/product-attributes.json sites/demo/fixtures/shipping-event-types.json
56
 	sites/us/manage.py syncdb --noinput
58
 	sites/us/manage.py syncdb --noinput
57
 	sites/us/manage.py migrate
59
 	sites/us/manage.py migrate
58
 	# Import some fixtures
60
 	# Import some fixtures
61
+	sites/us/manage.py oscar_populate_countries
59
 	sites/us/manage.py loaddata sites/us/fixtures/*.json
62
 	sites/us/manage.py loaddata sites/us/fixtures/*.json
60
 	sites/us/manage.py loaddata sites/_fixtures/auth.json sites/_fixtures/ranges.json 
63
 	sites/us/manage.py loaddata sites/_fixtures/auth.json sites/_fixtures/ranges.json 
61
 	# Create catalogue (using a fixture from the demo site)
64
 	# Create catalogue (using a fixture from the demo site)

+ 11
- 12
docs/source/internals/getting_started.rst Целия файл

251
 
251
 
252
 The default checkout process requires a shipping address with a country.  Oscar
252
 The default checkout process requires a shipping address with a country.  Oscar
253
 uses a model for countries with flags that indicate which are valid shipping
253
 uses a model for countries with flags that indicate which are valid shipping
254
-countries and so the ``address_country`` database table must be populated before
254
+countries and so the ``country`` database table must be populated before
255
 a customer can check out.
255
 a customer can check out.
256
 
256
 
257
-This is easily achieved using fixtures.  Oscar ships with a ``countries.json``
258
-fixture that loads most countries from the `ISO 3166 standard`_.  This can loaded
259
-via::
257
+The easiest way to achieve this is to use country data from the `pycountry`_
258
+package. Oscar ships with a management command to parse that data::
260
 
259
 
261
-    $ python manage.py loaddata countries
260
+.. code-block:: bash
262
 
261
 
263
-Note however that this file only sets the UK as a valid shipping country.  If
264
-you want other countries to be available, it would make more sense to take a
265
-copy of Oscar's countries fixture and edit it as you see it before loading it.
262
+    $ pip install pycountry
263
+    [...]
264
+    $ python manage.py oscar_populate_countries
266
 
265
 
267
-Further, a simple way of loading countries for your project is to use a `data
268
-migration`_.
266
+By default, this command will mark all countries as a shipping country. Call
267
+it with the ``--no-shipping`` option to prevent that. You then need to
268
+manually mark at least one country as a shipping country.
269
 
269
 
270
-.. _`ISO 3166 standard`: http://en.wikipedia.org/wiki/ISO_3166
271
-.. _`data migration`: http://codeinthehole.com/writing/prefer-data-migrations-to-initial-data/
270
+.. _pycountry: https://pypi.python.org/pypi/pycountry
272
 
271
 
273
 
272
 
274
 Creating product classes and fulfillment partners
273
 Creating product classes and fulfillment partners

+ 4
- 0
docs/source/releases/v0.8.rst Целия файл

200
   optional. It usually is desired behaviour, but can slow down an app when
200
   optional. It usually is desired behaviour, but can slow down an app when
201
   using a remote storage.
201
   using a remote storage.
202
 
202
 
203
+* Oscar now ships with a ``oscar_populate_countries`` management command to
204
+  populate the country databases. It replaces the ``countries.json`` fixture.
205
+  The command relies on the ``pycountry`` library being installed.
206
+
203
 .. _incompatible_changes_in_0.8:
207
 .. _incompatible_changes_in_0.8:
204
 
208
 
205
 Backwards incompatible changes in 0.8
209
 Backwards incompatible changes in 0.8

+ 2
- 3
oscar/apps/address/abstract_models.py Целия файл

387
     iso_3166_1_numeric = models.CharField(
387
     iso_3166_1_numeric = models.CharField(
388
         _('ISO 3166-1 numeric'), blank=True, max_length=3)
388
         _('ISO 3166-1 numeric'), blank=True, max_length=3)
389
 
389
 
390
-    #: The commonly used name
391
-    #: e.g. 'United Kingdom'
390
+    #: The commonly used name; e.g. 'United Kingdom'
392
     printable_name = models.CharField(_('Country name'), max_length=128)
391
     printable_name = models.CharField(_('Country name'), max_length=128)
393
     #: The full official name of a country
392
     #: The full official name of a country
394
     #: e.g. 'United Kingdom of Great Britain and Northern Ireland'
393
     #: e.g. 'United Kingdom of Great Britain and Northern Ireland'
405
         abstract = True
404
         abstract = True
406
         verbose_name = _('Country')
405
         verbose_name = _('Country')
407
         verbose_name_plural = _('Countries')
406
         verbose_name_plural = _('Countries')
408
-        ordering = ('-display_order', 'name',)
407
+        ordering = ('-display_order', 'printable_name',)
409
 
408
 
410
     def __unicode__(self):
409
     def __unicode__(self):
411
         return self.printable_name or self.name
410
         return self.printable_name or self.name

+ 0
- 2930
oscar/fixtures/countries.json
Файловите разлики са ограничени, защото са твърде много
Целия файл


+ 48
- 0
oscar/management/commands/oscar_populate_countries.py Целия файл

1
+# -*- coding: utf-8 -*-
2
+from optparse import make_option
3
+
4
+from django.core.management.base import BaseCommand, CommandError
5
+from oscar.core.loading import get_model
6
+
7
+Country = get_model('address', 'Country')
8
+
9
+
10
+class Command(BaseCommand):
11
+    help = "Populates the list of countries with data from pycountry."
12
+    # TODO: Allow setting locale to fetch country names in right locale
13
+    # https://code.djangoproject.com/ticket/6376
14
+
15
+    option_list = BaseCommand.option_list + (
16
+        make_option(
17
+            '--no-shipping',
18
+            action='store_false',
19
+            dest='is_shipping',
20
+            default=True,
21
+            help="Don't mark countries for shipping"),
22
+    )
23
+
24
+    def handle(self, *args, **options):
25
+        try:
26
+            import pycountry
27
+        except ImportError:
28
+            raise CommandError(
29
+                "You are missing the pycountry library. Install it with "
30
+                "'pip install pycountry'")
31
+
32
+        if Country.objects.exists():
33
+            raise CommandError(
34
+                "You already have countries in your database. This command"
35
+                "currently does not support updating existing countries.")
36
+
37
+        countries = [
38
+            Country(
39
+                iso_3166_1_a2=country.alpha2,
40
+                iso_3166_1_a3=country.alpha3,
41
+                iso_3166_1_numeric=country.numeric,
42
+                printable_name=country.name,
43
+                name=getattr(country, 'official_name', ''),
44
+                is_shipping_country=options['is_shipping'])
45
+            for country in pycountry.countries]
46
+
47
+        Country.objects.bulk_create(countries)
48
+        self.stdout.write("Successfully added %s countries." % len(countries))

+ 3
- 0
requirements.txt Целия файл

34
 pyprof2calltree==1.3.1
34
 pyprof2calltree==1.3.1
35
 ipdb>=0.8,<0.9
35
 ipdb>=0.8,<0.9
36
 ipython>=1.1.0,<1.2.0
36
 ipython>=1.1.0,<1.2.0
37
+
38
+# Country data
39
+pycountry>=1.8,<2.0

+ 0
- 2930
sites/us/fixtures/countries.json
Файловите разлики са ограничени, защото са твърде много
Целия файл


+ 2
- 4
tests/integration/order/model_tests.py Целия файл

5
 from django.utils import timezone
5
 from django.utils import timezone
6
 import mock
6
 import mock
7
 
7
 
8
-from oscar.apps.address.models import Country
9
 from oscar.apps.order.models import ShippingAddress, Order, Line, \
8
 from oscar.apps.order.models import ShippingAddress, Order, Line, \
10
         ShippingEvent, ShippingEventType, ShippingEventQuantity, OrderNote, \
9
         ShippingEvent, ShippingEventType, ShippingEventQuantity, OrderNote, \
11
         OrderDiscount
10
         OrderDiscount
12
 from oscar.apps.order.exceptions import (InvalidOrderStatus, InvalidLineStatus,
11
 from oscar.apps.order.exceptions import (InvalidOrderStatus, InvalidLineStatus,
13
                                          InvalidShippingEvent)
12
                                          InvalidShippingEvent)
14
-from oscar.test.factories import create_order, create_offer, create_voucher, create_basket
13
+from oscar.test.factories import create_order, create_offer, create_voucher, create_basket, CountryFactory
15
 from oscar.test.basket import add_product
14
 from oscar.test.basket import add_product
16
 
15
 
17
 ORDER_PLACED = 'order_placed'
16
 ORDER_PLACED = 'order_placed'
18
 
17
 
19
 
18
 
20
 class ShippingAddressTest(TestCase):
19
 class ShippingAddressTest(TestCase):
21
-    fixtures = ['countries.json']
22
 
20
 
23
     def test_titleless_salutation_is_stripped(self):
21
     def test_titleless_salutation_is_stripped(self):
24
-        country = Country.objects.get(iso_3166_1_a2='GB')
22
+        country = CountryFactory()
25
         a = ShippingAddress.objects.create(
23
         a = ShippingAddress.objects.create(
26
             last_name='Barrington', line1="75 Smith Road", postcode="N4 8TY", country=country)
24
             last_name='Barrington', line1="75 Smith Road", postcode="N4 8TY", country=country)
27
         self.assertEqual("Barrington", a.salutation)
25
         self.assertEqual("Barrington", a.salutation)

Loading…
Отказ
Запис