Преглед изворни кода

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,7 +18,8 @@ sandbox: install
18 18
 	sites/sandbox/manage.py loaddata sites/sandbox/fixtures/variants.json
19 19
 	sites/sandbox/manage.py oscar_import_catalogue sites/sandbox/fixtures/*.csv
20 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 23
 	sites/sandbox/manage.py clear_index --noinput
23 24
 	sites/sandbox/manage.py update_index catalogue
24 25
 
@@ -36,7 +37,8 @@ demo: install
36 37
 	sites/demo/manage.py syncdb --noinput
37 38
 	sites/demo/manage.py migrate
38 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 42
 	# Create catalogue (create product classes from fixture than import CSV files)
41 43
 	sites/demo/manage.py loaddata sites/_fixtures/auth.json sites/demo/fixtures/offers.json
42 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,6 +58,7 @@ us_site: install
56 58
 	sites/us/manage.py syncdb --noinput
57 59
 	sites/us/manage.py migrate
58 60
 	# Import some fixtures
61
+	sites/us/manage.py oscar_populate_countries
59 62
 	sites/us/manage.py loaddata sites/us/fixtures/*.json
60 63
 	sites/us/manage.py loaddata sites/_fixtures/auth.json sites/_fixtures/ranges.json 
61 64
 	# Create catalogue (using a fixture from the demo site)

+ 11
- 12
docs/source/internals/getting_started.rst Прегледај датотеку

@@ -251,24 +251,23 @@ Fixtures
251 251
 
252 252
 The default checkout process requires a shipping address with a country.  Oscar
253 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 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 273
 Creating product classes and fulfillment partners

+ 4
- 0
docs/source/releases/v0.8.rst Прегледај датотеку

@@ -200,6 +200,10 @@ Minor changes
200 200
   optional. It usually is desired behaviour, but can slow down an app when
201 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 207
 .. _incompatible_changes_in_0.8:
204 208
 
205 209
 Backwards incompatible changes in 0.8

+ 2
- 3
oscar/apps/address/abstract_models.py Прегледај датотеку

@@ -387,8 +387,7 @@ class AbstractCountry(models.Model):
387 387
     iso_3166_1_numeric = models.CharField(
388 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 391
     printable_name = models.CharField(_('Country name'), max_length=128)
393 392
     #: The full official name of a country
394 393
     #: e.g. 'United Kingdom of Great Britain and Northern Ireland'
@@ -405,7 +404,7 @@ class AbstractCountry(models.Model):
405 404
         abstract = True
406 405
         verbose_name = _('Country')
407 406
         verbose_name_plural = _('Countries')
408
-        ordering = ('-display_order', 'name',)
407
+        ordering = ('-display_order', 'printable_name',)
409 408
 
410 409
     def __unicode__(self):
411 410
         return self.printable_name or self.name

+ 0
- 2930
oscar/fixtures/countries.json
Разлика између датотеке није приказан због своје велике величине
Прегледај датотеку


+ 48
- 0
oscar/management/commands/oscar_populate_countries.py Прегледај датотеку

@@ -0,0 +1,48 @@
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,3 +34,6 @@ flake8>=2.1
34 34
 pyprof2calltree==1.3.1
35 35
 ipdb>=0.8,<0.9
36 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,23 +5,21 @@ from django.test import TestCase
5 5
 from django.utils import timezone
6 6
 import mock
7 7
 
8
-from oscar.apps.address.models import Country
9 8
 from oscar.apps.order.models import ShippingAddress, Order, Line, \
10 9
         ShippingEvent, ShippingEventType, ShippingEventQuantity, OrderNote, \
11 10
         OrderDiscount
12 11
 from oscar.apps.order.exceptions import (InvalidOrderStatus, InvalidLineStatus,
13 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 14
 from oscar.test.basket import add_product
16 15
 
17 16
 ORDER_PLACED = 'order_placed'
18 17
 
19 18
 
20 19
 class ShippingAddressTest(TestCase):
21
-    fixtures = ['countries.json']
22 20
 
23 21
     def test_titleless_salutation_is_stripped(self):
24
-        country = Country.objects.get(iso_3166_1_a2='GB')
22
+        country = CountryFactory()
25 23
         a = ShippingAddress.objects.create(
26 24
             last_name='Barrington', line1="75 Smith Road", postcode="N4 8TY", country=country)
27 25
         self.assertEqual("Barrington", a.salutation)

Loading…
Откажи
Сачувај