├── .gitignore ├── .travis.yml ├── LICENSE ├── MANIFEST.in ├── Procfile ├── README.md ├── app.json ├── drf_signed_auth ├── __init__.py ├── authentication.py ├── compat.py ├── settings.py ├── signing.py └── views.py ├── example ├── countries │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── fixtures │ │ ├── countries.json │ │ ├── token.json │ │ └── user.json │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── country_info.json ├── example │ ├── __init__.py │ ├── models.py │ ├── renderers.py │ ├── settings.py │ ├── urls.py │ ├── views.py │ └── wsgi.py ├── manage.py ├── static │ └── js │ │ ├── countries.js │ │ └── download-countries.js └── templates │ └── index.html ├── pylintrc ├── requirements.txt ├── requirements ├── example_app.txt ├── packaging.txt └── testing.txt ├── runtests.py ├── runtime.txt ├── setup.py ├── tests ├── __init__.py ├── integration │ ├── __init__.py │ ├── test_signing.py │ ├── urls.py │ └── views.py ├── settings.py └── unit │ ├── __init__.py │ ├── models.py │ ├── test_authentication.py │ ├── test_settings.py │ ├── test_signing.py │ └── test_views.py └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info 3 | .tox 4 | .coverage 5 | *.sqlite3 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: python 3 | 4 | python: 5 | - "3.6" 6 | - "2.7" 7 | install: pip install tox-travis codecov 8 | script: tox 9 | 10 | after_success: 11 | - codecov 12 | 13 | notifications: 14 | email: false 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, Marc Gibbons 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include LICENSE.md 3 | global-exclude __pycache__ 4 | global-exclude *.py[co] 5 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: cd example && gunicorn example.wsgi 2 | release: cd example && python manage.py migrate --noinput && python manage.py loaddata countries user token 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DRF Signed Auth 2 | A stateless authentication backend intended to temporarily expose protected 3 | resources. 4 | 5 | [![Build Status](https://travis-ci.org/marcgibbons/drf_signed_auth.png?branch=master)](https://travis-ci.org/marcgibbons/drf_signed_auth) 6 | [![Code Coverage](https://codecov.io/gh/marcgibbons/drf_signed_auth/branch/master/graph/badge.svg)](https://codecov.io/gh/marcgibbons/drf_signed_auth) 7 | [![PyPI Version](https://img.shields.io/pypi/v/drf-signed-auth.svg)](https://pypi.python.org/pypi/drf-signed-auth/0.1.1) 8 | 9 | 10 | ## Example app 11 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) 12 | 13 | Deploy your own, or try out https://drf-signed-auth.herokuapp.com 14 | 15 | 16 | ## Why? 17 | 18 | The motivation for this package comes from a frequent project requirement to 19 | directly download served by the API in formats like CSV or Excel within the 20 | context of a single-page-application. 21 | 22 | Within this context, authentication cannot be achieved using HTTP Headers, as 23 | the resource is accessed directly through a URL via an `` tag. Therefore, a 24 | temporary signature passed in the query string must be used to authenticate the 25 | request. 26 | 27 | 28 | This package uses Django's cryptographic signing to produce a short-lived 29 | signature. It provides a view used to produce the signature and a DRF 30 | authentication backend. 31 | 32 | 33 | ## Is this secure? 34 | Use this backend with caution and sparingly. Anyone with a copy of the signed 35 | URL will be able to access a protected resource, so keep the expiry time 36 | short (see settings), and ensure that the Django `SECRET_KEY` setting is kept 37 | private. 38 | 39 | 40 | ## Requirements 41 | - Python 2.7 / 3.6 42 | - Django 1.8, 1.9, 1.10, 1.11 43 | - Django REST Framework 3.6, 3.7 44 | 45 | 46 | ## Installation 47 | `pip install drf-signed-auth` 48 | 49 | 50 | ## Quick start 51 | Register the SignUrlView in `urls.py` 52 | 53 | ```python 54 | # urls.py 55 | 56 | from django.conf.urls import url 57 | from drf_signed_auth.views import SignUrlView 58 | 59 | 60 | urlpatterns = [ 61 | ... 62 | url(r'^sign-url/$', SignUrlView.as_view(), name='sign-url'), 63 | ... 64 | ] 65 | ``` 66 | 67 | Use the authentication backend on the view you wish to expose. 68 | 69 | ```python 70 | # views.py 71 | from drf_signed_auth.authentication import SignedURLAuthentication 72 | from rest_framework.permissions import IsAuthenticated 73 | from rest_framework.views import APIView 74 | 75 | 76 | class MyCSVView(APIView): 77 | ... 78 | authentication_classes = [SignedURLAuthentication] 79 | permission_classes = [IsAuthenticated] 80 | ... 81 | ``` 82 | 83 | ## Usage 84 | 85 | Obtain the signature by making a POST request to the Sign URL endpoint, and 86 | provide the `url` of the endpoint you wish to access. This can be a relative 87 | or absolute path. 88 | 89 | ### Example 90 | 91 | ``` 92 | # Request 93 | POST /sign-url HTTP/1.1 94 | HOST your.api.host 95 | Content-Type: application/json 96 | 97 | {"url": "/path"} 98 | 99 | 100 | # Response 101 | http://your.api.host/path?sig=xxxxxxxxxxxxxxx 102 | ``` 103 | 104 | The returned URL will be valid for the time specified by the `SIGNED_URL_TTL`. 105 | 106 | 107 | ## Settings 108 | 109 | The following settings may be configured in your project's `settings.py` 110 | 111 | | Setting | Description | Default | 112 | | --- | --- | --- | 113 | | `SIGNED_URL_TTL` | The time in seconds for which the signature is valid | `30` (seconds) | 114 | | `SIGNED_URL_QUERY_PARAM` | The querystring variable name | `sig` | 115 | | `SIGNED_URL_PERMISSION_CLASSES` | Permission classes on the signed URL view | `[rest_framework.permissions.IsAuthenticated]` | 116 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Example app using DRF Signed Auth", 3 | "description": "An example application demonstrating the direct CSV download of a protected endpoint", 4 | "repository": "https://github.com/marcgibbons/drf_signed_auth", 5 | "keywords": ["django", "auth", "rest", "framework"], 6 | } 7 | -------------------------------------------------------------------------------- /drf_signed_auth/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.1.1' 2 | -------------------------------------------------------------------------------- /drf_signed_auth/authentication.py: -------------------------------------------------------------------------------- 1 | """ 2 | Provides Django REST Framework authentication backend 3 | to authenticate signed URL requests. 4 | """ 5 | from django.core import signing 6 | from django.utils.translation import ugettext_lazy as _ 7 | from rest_framework import authentication, exceptions 8 | 9 | from . import settings 10 | from .signing import UserSigner 11 | 12 | 13 | class SignedURLAuthentication(authentication.BaseAuthentication): 14 | """ 15 | Authentication backend for signed URLs. 16 | """ 17 | def authenticate(self, request): 18 | """ 19 | Returns authenticated user if URL signature is valid. 20 | """ 21 | signer = UserSigner() 22 | sig = request.query_params.get(settings.SIGNED_URL_QUERY_PARAM) 23 | if not sig: 24 | return 25 | 26 | try: 27 | user = signer.unsign(sig) 28 | except signing.SignatureExpired: 29 | raise exceptions.AuthenticationFailed(_('This URL has expired.')) 30 | except signing.BadSignature: 31 | raise exceptions.AuthenticationFailed(_('Invalid signature.')) 32 | if not user.is_active: 33 | raise exceptions.AuthenticationFailed( 34 | _('User inactive or deleted.') 35 | ) 36 | 37 | return (user, None) 38 | -------------------------------------------------------------------------------- /drf_signed_auth/compat.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=W0611 2 | try: 3 | from django.urls import reverse 4 | except ImportError: 5 | from django.core.urlresolvers import reverse 6 | 7 | 8 | try: 9 | from unittest import mock 10 | except ImportError: 11 | import mock 12 | 13 | 14 | try: 15 | from importlib import reload 16 | except ImportError: 17 | reload = reload 18 | -------------------------------------------------------------------------------- /drf_signed_auth/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains app-settings imported from Django with default values 3 | """ 4 | from django.conf import settings 5 | from rest_framework import permissions 6 | 7 | # Time (in seconds) URL is valid 8 | # Default: 30 9 | SIGNED_URL_TTL = getattr(settings, 'SIGNED_URL_TTL', 30) 10 | 11 | # Query parameter variable name 12 | # Default: 'sig' => /fizz?sig= 13 | SIGNED_URL_QUERY_PARAM = getattr(settings, 'SIGNED_URL_QUERY_PARAM', 'sig') 14 | 15 | # Permission classes for signing view. 16 | # Default: IsAuthenticated 17 | SIGNED_URL_PERMISSION_CLASSES = getattr( 18 | settings, 19 | 'SIGNED_URL_PERMISSION_CLASSES', 20 | [permissions.IsAuthenticated] 21 | ) 22 | -------------------------------------------------------------------------------- /drf_signed_auth/signing.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains cryptographic signing of the user model 3 | """ 4 | from django.contrib.auth import get_user_model 5 | from django.core import signing 6 | 7 | from . import settings 8 | 9 | 10 | class UserSigner: 11 | """ 12 | Signs/unsigns user object with an expiry. 13 | """ 14 | signer_class = signing.TimestampSigner 15 | 16 | def sign(self, user): 17 | """ 18 | Creates signatures for user object. 19 | """ 20 | signer = self.signer_class() 21 | data = { 22 | 'user_id': user.pk, 23 | 'username': user.get_username() # Support custom user models 24 | } 25 | 26 | return signer.sign(signing.dumps(data)) 27 | 28 | def unsign(self, signature, max_age=settings.SIGNED_URL_TTL): 29 | """ 30 | Returns fresh user object for a valid signature. 31 | """ 32 | User = get_user_model() 33 | signer = self.signer_class() 34 | data = signing.loads(signer.unsign(signature, max_age)) 35 | 36 | if not isinstance(data, dict): 37 | raise signing.BadSignature() 38 | try: 39 | return User.objects.get(**{ 40 | 'pk': data.get('user_id'), 41 | User.USERNAME_FIELD: data.get('username') 42 | }) 43 | except User.DoesNotExist: 44 | raise signing.BadSignature() 45 | -------------------------------------------------------------------------------- /drf_signed_auth/views.py: -------------------------------------------------------------------------------- 1 | from furl import furl 2 | from rest_framework import exceptions, response, views 3 | 4 | from . import settings 5 | from .signing import UserSigner 6 | 7 | 8 | class SignUrlView(views.APIView): 9 | """ 10 | Adds authentication signature to provided URL 11 | 12 | url -- URL to be wrapped 13 | """ 14 | param = settings.SIGNED_URL_QUERY_PARAM 15 | permission_classes = settings.SIGNED_URL_PERMISSION_CLASSES 16 | 17 | def post(self, request): 18 | url = request.data.get('url') 19 | if not url: 20 | raise exceptions.ValidationError('`url` must be provided') 21 | 22 | return response.Response(self.get_signed_url(url)) 23 | 24 | def get_signed_url(self, url): 25 | """ 26 | Returns provided URL with an authentication 27 | signature. 28 | """ 29 | signer = UserSigner() 30 | url = self.request.build_absolute_uri(url) 31 | signature = signer.sign(user=self.request.user) 32 | 33 | return furl(url).add({self.param: signature}).url 34 | -------------------------------------------------------------------------------- /example/countries/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcgibbons/drf_signed_auth/39bdd3ea01a09a00fd3c9bf7c6d2ad6bb4dabba6/example/countries/__init__.py -------------------------------------------------------------------------------- /example/countries/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /example/countries/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CountriesConfig(AppConfig): 5 | name = 'countries' 6 | -------------------------------------------------------------------------------- /example/countries/fixtures/countries.json: -------------------------------------------------------------------------------- 1 | [{"model": "countries.country", "pk": 1, "fields": {"continent": "EU", "capital": "Andorra la Vella", "geoname_id": 3041565, "languages": "ca", "north": 42.65604389629997, "south": 42.42849259876837, "iso_alpha_3": "AND", "fips_code": "AN", "population": "84000", "east": 1.7865427778319827, "iso_numeric": "020", "area_in_sq_km": "468.0", "country_code": "AD", "west": 1.4071867141112762, "country_name": "Andorra", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 2, "fields": {"continent": "AS", "capital": "Abu Dhabi", "geoname_id": 290557, "languages": "ar-AE,fa,en,hi,ur", "north": 26.08415985107422, "south": 22.633329391479492, "iso_alpha_3": "ARE", "fips_code": "AE", "population": "4975593", "east": 56.38166046142578, "iso_numeric": "784", "area_in_sq_km": "82880.0", "country_code": "AE", "west": 51.58332824707031, "country_name": "United Arab Emirates", "continent_name": "Asia", "currency_code": "AED"}}, {"model": "countries.country", "pk": 3, "fields": {"continent": "AS", "capital": "Kabul", "geoname_id": 1149361, "languages": "fa-AF,ps,uz-AF,tk", "north": 38.483418, "south": 29.377472, "iso_alpha_3": "AFG", "fips_code": "AF", "population": "29121286", "east": 74.879448, "iso_numeric": "004", "area_in_sq_km": "647500.0", "country_code": "AF", "west": 60.478443, "country_name": "Afghanistan", "continent_name": "Asia", "currency_code": "AFN"}}, {"model": "countries.country", "pk": 4, "fields": {"continent": "NA", "capital": "St. John's", "geoname_id": 3576396, "languages": "en-AG", "north": 17.729387, "south": 16.996979, "iso_alpha_3": "ATG", "fips_code": "AC", "population": "86754", "east": -61.672421, "iso_numeric": "028", "area_in_sq_km": "443.0", "country_code": "AG", "west": -61.906425, "country_name": "Antigua and Barbuda", "continent_name": "North America", "currency_code": "XCD"}}, {"model": "countries.country", "pk": 5, "fields": {"continent": "NA", "capital": "The Valley", "geoname_id": 3573511, "languages": "en-AI", "north": 18.276901971658063, "south": 18.160292974311673, "iso_alpha_3": "AIA", "fips_code": "AV", "population": "13254", "east": -62.96655544577948, "iso_numeric": "660", "area_in_sq_km": "102.0", "country_code": "AI", "west": -63.16808989603879, "country_name": "Anguilla", "continent_name": "North America", "currency_code": "XCD"}}, {"model": "countries.country", "pk": 6, "fields": {"continent": "EU", "capital": "Tirana", "geoname_id": 783754, "languages": "sq,el", "north": 42.6611669383269, "south": 39.6448624829142, "iso_alpha_3": "ALB", "fips_code": "AL", "population": "2986952", "east": 21.0574334835312, "iso_numeric": "008", "area_in_sq_km": "28748.0", "country_code": "AL", "west": 19.2639112711741, "country_name": "Albania", "continent_name": "Europe", "currency_code": "ALL"}}, {"model": "countries.country", "pk": 7, "fields": {"continent": "AS", "capital": "Yerevan", "geoname_id": 174982, "languages": "hy", "north": 41.301834, "south": 38.830528, "iso_alpha_3": "ARM", "fips_code": "AM", "population": "2968000", "east": 46.772435045159995, "iso_numeric": "051", "area_in_sq_km": "29800.0", "country_code": "AM", "west": 43.44978, "country_name": "Armenia", "continent_name": "Asia", "currency_code": "AMD"}}, {"model": "countries.country", "pk": 8, "fields": {"continent": "AF", "capital": "Luanda", "geoname_id": 3351879, "languages": "pt-AO", "north": -4.376826, "south": -18.042076, "iso_alpha_3": "AGO", "fips_code": "AO", "population": "13068161", "east": 24.082119, "iso_numeric": "024", "area_in_sq_km": "1246700.0", "country_code": "AO", "west": 11.679219, "country_name": "Angola", "continent_name": "Africa", "currency_code": "AOA"}}, {"model": "countries.country", "pk": 9, "fields": {"continent": "AN", "capital": "", "geoname_id": 6697173, "languages": "", "north": -60.515533, "south": -89.9999, "iso_alpha_3": "ATA", "fips_code": "AY", "population": "0", "east": 179.9999, "iso_numeric": "010", "area_in_sq_km": "1.4E7", "country_code": "AQ", "west": -179.9999, "country_name": "Antarctica", "continent_name": "Antarctica", "currency_code": ""}}, {"model": "countries.country", "pk": 10, "fields": {"continent": "SA", "capital": "Buenos Aires", "geoname_id": 3865483, "languages": "es-AR,en,it,de,fr,gn", "north": -21.777951173, "south": -55.0576984539999, "iso_alpha_3": "ARG", "fips_code": "AR", "population": "41343201", "east": -53.637962552, "iso_numeric": "032", "area_in_sq_km": "2766890.0", "country_code": "AR", "west": -73.566302817, "country_name": "Argentina", "continent_name": "South America", "currency_code": "ARS"}}, {"model": "countries.country", "pk": 11, "fields": {"continent": "OC", "capital": "Pago Pago", "geoname_id": 5880801, "languages": "en-AS,sm,to", "north": -11.0497, "south": -14.382478, "iso_alpha_3": "ASM", "fips_code": "AQ", "population": "57881", "east": -169.416077, "iso_numeric": "016", "area_in_sq_km": "199.0", "country_code": "AS", "west": -171.091888, "country_name": "American Samoa", "continent_name": "Oceania", "currency_code": "USD"}}, {"model": "countries.country", "pk": 12, "fields": {"continent": "EU", "capital": "Vienna", "geoname_id": 2782113, "languages": "de-AT,hr,hu,sl", "north": 49.0211627691393, "south": 46.3726520216244, "iso_alpha_3": "AUT", "fips_code": "AU", "population": "8205000", "east": 17.1620685652599, "iso_numeric": "040", "area_in_sq_km": "83858.0", "country_code": "AT", "west": 9.53095237240833, "country_name": "Austria", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 13, "fields": {"continent": "OC", "capital": "Canberra", "geoname_id": 2077456, "languages": "en-AU", "north": -10.062805, "south": -43.64397, "iso_alpha_3": "AUS", "fips_code": "AS", "population": "21515754", "east": 153.639252, "iso_numeric": "036", "area_in_sq_km": "7686850.0", "country_code": "AU", "west": 112.911057, "country_name": "Australia", "continent_name": "Oceania", "currency_code": "AUD"}}, {"model": "countries.country", "pk": 14, "fields": {"continent": "NA", "capital": "Oranjestad", "geoname_id": 3577279, "languages": "nl-AW,pap,es,en", "north": 12.623718127152925, "south": 12.411707706190716, "iso_alpha_3": "ABW", "fips_code": "AA", "population": "71566", "east": -69.86575120104982, "iso_numeric": "533", "area_in_sq_km": "193.0", "country_code": "AW", "west": -70.0644737196045, "country_name": "Aruba", "continent_name": "North America", "currency_code": "AWG"}}, {"model": "countries.country", "pk": 15, "fields": {"continent": "EU", "capital": "Mariehamn", "geoname_id": 661882, "languages": "sv-AX", "north": 60.488861, "south": 59.90675, "iso_alpha_3": "ALA", "fips_code": "", "population": "26711", "east": 21.011862, "iso_numeric": "248", "area_in_sq_km": "1580.0", "country_code": "AX", "west": 19.317694, "country_name": "\u00c5land", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 16, "fields": {"continent": "AS", "capital": "Baku", "geoname_id": 587116, "languages": "az,ru,hy", "north": 41.90564, "south": 38.38915252685547, "iso_alpha_3": "AZE", "fips_code": "AJ", "population": "8303512", "east": 50.370083, "iso_numeric": "031", "area_in_sq_km": "86600.0", "country_code": "AZ", "west": 44.774113, "country_name": "Azerbaijan", "continent_name": "Asia", "currency_code": "AZN"}}, {"model": "countries.country", "pk": 17, "fields": {"continent": "EU", "capital": "Sarajevo", "geoname_id": 3277605, "languages": "bs,hr-BA,sr-BA", "north": 45.239193, "south": 42.546112, "iso_alpha_3": "BIH", "fips_code": "BK", "population": "4590000", "east": 19.622223, "iso_numeric": "070", "area_in_sq_km": "51129.0", "country_code": "BA", "west": 15.718945, "country_name": "Bosnia and Herzegovina", "continent_name": "Europe", "currency_code": "BAM"}}, {"model": "countries.country", "pk": 18, "fields": {"continent": "NA", "capital": "Bridgetown", "geoname_id": 3374084, "languages": "en-BB", "north": 13.327257, "south": 13.039844, "iso_alpha_3": "BRB", "fips_code": "BB", "population": "285653", "east": -59.420376, "iso_numeric": "052", "area_in_sq_km": "431.0", "country_code": "BB", "west": -59.648922, "country_name": "Barbados", "continent_name": "North America", "currency_code": "BBD"}}, {"model": "countries.country", "pk": 19, "fields": {"continent": "AS", "capital": "Dhaka", "geoname_id": 1210997, "languages": "bn-BD,en", "north": 26.631945, "south": 20.743334, "iso_alpha_3": "BGD", "fips_code": "BG", "population": "156118464", "east": 92.673668, "iso_numeric": "050", "area_in_sq_km": "144000.0", "country_code": "BD", "west": 88.028336, "country_name": "Bangladesh", "continent_name": "Asia", "currency_code": "BDT"}}, {"model": "countries.country", "pk": 20, "fields": {"continent": "EU", "capital": "Brussels", "geoname_id": 2802361, "languages": "nl-BE,fr-BE,de-BE", "north": 51.5051118897455, "south": 49.496968483036, "iso_alpha_3": "BEL", "fips_code": "BE", "population": "10403000", "east": 6.40793743953125, "iso_numeric": "056", "area_in_sq_km": "30510.0", "country_code": "BE", "west": 2.54132898439873, "country_name": "Belgium", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 21, "fields": {"continent": "AF", "capital": "Ouagadougou", "geoname_id": 2361809, "languages": "fr-BF,mos", "north": 15.082593, "south": 9.401108, "iso_alpha_3": "BFA", "fips_code": "UV", "population": "16241811", "east": 2.405395, "iso_numeric": "854", "area_in_sq_km": "274200.0", "country_code": "BF", "west": -5.518916, "country_name": "Burkina Faso", "continent_name": "Africa", "currency_code": "XOF"}}, {"model": "countries.country", "pk": 22, "fields": {"continent": "EU", "capital": "Sofia", "geoname_id": 732800, "languages": "bg,tr-BG,rom", "north": 44.21764, "south": 41.242084, "iso_alpha_3": "BGR", "fips_code": "BU", "population": "7148785", "east": 28.612167, "iso_numeric": "100", "area_in_sq_km": "110910.0", "country_code": "BG", "west": 22.371166, "country_name": "Bulgaria", "continent_name": "Europe", "currency_code": "BGN"}}, {"model": "countries.country", "pk": 23, "fields": {"continent": "AS", "capital": "Manama", "geoname_id": 290291, "languages": "ar-BH,en,fa,ur", "north": 26.282583, "south": 25.796862, "iso_alpha_3": "BHR", "fips_code": "BA", "population": "738004", "east": 50.664471, "iso_numeric": "048", "area_in_sq_km": "665.0", "country_code": "BH", "west": 50.45414, "country_name": "Bahrain", "continent_name": "Asia", "currency_code": "BHD"}}, {"model": "countries.country", "pk": 24, "fields": {"continent": "AF", "capital": "Bujumbura", "geoname_id": 433561, "languages": "fr-BI,rn", "north": -2.310123, "south": -4.465713, "iso_alpha_3": "BDI", "fips_code": "BY", "population": "9863117", "east": 30.847729, "iso_numeric": "108", "area_in_sq_km": "27830.0", "country_code": "BI", "west": 28.993061, "country_name": "Burundi", "continent_name": "Africa", "currency_code": "BIF"}}, {"model": "countries.country", "pk": 25, "fields": {"continent": "AF", "capital": "Porto-Novo", "geoname_id": 2395170, "languages": "fr-BJ", "north": 12.418347, "south": 6.225748, "iso_alpha_3": "BEN", "fips_code": "BN", "population": "9056010", "east": 3.851701, "iso_numeric": "204", "area_in_sq_km": "112620.0", "country_code": "BJ", "west": 0.774575, "country_name": "Benin", "continent_name": "Africa", "currency_code": "XOF"}}, {"model": "countries.country", "pk": 26, "fields": {"continent": "NA", "capital": "Gustavia", "geoname_id": 3578476, "languages": "fr", "north": 17.928808791949283, "south": 17.878183227405575, "iso_alpha_3": "BLM", "fips_code": "TB", "population": "8450", "east": -62.788983372985854, "iso_numeric": "652", "area_in_sq_km": "21.0", "country_code": "BL", "west": -62.8739118253784, "country_name": "Saint Barth\u00e9lemy", "continent_name": "North America", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 27, "fields": {"continent": "NA", "capital": "Hamilton", "geoname_id": 3573345, "languages": "en-BM,pt", "north": 32.39122351646162, "south": 32.247551, "iso_alpha_3": "BMU", "fips_code": "BD", "population": "65365", "east": -64.64718648144532, "iso_numeric": "060", "area_in_sq_km": "53.0", "country_code": "BM", "west": -64.88723800000002, "country_name": "Bermuda", "continent_name": "North America", "currency_code": "BMD"}}, {"model": "countries.country", "pk": 28, "fields": {"continent": "AS", "capital": "Bandar Seri Begawan", "geoname_id": 1820814, "languages": "ms-BN,en-BN", "north": 5.047167, "south": 4.003083, "iso_alpha_3": "BRN", "fips_code": "BX", "population": "395027", "east": 115.359444, "iso_numeric": "096", "area_in_sq_km": "5770.0", "country_code": "BN", "west": 114.071442, "country_name": "Brunei", "continent_name": "Asia", "currency_code": "BND"}}, {"model": "countries.country", "pk": 29, "fields": {"continent": "SA", "capital": "Sucre", "geoname_id": 3923057, "languages": "es-BO,qu,ay", "north": -9.680567, "south": -22.896133, "iso_alpha_3": "BOL", "fips_code": "BL", "population": "9947418", "east": -57.45809600000001, "iso_numeric": "068", "area_in_sq_km": "1098580.0", "country_code": "BO", "west": -69.640762, "country_name": "Bolivia", "continent_name": "South America", "currency_code": "BOB"}}, {"model": "countries.country", "pk": 30, "fields": {"continent": "NA", "capital": "Kralendijk", "geoname_id": 7626844, "languages": "nl,pap,en", "north": 12.304535, "south": 12.017149, "iso_alpha_3": "BES", "fips_code": "", "population": "18012", "east": -68.192307, "iso_numeric": "535", "area_in_sq_km": "328.0", "country_code": "BQ", "west": -68.416458, "country_name": "Bonaire", "continent_name": "North America", "currency_code": "USD"}}, {"model": "countries.country", "pk": 31, "fields": {"continent": "SA", "capital": "Bras\u00edlia", "geoname_id": 3469034, "languages": "pt-BR,es,en,fr", "north": 5.264877, "south": -33.750706, "iso_alpha_3": "BRA", "fips_code": "BR", "population": "201103330", "east": -32.392998, "iso_numeric": "076", "area_in_sq_km": "8511965.0", "country_code": "BR", "west": -73.985535, "country_name": "Brazil", "continent_name": "South America", "currency_code": "BRL"}}, {"model": "countries.country", "pk": 32, "fields": {"continent": "NA", "capital": "Nassau", "geoname_id": 3572887, "languages": "en-BS", "north": 26.919243, "south": 22.852743, "iso_alpha_3": "BHS", "fips_code": "BF", "population": "301790", "east": -74.423874, "iso_numeric": "044", "area_in_sq_km": "13940.0", "country_code": "BS", "west": -78.995911, "country_name": "Bahamas", "continent_name": "North America", "currency_code": "BSD"}}, {"model": "countries.country", "pk": 33, "fields": {"continent": "AS", "capital": "Thimphu", "geoname_id": 1252634, "languages": "dz", "north": 28.323778, "south": 26.70764, "iso_alpha_3": "BTN", "fips_code": "BT", "population": "699847", "east": 92.125191, "iso_numeric": "064", "area_in_sq_km": "47000.0", "country_code": "BT", "west": 88.75972, "country_name": "Bhutan", "continent_name": "Asia", "currency_code": "BTN"}}, {"model": "countries.country", "pk": 34, "fields": {"continent": "AN", "capital": "", "geoname_id": 3371123, "languages": "", "north": -54.3887383509872, "south": -54.4507993522734, "iso_alpha_3": "BVT", "fips_code": "BV", "population": "0", "east": 3.434845577758324, "iso_numeric": "074", "area_in_sq_km": "49.0", "country_code": "BV", "west": 3.286776428037342, "country_name": "Bouvet Island", "continent_name": "Antarctica", "currency_code": "NOK"}}, {"model": "countries.country", "pk": 35, "fields": {"continent": "AF", "capital": "Gaborone", "geoname_id": 933860, "languages": "en-BW,tn-BW", "north": -17.780813, "south": -26.907246, "iso_alpha_3": "BWA", "fips_code": "BC", "population": "2029307", "east": 29.360781, "iso_numeric": "072", "area_in_sq_km": "600370.0", "country_code": "BW", "west": 19.999535, "country_name": "Botswana", "continent_name": "Africa", "currency_code": "BWP"}}, {"model": "countries.country", "pk": 36, "fields": {"continent": "EU", "capital": "Minsk", "geoname_id": 630336, "languages": "be,ru", "north": 56.165806, "south": 51.256416, "iso_alpha_3": "BLR", "fips_code": "BO", "population": "9685000", "east": 32.770805, "iso_numeric": "112", "area_in_sq_km": "207600.0", "country_code": "BY", "west": 23.176889, "country_name": "Belarus", "continent_name": "Europe", "currency_code": "BYN"}}, {"model": "countries.country", "pk": 37, "fields": {"continent": "NA", "capital": "Belmopan", "geoname_id": 3582678, "languages": "en-BZ,es", "north": 18.496557, "south": 15.8893, "iso_alpha_3": "BLZ", "fips_code": "BH", "population": "314522", "east": -87.776985, "iso_numeric": "084", "area_in_sq_km": "22966.0", "country_code": "BZ", "west": -89.224815, "country_name": "Belize", "continent_name": "North America", "currency_code": "BZD"}}, {"model": "countries.country", "pk": 38, "fields": {"continent": "NA", "capital": "Ottawa", "geoname_id": 6251999, "languages": "en-CA,fr-CA,iu", "north": 83.110626, "south": 41.67598, "iso_alpha_3": "CAN", "fips_code": "CA", "population": "33679000", "east": -52.636291, "iso_numeric": "124", "area_in_sq_km": "9984670.0", "country_code": "CA", "west": -141.0, "country_name": "Canada", "continent_name": "North America", "currency_code": "CAD"}}, {"model": "countries.country", "pk": 39, "fields": {"continent": "AS", "capital": "West Island", "geoname_id": 1547376, "languages": "ms-CC,en", "north": -12.072459094, "south": -12.208725839, "iso_alpha_3": "CCK", "fips_code": "CK", "population": "628", "east": 96.929489344, "iso_numeric": "166", "area_in_sq_km": "14.0", "country_code": "CC", "west": 96.816941408, "country_name": "Cocos [Keeling] Islands", "continent_name": "Asia", "currency_code": "AUD"}}, {"model": "countries.country", "pk": 40, "fields": {"continent": "AF", "capital": "Kinshasa", "geoname_id": 203312, "languages": "fr-CD,ln,ktu,kg,sw,lua", "north": 5.386098, "south": -13.455675, "iso_alpha_3": "COD", "fips_code": "CG", "population": "70916439", "east": 31.305912, "iso_numeric": "180", "area_in_sq_km": "2345410.0", "country_code": "CD", "west": 12.204144, "country_name": "Democratic Republic of the Congo", "continent_name": "Africa", "currency_code": "CDF"}}, {"model": "countries.country", "pk": 41, "fields": {"continent": "AF", "capital": "Bangui", "geoname_id": 239880, "languages": "fr-CF,sg,ln,kg", "north": 11.007569, "south": 2.220514, "iso_alpha_3": "CAF", "fips_code": "CT", "population": "4844927", "east": 27.463421, "iso_numeric": "140", "area_in_sq_km": "622984.0", "country_code": "CF", "west": 14.420097, "country_name": "Central African Republic", "continent_name": "Africa", "currency_code": "XAF"}}, {"model": "countries.country", "pk": 42, "fields": {"continent": "AF", "capital": "Brazzaville", "geoname_id": 2260494, "languages": "fr-CG,kg,ln-CG", "north": 3.703082, "south": -5.027223, "iso_alpha_3": "COG", "fips_code": "CF", "population": "3039126", "east": 18.649839, "iso_numeric": "178", "area_in_sq_km": "342000.0", "country_code": "CG", "west": 11.205009, "country_name": "Republic of the Congo", "continent_name": "Africa", "currency_code": "XAF"}}, {"model": "countries.country", "pk": 43, "fields": {"continent": "EU", "capital": "Bern", "geoname_id": 2658434, "languages": "de-CH,fr-CH,it-CH,rm", "north": 47.8098679329775, "south": 45.8191539516188, "iso_alpha_3": "CHE", "fips_code": "SZ", "population": "7581000", "east": 10.4934735095497, "iso_numeric": "756", "area_in_sq_km": "41290.0", "country_code": "CH", "west": 5.95661377423453, "country_name": "Switzerland", "continent_name": "Europe", "currency_code": "CHF"}}, {"model": "countries.country", "pk": 44, "fields": {"continent": "AF", "capital": "Yamoussoukro", "geoname_id": 2287781, "languages": "fr-CI", "north": 10.736642, "south": 4.357067, "iso_alpha_3": "CIV", "fips_code": "IV", "population": "21058798", "east": -2.494897, "iso_numeric": "384", "area_in_sq_km": "322460.0", "country_code": "CI", "west": -8.599302, "country_name": "Ivory Coast", "continent_name": "Africa", "currency_code": "XOF"}}, {"model": "countries.country", "pk": 45, "fields": {"continent": "OC", "capital": "Avarua", "geoname_id": 1899402, "languages": "en-CK,mi", "north": -10.023114, "south": -21.944164, "iso_alpha_3": "COK", "fips_code": "CW", "population": "21388", "east": -157.312134, "iso_numeric": "184", "area_in_sq_km": "240.0", "country_code": "CK", "west": -161.093658, "country_name": "Cook Islands", "continent_name": "Oceania", "currency_code": "NZD"}}, {"model": "countries.country", "pk": 46, "fields": {"continent": "SA", "capital": "Santiago", "geoname_id": 3895114, "languages": "es-CL", "north": -17.4977759459999, "south": -55.909795409, "iso_alpha_3": "CHL", "fips_code": "CI", "population": "16746491", "east": -66.416152278, "iso_numeric": "152", "area_in_sq_km": "756950.0", "country_code": "CL", "west": -80.8370287079999, "country_name": "Chile", "continent_name": "South America", "currency_code": "CLP"}}, {"model": "countries.country", "pk": 47, "fields": {"continent": "AF", "capital": "Yaound\u00e9", "geoname_id": 2233387, "languages": "en-CM,fr-CM", "north": 13.078056, "south": 1.652548, "iso_alpha_3": "CMR", "fips_code": "CM", "population": "19294149", "east": 16.192116, "iso_numeric": "120", "area_in_sq_km": "475440.0", "country_code": "CM", "west": 8.494763, "country_name": "Cameroon", "continent_name": "Africa", "currency_code": "XAF"}}, {"model": "countries.country", "pk": 48, "fields": {"continent": "AS", "capital": "Beijing", "geoname_id": 1814991, "languages": "zh-CN,yue,wuu,dta,ug,za", "north": 53.56086, "south": 15.775416, "iso_alpha_3": "CHN", "fips_code": "CH", "population": "1330044000", "east": 134.773911, "iso_numeric": "156", "area_in_sq_km": "9596960.0", "country_code": "CN", "west": 73.557693, "country_name": "China", "continent_name": "Asia", "currency_code": "CNY"}}, {"model": "countries.country", "pk": 49, "fields": {"continent": "SA", "capital": "Bogot\u00e1", "geoname_id": 3686110, "languages": "es-CO", "north": 13.380502, "south": -4.225869, "iso_alpha_3": "COL", "fips_code": "CO", "population": "47790000", "east": -66.869835, "iso_numeric": "170", "area_in_sq_km": "1138910.0", "country_code": "CO", "west": -81.728111, "country_name": "Colombia", "continent_name": "South America", "currency_code": "COP"}}, {"model": "countries.country", "pk": 50, "fields": {"continent": "NA", "capital": "San Jos\u00e9", "geoname_id": 3624060, "languages": "es-CR,en", "north": 11.216819, "south": 8.032975, "iso_alpha_3": "CRI", "fips_code": "CS", "population": "4516220", "east": -82.555992, "iso_numeric": "188", "area_in_sq_km": "51100.0", "country_code": "CR", "west": -85.950623, "country_name": "Costa Rica", "continent_name": "North America", "currency_code": "CRC"}}, {"model": "countries.country", "pk": 51, "fields": {"continent": "NA", "capital": "Havana", "geoname_id": 3562981, "languages": "es-CU,pap", "north": 23.226042, "south": 19.828083, "iso_alpha_3": "CUB", "fips_code": "CU", "population": "11423000", "east": -74.131775, "iso_numeric": "192", "area_in_sq_km": "110860.0", "country_code": "CU", "west": -84.957428, "country_name": "Cuba", "continent_name": "North America", "currency_code": "CUP"}}, {"model": "countries.country", "pk": 52, "fields": {"continent": "AF", "capital": "Praia", "geoname_id": 3374766, "languages": "pt-CV", "north": 17.197178, "south": 14.808022, "iso_alpha_3": "CPV", "fips_code": "CV", "population": "508659", "east": -22.669443, "iso_numeric": "132", "area_in_sq_km": "4033.0", "country_code": "CV", "west": -25.358747, "country_name": "Cape Verde", "continent_name": "Africa", "currency_code": "CVE"}}, {"model": "countries.country", "pk": 53, "fields": {"continent": "NA", "capital": "Willemstad", "geoname_id": 7626836, "languages": "nl,pap", "north": 12.385672, "south": 12.032745, "iso_alpha_3": "CUW", "fips_code": "UC", "population": "141766", "east": -68.733948, "iso_numeric": "531", "area_in_sq_km": "444.0", "country_code": "CW", "west": -69.157204, "country_name": "Curacao", "continent_name": "North America", "currency_code": "ANG"}}, {"model": "countries.country", "pk": 54, "fields": {"continent": "OC", "capital": "Flying Fish Cove", "geoname_id": 2078138, "languages": "en,zh,ms-CC", "north": -10.412356007, "south": -10.5704829995, "iso_alpha_3": "CXR", "fips_code": "KT", "population": "1500", "east": 105.712596992, "iso_numeric": "162", "area_in_sq_km": "135.0", "country_code": "CX", "west": 105.533276992, "country_name": "Christmas Island", "continent_name": "Oceania", "currency_code": "AUD"}}, {"model": "countries.country", "pk": 55, "fields": {"continent": "EU", "capital": "Nicosia", "geoname_id": 146669, "languages": "el-CY,tr-CY,en", "north": 35.701527, "south": 34.6332846722908, "iso_alpha_3": "CYP", "fips_code": "CY", "population": "1102677", "east": 34.59791599999994, "iso_numeric": "196", "area_in_sq_km": "9250.0", "country_code": "CY", "west": 32.27308300000004, "country_name": "Cyprus", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 56, "fields": {"continent": "EU", "capital": "Prague", "geoname_id": 3077311, "languages": "cs,sk", "north": 51.058887, "south": 48.542915, "iso_alpha_3": "CZE", "fips_code": "EZ", "population": "10476000", "east": 18.860111, "iso_numeric": "203", "area_in_sq_km": "78866.0", "country_code": "CZ", "west": 12.096194, "country_name": "Czechia", "continent_name": "Europe", "currency_code": "CZK"}}, {"model": "countries.country", "pk": 57, "fields": {"continent": "EU", "capital": "Berlin", "geoname_id": 2921044, "languages": "de", "north": 55.0583836008072, "south": 47.2701236047002, "iso_alpha_3": "DEU", "fips_code": "GM", "population": "81802257", "east": 15.0418156516163, "iso_numeric": "276", "area_in_sq_km": "357021.0", "country_code": "DE", "west": 5.8663152683722, "country_name": "Germany", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 58, "fields": {"continent": "AF", "capital": "Djibouti", "geoname_id": 223816, "languages": "fr-DJ,ar,so-DJ,aa", "north": 12.706833, "south": 10.909917, "iso_alpha_3": "DJI", "fips_code": "DJ", "population": "740528", "east": 43.416973, "iso_numeric": "262", "area_in_sq_km": "23000.0", "country_code": "DJ", "west": 41.773472, "country_name": "Djibouti", "continent_name": "Africa", "currency_code": "DJF"}}, {"model": "countries.country", "pk": 59, "fields": {"continent": "EU", "capital": "Copenhagen", "geoname_id": 2623032, "languages": "da-DK,en,fo,de-DK", "north": 57.748417, "south": 54.562389, "iso_alpha_3": "DNK", "fips_code": "DA", "population": "5484000", "east": 15.158834, "iso_numeric": "208", "area_in_sq_km": "43094.0", "country_code": "DK", "west": 8.075611, "country_name": "Denmark", "continent_name": "Europe", "currency_code": "DKK"}}, {"model": "countries.country", "pk": 60, "fields": {"continent": "NA", "capital": "Roseau", "geoname_id": 3575830, "languages": "en-DM", "north": 15.631809, "south": 15.20169, "iso_alpha_3": "DMA", "fips_code": "DO", "population": "72813", "east": -61.244152, "iso_numeric": "212", "area_in_sq_km": "754.0", "country_code": "DM", "west": -61.484108, "country_name": "Dominica", "continent_name": "North America", "currency_code": "XCD"}}, {"model": "countries.country", "pk": 61, "fields": {"continent": "NA", "capital": "Santo Domingo", "geoname_id": 3508796, "languages": "es-DO", "north": 19.9321257501267, "south": 17.5395066830409, "iso_alpha_3": "DOM", "fips_code": "DR", "population": "9823821", "east": -68.3229591969468, "iso_numeric": "214", "area_in_sq_km": "48730.0", "country_code": "DO", "west": -72.0114723981787, "country_name": "Dominican Republic", "continent_name": "North America", "currency_code": "DOP"}}, {"model": "countries.country", "pk": 62, "fields": {"continent": "AF", "capital": "Algiers", "geoname_id": 2589581, "languages": "ar-DZ", "north": 37.093723, "south": 18.960028, "iso_alpha_3": "DZA", "fips_code": "AG", "population": "34586184", "east": 11.979548, "iso_numeric": "012", "area_in_sq_km": "2381740.0", "country_code": "DZ", "west": -8.673868, "country_name": "Algeria", "continent_name": "Africa", "currency_code": "DZD"}}, {"model": "countries.country", "pk": 63, "fields": {"continent": "SA", "capital": "Quito", "geoname_id": 3658394, "languages": "es-EC", "north": 1.43523516349953, "south": -5.01615732302488, "iso_alpha_3": "ECU", "fips_code": "EC", "population": "14790608", "east": -75.1871465547501, "iso_numeric": "218", "area_in_sq_km": "283560.0", "country_code": "EC", "west": -81.0836838953894, "country_name": "Ecuador", "continent_name": "South America", "currency_code": "USD"}}, {"model": "countries.country", "pk": 64, "fields": {"continent": "EU", "capital": "Tallinn", "geoname_id": 453733, "languages": "et,ru", "north": 59.6753143130129, "south": 57.5093097920079, "iso_alpha_3": "EST", "fips_code": "EN", "population": "1291170", "east": 28.2090381531431, "iso_numeric": "233", "area_in_sq_km": "45226.0", "country_code": "EE", "west": 21.8285886498081, "country_name": "Estonia", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 65, "fields": {"continent": "AF", "capital": "Cairo", "geoname_id": 357994, "languages": "ar-EG,en,fr", "north": 31.667334, "south": 21.725389, "iso_alpha_3": "EGY", "fips_code": "EG", "population": "80471869", "east": 36.89833068847656, "iso_numeric": "818", "area_in_sq_km": "1001450.0", "country_code": "EG", "west": 24.698111, "country_name": "Egypt", "continent_name": "Africa", "currency_code": "EGP"}}, {"model": "countries.country", "pk": 66, "fields": {"continent": "AF", "capital": "La\u00e2youne / El Aai\u00fan", "geoname_id": 2461445, "languages": "ar,mey", "north": 27.669674, "south": 20.774158, "iso_alpha_3": "ESH", "fips_code": "WI", "population": "273008", "east": -8.670276, "iso_numeric": "732", "area_in_sq_km": "266000.0", "country_code": "EH", "west": -17.103182, "country_name": "Western Sahara", "continent_name": "Africa", "currency_code": "MAD"}}, {"model": "countries.country", "pk": 67, "fields": {"continent": "AF", "capital": "Asmara", "geoname_id": 338010, "languages": "aa-ER,ar,tig,kun,ti-ER", "north": 18.003084, "south": 12.359555, "iso_alpha_3": "ERI", "fips_code": "ER", "population": "5792984", "east": 43.13464, "iso_numeric": "232", "area_in_sq_km": "121320.0", "country_code": "ER", "west": 36.438778, "country_name": "Eritrea", "continent_name": "Africa", "currency_code": "ERN"}}, {"model": "countries.country", "pk": 68, "fields": {"continent": "EU", "capital": "Madrid", "geoname_id": 2510769, "languages": "es-ES,ca,gl,eu,oc", "north": 43.7913565913767, "south": 36.0001044260548, "iso_alpha_3": "ESP", "fips_code": "SP", "population": "46505963", "east": 4.32778473043961, "iso_numeric": "724", "area_in_sq_km": "504782.0", "country_code": "ES", "west": -9.30151567231899, "country_name": "Spain", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 69, "fields": {"continent": "AF", "capital": "Addis Ababa", "geoname_id": 337996, "languages": "am,en-ET,om-ET,ti-ET,so-ET,sid", "north": 14.89375, "south": 3.402422, "iso_alpha_3": "ETH", "fips_code": "ET", "population": "88013491", "east": 47.986179, "iso_numeric": "231", "area_in_sq_km": "1127127.0", "country_code": "ET", "west": 32.999939, "country_name": "Ethiopia", "continent_name": "Africa", "currency_code": "ETB"}}, {"model": "countries.country", "pk": 70, "fields": {"continent": "EU", "capital": "Helsinki", "geoname_id": 660013, "languages": "fi-FI,sv-FI,smn", "north": 70.096054, "south": 59.808777, "iso_alpha_3": "FIN", "fips_code": "FI", "population": "5244000", "east": 31.580944, "iso_numeric": "246", "area_in_sq_km": "337030.0", "country_code": "FI", "west": 20.556944, "country_name": "Finland", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 71, "fields": {"continent": "OC", "capital": "Suva", "geoname_id": 2205218, "languages": "en-FJ,fj", "north": -12.479632058714332, "south": -20.67597, "iso_alpha_3": "FJI", "fips_code": "FJ", "population": "875983", "east": -178.424438, "iso_numeric": "242", "area_in_sq_km": "18270.0", "country_code": "FJ", "west": 177.14038537647912, "country_name": "Fiji", "continent_name": "Oceania", "currency_code": "FJD"}}, {"model": "countries.country", "pk": 72, "fields": {"continent": "SA", "capital": "Stanley", "geoname_id": 3474414, "languages": "en-FK", "north": -51.2331394719999, "south": -52.383984175, "iso_alpha_3": "FLK", "fips_code": "FK", "population": "2638", "east": -57.718087652, "iso_numeric": "238", "area_in_sq_km": "12173.0", "country_code": "FK", "west": -61.3474566739999, "country_name": "Falkland Islands", "continent_name": "South America", "currency_code": "FKP"}}, {"model": "countries.country", "pk": 73, "fields": {"continent": "OC", "capital": "Palikir", "geoname_id": 2081918, "languages": "en-FM,chk,pon,yap,kos,uli,woe,nkr,kpg", "north": 10.08904, "south": 1.02629, "iso_alpha_3": "FSM", "fips_code": "FM", "population": "107708", "east": 163.03717, "iso_numeric": "583", "area_in_sq_km": "702.0", "country_code": "FM", "west": 137.33648, "country_name": "Micronesia", "continent_name": "Oceania", "currency_code": "USD"}}, {"model": "countries.country", "pk": 74, "fields": {"continent": "EU", "capital": "T\u00f3rshavn", "geoname_id": 2622320, "languages": "fo,da-FO", "north": 62.3938884414274, "south": 61.3910302656013, "iso_alpha_3": "FRO", "fips_code": "FO", "population": "48228", "east": -6.25655957192113, "iso_numeric": "234", "area_in_sq_km": "1399.0", "country_code": "FO", "west": -7.688191677774624, "country_name": "Faroe Islands", "continent_name": "Europe", "currency_code": "DKK"}}, {"model": "countries.country", "pk": 75, "fields": {"continent": "EU", "capital": "Paris", "geoname_id": 3017382, "languages": "fr-FR,frp,br,co,ca,eu,oc", "north": 51.0890012279322, "south": 41.3658213299999, "iso_alpha_3": "FRA", "fips_code": "FR", "population": "64768389", "east": 9.5596148665824, "iso_numeric": "250", "area_in_sq_km": "547030.0", "country_code": "FR", "west": -5.1389964684508, "country_name": "France", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 76, "fields": {"continent": "AF", "capital": "Libreville", "geoname_id": 2400553, "languages": "fr-GA", "north": 2.322612, "south": -3.978806, "iso_alpha_3": "GAB", "fips_code": "GB", "population": "1545255", "east": 14.502347, "iso_numeric": "266", "area_in_sq_km": "267667.0", "country_code": "GA", "west": 8.695471, "country_name": "Gabon", "continent_name": "Africa", "currency_code": "XAF"}}, {"model": "countries.country", "pk": 77, "fields": {"continent": "EU", "capital": "London", "geoname_id": 2635167, "languages": "en-GB,cy-GB,gd", "north": 59.3607741849963, "south": 49.9028622252397, "iso_alpha_3": "GBR", "fips_code": "UK", "population": "62348447", "east": 1.7689121033873, "iso_numeric": "826", "area_in_sq_km": "244820.0", "country_code": "GB", "west": -8.61772077108559, "country_name": "United Kingdom", "continent_name": "Europe", "currency_code": "GBP"}}, {"model": "countries.country", "pk": 78, "fields": {"continent": "NA", "capital": "St. George's", "geoname_id": 3580239, "languages": "en-GD", "north": 12.318283928171299, "south": 11.986893, "iso_alpha_3": "GRD", "fips_code": "GJ", "population": "107818", "east": -61.57676970108031, "iso_numeric": "308", "area_in_sq_km": "344.0", "country_code": "GD", "west": -61.802344, "country_name": "Grenada", "continent_name": "North America", "currency_code": "XCD"}}, {"model": "countries.country", "pk": 79, "fields": {"continent": "AS", "capital": "Tbilisi", "geoname_id": 614540, "languages": "ka,ru,hy,az", "north": 43.586498, "south": 41.053196, "iso_alpha_3": "GEO", "fips_code": "GG", "population": "4630000", "east": 46.725971, "iso_numeric": "268", "area_in_sq_km": "69700.0", "country_code": "GE", "west": 40.010139, "country_name": "Georgia", "continent_name": "Asia", "currency_code": "GEL"}}, {"model": "countries.country", "pk": 80, "fields": {"continent": "SA", "capital": "Cayenne", "geoname_id": 3381670, "languages": "fr-GF", "north": 5.776496, "south": 2.127094, "iso_alpha_3": "GUF", "fips_code": "FG", "population": "195506", "east": -51.613949, "iso_numeric": "254", "area_in_sq_km": "91000.0", "country_code": "GF", "west": -54.542511, "country_name": "French Guiana", "continent_name": "South America", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 81, "fields": {"continent": "EU", "capital": "St Peter Port", "geoname_id": 3042362, "languages": "en,nrf", "north": 49.731727816705416, "south": 49.40764156876899, "iso_alpha_3": "GGY", "fips_code": "GK", "population": "65228", "east": -2.1577152112246267, "iso_numeric": "831", "area_in_sq_km": "78.0", "country_code": "GG", "west": -2.673194593476069, "country_name": "Guernsey", "continent_name": "Europe", "currency_code": "GBP"}}, {"model": "countries.country", "pk": 82, "fields": {"continent": "AF", "capital": "Accra", "geoname_id": 2300660, "languages": "en-GH,ak,ee,tw", "north": 11.173301, "south": 4.736723, "iso_alpha_3": "GHA", "fips_code": "GH", "population": "24339838", "east": 1.191781, "iso_numeric": "288", "area_in_sq_km": "239460.0", "country_code": "GH", "west": -3.25542, "country_name": "Ghana", "continent_name": "Africa", "currency_code": "GHS"}}, {"model": "countries.country", "pk": 83, "fields": {"continent": "EU", "capital": "Gibraltar", "geoname_id": 2411586, "languages": "en-GI,es,it,pt", "north": 36.155439135670726, "south": 36.10903070140248, "iso_alpha_3": "GIB", "fips_code": "GI", "population": "27884", "east": -5.338285164001491, "iso_numeric": "292", "area_in_sq_km": "6.5", "country_code": "GI", "west": -5.36626149743654, "country_name": "Gibraltar", "continent_name": "Europe", "currency_code": "GIP"}}, {"model": "countries.country", "pk": 84, "fields": {"continent": "NA", "capital": "Nuuk", "geoname_id": 3425505, "languages": "kl,da-GL,en", "north": 83.627357, "south": 59.777401, "iso_alpha_3": "GRL", "fips_code": "GL", "population": "56375", "east": -11.312319, "iso_numeric": "304", "area_in_sq_km": "2166086.0", "country_code": "GL", "west": -73.04203, "country_name": "Greenland", "continent_name": "North America", "currency_code": "DKK"}}, {"model": "countries.country", "pk": 85, "fields": {"continent": "AF", "capital": "Bathurst", "geoname_id": 2413451, "languages": "en-GM,mnk,wof,wo,ff", "north": 13.826571, "south": 13.064252, "iso_alpha_3": "GMB", "fips_code": "GA", "population": "1593256", "east": -13.797793, "iso_numeric": "270", "area_in_sq_km": "11300.0", "country_code": "GM", "west": -16.825079, "country_name": "Gambia", "continent_name": "Africa", "currency_code": "GMD"}}, {"model": "countries.country", "pk": 86, "fields": {"continent": "AF", "capital": "Conakry", "geoname_id": 2420477, "languages": "fr-GN", "north": 12.67622, "south": 7.193553, "iso_alpha_3": "GIN", "fips_code": "GV", "population": "10324025", "east": -7.641071, "iso_numeric": "324", "area_in_sq_km": "245857.0", "country_code": "GN", "west": -14.926619, "country_name": "Guinea", "continent_name": "Africa", "currency_code": "GNF"}}, {"model": "countries.country", "pk": 87, "fields": {"continent": "NA", "capital": "Basse-Terre", "geoname_id": 3579143, "languages": "fr-GP", "north": 16.516848, "south": 15.867565, "iso_alpha_3": "GLP", "fips_code": "GP", "population": "443000", "east": -61.0, "iso_numeric": "312", "area_in_sq_km": "1780.0", "country_code": "GP", "west": -61.544765, "country_name": "Guadeloupe", "continent_name": "North America", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 88, "fields": {"continent": "AF", "capital": "Malabo", "geoname_id": 2309096, "languages": "es-GQ,fr", "north": 2.346989, "south": 0.92086, "iso_alpha_3": "GNQ", "fips_code": "EK", "population": "1014999", "east": 11.335724, "iso_numeric": "226", "area_in_sq_km": "28051.0", "country_code": "GQ", "west": 9.346865, "country_name": "Equatorial Guinea", "continent_name": "Africa", "currency_code": "XAF"}}, {"model": "countries.country", "pk": 89, "fields": {"continent": "EU", "capital": "Athens", "geoname_id": 390903, "languages": "el-GR,en,fr", "north": 41.7484999849641, "south": 34.8020663391466, "iso_alpha_3": "GRC", "fips_code": "GR", "population": "11000000", "east": 28.2470831714347, "iso_numeric": "300", "area_in_sq_km": "131940.0", "country_code": "GR", "west": 19.3736035624134, "country_name": "Greece", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 90, "fields": {"continent": "AN", "capital": "Grytviken", "geoname_id": 3474415, "languages": "en", "north": -53.980896636, "south": -59.46319341, "iso_alpha_3": "SGS", "fips_code": "SX", "population": "30", "east": -26.252069712, "iso_numeric": "239", "area_in_sq_km": "3903.0", "country_code": "GS", "west": -38.0479509639999, "country_name": "South Georgia and the South Sandwich Islands", "continent_name": "Antarctica", "currency_code": "GBP"}}, {"model": "countries.country", "pk": 91, "fields": {"continent": "NA", "capital": "Guatemala City", "geoname_id": 3595528, "languages": "es-GT", "north": 17.81522, "south": 13.737302, "iso_alpha_3": "GTM", "fips_code": "GT", "population": "13550440", "east": -88.223198, "iso_numeric": "320", "area_in_sq_km": "108890.0", "country_code": "GT", "west": -92.23629, "country_name": "Guatemala", "continent_name": "North America", "currency_code": "GTQ"}}, {"model": "countries.country", "pk": 92, "fields": {"continent": "OC", "capital": "Hag\u00e5t\u00f1a", "geoname_id": 4043988, "languages": "en-GU,ch-GU", "north": 13.654402, "south": 13.23376, "iso_alpha_3": "GUM", "fips_code": "GQ", "population": "159358", "east": 144.956894, "iso_numeric": "316", "area_in_sq_km": "549.0", "country_code": "GU", "west": 144.61806, "country_name": "Guam", "continent_name": "Oceania", "currency_code": "USD"}}, {"model": "countries.country", "pk": 93, "fields": {"continent": "AF", "capital": "Bissau", "geoname_id": 2372248, "languages": "pt-GW,pov", "north": 12.680789, "south": 10.924265, "iso_alpha_3": "GNB", "fips_code": "PU", "population": "1565126", "east": -13.636522, "iso_numeric": "624", "area_in_sq_km": "36120.0", "country_code": "GW", "west": -16.717535, "country_name": "Guinea-Bissau", "continent_name": "Africa", "currency_code": "XOF"}}, {"model": "countries.country", "pk": 94, "fields": {"continent": "SA", "capital": "Georgetown", "geoname_id": 3378535, "languages": "en-GY", "north": 8.557567, "south": 1.17508, "iso_alpha_3": "GUY", "fips_code": "GY", "population": "748486", "east": -56.480251, "iso_numeric": "328", "area_in_sq_km": "214970.0", "country_code": "GY", "west": -61.384762, "country_name": "Guyana", "continent_name": "South America", "currency_code": "GYD"}}, {"model": "countries.country", "pk": 95, "fields": {"continent": "AS", "capital": "Hong Kong", "geoname_id": 1819730, "languages": "zh-HK,yue,zh,en", "north": 22.559778, "south": 22.15325, "iso_alpha_3": "HKG", "fips_code": "HK", "population": "6898686", "east": 114.434753, "iso_numeric": "344", "area_in_sq_km": "1092.0", "country_code": "HK", "west": 113.837753, "country_name": "Hong Kong", "continent_name": "Asia", "currency_code": "HKD"}}, {"model": "countries.country", "pk": 96, "fields": {"continent": "AN", "capital": "", "geoname_id": 1547314, "languages": "", "north": -52.909416, "south": -53.192001, "iso_alpha_3": "HMD", "fips_code": "HM", "population": "0", "east": 73.859146, "iso_numeric": "334", "area_in_sq_km": "412.0", "country_code": "HM", "west": 72.596535, "country_name": "Heard Island and McDonald Islands", "continent_name": "Antarctica", "currency_code": "AUD"}}, {"model": "countries.country", "pk": 97, "fields": {"continent": "NA", "capital": "Tegucigalpa", "geoname_id": 3608932, "languages": "es-HN,cab,miq", "north": 16.510256, "south": 12.982411, "iso_alpha_3": "HND", "fips_code": "HO", "population": "7989415", "east": -83.155403, "iso_numeric": "340", "area_in_sq_km": "112090.0", "country_code": "HN", "west": -89.350792, "country_name": "Honduras", "continent_name": "North America", "currency_code": "HNL"}}, {"model": "countries.country", "pk": 98, "fields": {"continent": "EU", "capital": "Zagreb", "geoname_id": 3202326, "languages": "hr-HR,sr", "north": 46.53875, "south": 42.43589, "iso_alpha_3": "HRV", "fips_code": "HR", "population": "4284889", "east": 19.427389, "iso_numeric": "191", "area_in_sq_km": "56542.0", "country_code": "HR", "west": 13.493222, "country_name": "Croatia", "continent_name": "Europe", "currency_code": "HRK"}}, {"model": "countries.country", "pk": 99, "fields": {"continent": "NA", "capital": "Port-au-Prince", "geoname_id": 3723988, "languages": "ht,fr-HT", "north": 20.08782, "south": 18.021032, "iso_alpha_3": "HTI", "fips_code": "HA", "population": "9648924", "east": -71.613358, "iso_numeric": "332", "area_in_sq_km": "27750.0", "country_code": "HT", "west": -74.478584, "country_name": "Haiti", "continent_name": "North America", "currency_code": "HTG"}}, {"model": "countries.country", "pk": 100, "fields": {"continent": "EU", "capital": "Budapest", "geoname_id": 719819, "languages": "hu-HU", "north": 48.585667, "south": 45.74361, "iso_alpha_3": "HUN", "fips_code": "HU", "population": "9982000", "east": 22.906, "iso_numeric": "348", "area_in_sq_km": "93030.0", "country_code": "HU", "west": 16.111889, "country_name": "Hungary", "continent_name": "Europe", "currency_code": "HUF"}}, {"model": "countries.country", "pk": 101, "fields": {"continent": "AS", "capital": "Jakarta", "geoname_id": 1643084, "languages": "id,en,nl,jv", "north": 5.904417, "south": -10.941861, "iso_alpha_3": "IDN", "fips_code": "ID", "population": "242968342", "east": 141.021805, "iso_numeric": "360", "area_in_sq_km": "1919440.0", "country_code": "ID", "west": 95.009331, "country_name": "Indonesia", "continent_name": "Asia", "currency_code": "IDR"}}, {"model": "countries.country", "pk": 102, "fields": {"continent": "EU", "capital": "Dublin", "geoname_id": 2963597, "languages": "en-IE,ga-IE", "north": 55.3829431564742, "south": 51.4475491577615, "iso_alpha_3": "IRL", "fips_code": "EI", "population": "4622917", "east": -5.99804990172185, "iso_numeric": "372", "area_in_sq_km": "70280.0", "country_code": "IE", "west": -10.4800035816853, "country_name": "Ireland", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 103, "fields": {"continent": "AS", "capital": "", "geoname_id": 294640, "languages": "he,ar-IL,en-IL,", "north": 33.340137, "south": 29.496639, "iso_alpha_3": "ISR", "fips_code": "IS", "population": "7353985", "east": 35.876804, "iso_numeric": "376", "area_in_sq_km": "20770.0", "country_code": "IL", "west": 34.270278754419145, "country_name": "Israel", "continent_name": "Asia", "currency_code": "ILS"}}, {"model": "countries.country", "pk": 104, "fields": {"continent": "EU", "capital": "Douglas", "geoname_id": 3042225, "languages": "en,gv", "north": 54.419724, "south": 54.055916, "iso_alpha_3": "IMN", "fips_code": "IM", "population": "75049", "east": -4.3115, "iso_numeric": "833", "area_in_sq_km": "572.0", "country_code": "IM", "west": -4.798722, "country_name": "Isle of Man", "continent_name": "Europe", "currency_code": "GBP"}}, {"model": "countries.country", "pk": 105, "fields": {"continent": "AS", "capital": "New Delhi", "geoname_id": 1269750, "languages": "en-IN,hi,bn,te,mr,ta,ur,gu,kn,ml,or,pa,as,bh,sat,ks,ne,sd,kok,doi,mni,sit,sa,fr,lus,inc", "north": 35.524548272882, "south": 6.7559528993543, "iso_alpha_3": "IND", "fips_code": "IN", "population": "1173108018", "east": 97.4152926679075, "iso_numeric": "356", "area_in_sq_km": "3287590.0", "country_code": "IN", "west": 68.4840183183648, "country_name": "India", "continent_name": "Asia", "currency_code": "INR"}}, {"model": "countries.country", "pk": 106, "fields": {"continent": "AS", "capital": "", "geoname_id": 1282588, "languages": "en-IO", "north": -5.268333, "south": -7.438028, "iso_alpha_3": "IOT", "fips_code": "IO", "population": "4000", "east": 72.493164, "iso_numeric": "086", "area_in_sq_km": "60.0", "country_code": "IO", "west": 71.259972, "country_name": "British Indian Ocean Territory", "continent_name": "Asia", "currency_code": "USD"}}, {"model": "countries.country", "pk": 107, "fields": {"continent": "AS", "capital": "Baghdad", "geoname_id": 99237, "languages": "ar-IQ,ku,hy", "north": 37.378029, "south": 29.069445, "iso_alpha_3": "IRQ", "fips_code": "IZ", "population": "29671605", "east": 48.575916, "iso_numeric": "368", "area_in_sq_km": "437072.0", "country_code": "IQ", "west": 38.795887, "country_name": "Iraq", "continent_name": "Asia", "currency_code": "IQD"}}, {"model": "countries.country", "pk": 108, "fields": {"continent": "AS", "capital": "Tehran", "geoname_id": 130758, "languages": "fa-IR,ku", "north": 39.777222, "south": 25.064083, "iso_alpha_3": "IRN", "fips_code": "IR", "population": "76923300", "east": 63.317471, "iso_numeric": "364", "area_in_sq_km": "1648000.0", "country_code": "IR", "west": 44.047279, "country_name": "Iran", "continent_name": "Asia", "currency_code": "IRR"}}, {"model": "countries.country", "pk": 109, "fields": {"continent": "EU", "capital": "Reykjavik", "geoname_id": 2629691, "languages": "is,en,de,da,sv,no", "north": 66.5377933098397, "south": 63.394392778588, "iso_alpha_3": "ISL", "fips_code": "IC", "population": "308910", "east": -13.4946206239501, "iso_numeric": "352", "area_in_sq_km": "103000.0", "country_code": "IS", "west": -24.5326753866625, "country_name": "Iceland", "continent_name": "Europe", "currency_code": "ISK"}}, {"model": "countries.country", "pk": 110, "fields": {"continent": "EU", "capital": "Rome", "geoname_id": 3175395, "languages": "it-IT,de-IT,fr-IT,sc,ca,co,sl", "north": 47.0917837415439, "south": 36.6440816661648, "iso_alpha_3": "ITA", "fips_code": "IT", "population": "60340328", "east": 18.5203814091888, "iso_numeric": "380", "area_in_sq_km": "301230.0", "country_code": "IT", "west": 6.62662135986088, "country_name": "Italy", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 111, "fields": {"continent": "EU", "capital": "Saint Helier", "geoname_id": 3042142, "languages": "en,fr,nrf", "north": 49.265057, "south": 49.169834, "iso_alpha_3": "JEY", "fips_code": "JE", "population": "90812", "east": -2.022083, "iso_numeric": "832", "area_in_sq_km": "116.0", "country_code": "JE", "west": -2.260028, "country_name": "Jersey", "continent_name": "Europe", "currency_code": "GBP"}}, {"model": "countries.country", "pk": 112, "fields": {"continent": "NA", "capital": "Kingston", "geoname_id": 3489940, "languages": "en-JM", "north": 18.524766185516, "south": 17.7059966193696, "iso_alpha_3": "JAM", "fips_code": "JM", "population": "2847232", "east": -76.1830989848426, "iso_numeric": "388", "area_in_sq_km": "10991.0", "country_code": "JM", "west": -78.3690062954957, "country_name": "Jamaica", "continent_name": "North America", "currency_code": "JMD"}}, {"model": "countries.country", "pk": 113, "fields": {"continent": "AS", "capital": "Amman", "geoname_id": 248816, "languages": "ar-JO,en", "north": 33.367668, "south": 29.185888, "iso_alpha_3": "JOR", "fips_code": "JO", "population": "6407085", "east": 39.301167, "iso_numeric": "400", "area_in_sq_km": "92300.0", "country_code": "JO", "west": 34.959999, "country_name": "Jordan", "continent_name": "Asia", "currency_code": "JOD"}}, {"model": "countries.country", "pk": 114, "fields": {"continent": "AS", "capital": "Tokyo", "geoname_id": 1861060, "languages": "ja", "north": 45.52295736, "south": 24.255169441, "iso_alpha_3": "JPN", "fips_code": "JA", "population": "127288000", "east": 145.817458885, "iso_numeric": "392", "area_in_sq_km": "377835.0", "country_code": "JP", "west": 122.933653061, "country_name": "Japan", "continent_name": "Asia", "currency_code": "JPY"}}, {"model": "countries.country", "pk": 115, "fields": {"continent": "AF", "capital": "Nairobi", "geoname_id": 192950, "languages": "en-KE,sw-KE", "north": 5.019938, "south": -4.678047, "iso_alpha_3": "KEN", "fips_code": "KE", "population": "40046566", "east": 41.899078, "iso_numeric": "404", "area_in_sq_km": "582650.0", "country_code": "KE", "west": 33.908859, "country_name": "Kenya", "continent_name": "Africa", "currency_code": "KES"}}, {"model": "countries.country", "pk": 116, "fields": {"continent": "AS", "capital": "Bishkek", "geoname_id": 1527747, "languages": "ky,uz,ru", "north": 43.238224, "south": 39.172832, "iso_alpha_3": "KGZ", "fips_code": "KG", "population": "5776500", "east": 80.283165, "iso_numeric": "417", "area_in_sq_km": "198500.0", "country_code": "KG", "west": 69.276611, "country_name": "Kyrgyzstan", "continent_name": "Asia", "currency_code": "KGS"}}, {"model": "countries.country", "pk": 117, "fields": {"continent": "AS", "capital": "Phnom Penh", "geoname_id": 1831722, "languages": "km,fr,en", "north": 14.686417, "south": 10.409083, "iso_alpha_3": "KHM", "fips_code": "CB", "population": "14453680", "east": 107.627724, "iso_numeric": "116", "area_in_sq_km": "181040.0", "country_code": "KH", "west": 102.339996, "country_name": "Cambodia", "continent_name": "Asia", "currency_code": "KHR"}}, {"model": "countries.country", "pk": 118, "fields": {"continent": "OC", "capital": "Tarawa", "geoname_id": 4030945, "languages": "en-KI,gil", "north": 4.71957, "south": -11.446881150186856, "iso_alpha_3": "KIR", "fips_code": "KR", "population": "92533", "east": -150.215347, "iso_numeric": "296", "area_in_sq_km": "811.0", "country_code": "KI", "west": 169.556137, "country_name": "Kiribati", "continent_name": "Oceania", "currency_code": "AUD"}}, {"model": "countries.country", "pk": 119, "fields": {"continent": "AF", "capital": "Moroni", "geoname_id": 921929, "languages": "ar,fr-KM", "north": -11.362381, "south": -12.387857, "iso_alpha_3": "COM", "fips_code": "CN", "population": "773407", "east": 44.538223, "iso_numeric": "174", "area_in_sq_km": "2170.0", "country_code": "KM", "west": 43.21579, "country_name": "Comoros", "continent_name": "Africa", "currency_code": "KMF"}}, {"model": "countries.country", "pk": 120, "fields": {"continent": "NA", "capital": "Basseterre", "geoname_id": 3575174, "languages": "en-KN", "north": 17.420118, "south": 17.095343, "iso_alpha_3": "KNA", "fips_code": "SC", "population": "51134", "east": -62.543266, "iso_numeric": "659", "area_in_sq_km": "261.0", "country_code": "KN", "west": -62.86956, "country_name": "Saint Kitts and Nevis", "continent_name": "North America", "currency_code": "XCD"}}, {"model": "countries.country", "pk": 121, "fields": {"continent": "AS", "capital": "Pyongyang", "geoname_id": 1873107, "languages": "ko-KP", "north": 43.006054, "south": 37.673332, "iso_alpha_3": "PRK", "fips_code": "KN", "population": "22912177", "east": 130.674866, "iso_numeric": "408", "area_in_sq_km": "120540.0", "country_code": "KP", "west": 124.315887, "country_name": "North Korea", "continent_name": "Asia", "currency_code": "KPW"}}, {"model": "countries.country", "pk": 122, "fields": {"continent": "AS", "capital": "Seoul", "geoname_id": 1835841, "languages": "ko-KR,en", "north": 38.5933891092225, "south": 33.1954102977009, "iso_alpha_3": "KOR", "fips_code": "KS", "population": "48422644", "east": 129.583016157998, "iso_numeric": "410", "area_in_sq_km": "98480.0", "country_code": "KR", "west": 125.887442375577, "country_name": "South Korea", "continent_name": "Asia", "currency_code": "KRW"}}, {"model": "countries.country", "pk": 123, "fields": {"continent": "AS", "capital": "Kuwait City", "geoname_id": 285570, "languages": "ar-KW,en", "north": 30.095945, "south": 28.524611, "iso_alpha_3": "KWT", "fips_code": "KU", "population": "2789132", "east": 48.431473, "iso_numeric": "414", "area_in_sq_km": "17820.0", "country_code": "KW", "west": 46.555557, "country_name": "Kuwait", "continent_name": "Asia", "currency_code": "KWD"}}, {"model": "countries.country", "pk": 124, "fields": {"continent": "NA", "capital": "George Town", "geoname_id": 3580718, "languages": "en-KY", "north": 19.7617, "south": 19.263029, "iso_alpha_3": "CYM", "fips_code": "CJ", "population": "44270", "east": -79.727272, "iso_numeric": "136", "area_in_sq_km": "262.0", "country_code": "KY", "west": -81.432777, "country_name": "Cayman Islands", "continent_name": "North America", "currency_code": "KYD"}}, {"model": "countries.country", "pk": 125, "fields": {"continent": "AS", "capital": "Astana", "geoname_id": 1522867, "languages": "kk,ru", "north": 55.451195, "south": 40.936333, "iso_alpha_3": "KAZ", "fips_code": "KZ", "population": "15340000", "east": 87.312668, "iso_numeric": "398", "area_in_sq_km": "2717300.0", "country_code": "KZ", "west": 46.491859, "country_name": "Kazakhstan", "continent_name": "Asia", "currency_code": "KZT"}}, {"model": "countries.country", "pk": 126, "fields": {"continent": "AS", "capital": "Vientiane", "geoname_id": 1655842, "languages": "lo,fr,en", "north": 22.500389, "south": 13.910027, "iso_alpha_3": "LAO", "fips_code": "LA", "population": "6368162", "east": 107.697029, "iso_numeric": "418", "area_in_sq_km": "236800.0", "country_code": "LA", "west": 100.093056, "country_name": "Laos", "continent_name": "Asia", "currency_code": "LAK"}}, {"model": "countries.country", "pk": 127, "fields": {"continent": "AS", "capital": "Beirut", "geoname_id": 272103, "languages": "ar-LB,fr-LB,en,hy", "north": 34.691418, "south": 33.05386, "iso_alpha_3": "LBN", "fips_code": "LE", "population": "4125247", "east": 36.639194, "iso_numeric": "422", "area_in_sq_km": "10400.0", "country_code": "LB", "west": 35.114277, "country_name": "Lebanon", "continent_name": "Asia", "currency_code": "LBP"}}, {"model": "countries.country", "pk": 128, "fields": {"continent": "NA", "capital": "Castries", "geoname_id": 3576468, "languages": "en-LC", "north": 14.110317287646, "south": 13.7072692224982, "iso_alpha_3": "LCA", "fips_code": "ST", "population": "160922", "east": -60.8732306422271, "iso_numeric": "662", "area_in_sq_km": "616.0", "country_code": "LC", "west": -61.07995730159752, "country_name": "Saint Lucia", "continent_name": "North America", "currency_code": "XCD"}}, {"model": "countries.country", "pk": 129, "fields": {"continent": "EU", "capital": "Vaduz", "geoname_id": 3042058, "languages": "de-LI", "north": 47.2706251386959, "south": 47.0484284123471, "iso_alpha_3": "LIE", "fips_code": "LS", "population": "35000", "east": 9.63564281136796, "iso_numeric": "438", "area_in_sq_km": "160.0", "country_code": "LI", "west": 9.47167359782014, "country_name": "Liechtenstein", "continent_name": "Europe", "currency_code": "CHF"}}, {"model": "countries.country", "pk": 130, "fields": {"continent": "AS", "capital": "Colombo", "geoname_id": 1227603, "languages": "si,ta,en", "north": 9.831361, "south": 5.916833, "iso_alpha_3": "LKA", "fips_code": "CE", "population": "21513990", "east": 81.881279, "iso_numeric": "144", "area_in_sq_km": "65610.0", "country_code": "LK", "west": 79.652916, "country_name": "Sri Lanka", "continent_name": "Asia", "currency_code": "LKR"}}, {"model": "countries.country", "pk": 131, "fields": {"continent": "AF", "capital": "Monrovia", "geoname_id": 2275384, "languages": "en-LR", "north": 8.551791, "south": 4.353057, "iso_alpha_3": "LBR", "fips_code": "LI", "population": "3685076", "east": -7.365113, "iso_numeric": "430", "area_in_sq_km": "111370.0", "country_code": "LR", "west": -11.492083, "country_name": "Liberia", "continent_name": "Africa", "currency_code": "LRD"}}, {"model": "countries.country", "pk": 132, "fields": {"continent": "AF", "capital": "Maseru", "geoname_id": 932692, "languages": "en-LS,st,zu,xh", "north": -28.5708, "south": -30.6755750029999, "iso_alpha_3": "LSO", "fips_code": "LT", "population": "1919552", "east": 29.4557099420001, "iso_numeric": "426", "area_in_sq_km": "30355.0", "country_code": "LS", "west": 27.011229998, "country_name": "Lesotho", "continent_name": "Africa", "currency_code": "LSL"}}, {"model": "countries.country", "pk": 133, "fields": {"continent": "EU", "capital": "Vilnius", "geoname_id": 597427, "languages": "lt,ru,pl", "north": 56.446918, "south": 53.901306, "iso_alpha_3": "LTU", "fips_code": "LH", "population": "2944459", "east": 26.871944, "iso_numeric": "440", "area_in_sq_km": "65200.0", "country_code": "LT", "west": 20.941528, "country_name": "Lithuania", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 134, "fields": {"continent": "EU", "capital": "Luxembourg", "geoname_id": 2960313, "languages": "lb,de-LU,fr-LU", "north": 50.182772453796446, "south": 49.447858677765716, "iso_alpha_3": "LUX", "fips_code": "LU", "population": "497538", "east": 6.5308980672559525, "iso_numeric": "442", "area_in_sq_km": "2586.0", "country_code": "LU", "west": 5.735698938390786, "country_name": "Luxembourg", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 135, "fields": {"continent": "EU", "capital": "Riga", "geoname_id": 458258, "languages": "lv,ru,lt", "north": 58.0856982477268, "south": 55.6747774931332, "iso_alpha_3": "LVA", "fips_code": "LG", "population": "2217969", "east": 28.2412717372783, "iso_numeric": "428", "area_in_sq_km": "64589.0", "country_code": "LV", "west": 20.9719557460935, "country_name": "Latvia", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 136, "fields": {"continent": "AF", "capital": "Tripoli", "geoname_id": 2215636, "languages": "ar-LY,it,en", "north": 33.168999, "south": 19.508045, "iso_alpha_3": "LBY", "fips_code": "LY", "population": "6461454", "east": 25.150612, "iso_numeric": "434", "area_in_sq_km": "1759540.0", "country_code": "LY", "west": 9.38702, "country_name": "Libya", "continent_name": "Africa", "currency_code": "LYD"}}, {"model": "countries.country", "pk": 137, "fields": {"continent": "AF", "capital": "Rabat", "geoname_id": 2542007, "languages": "ar-MA,ber,fr", "north": 35.9224966985384, "south": 27.662115, "iso_alpha_3": "MAR", "fips_code": "MO", "population": "33848242", "east": -0.991750000000025, "iso_numeric": "504", "area_in_sq_km": "446550.0", "country_code": "MA", "west": -13.168586, "country_name": "Morocco", "continent_name": "Africa", "currency_code": "MAD"}}, {"model": "countries.country", "pk": 138, "fields": {"continent": "EU", "capital": "Monaco", "geoname_id": 2993457, "languages": "fr-MC,en,it", "north": 43.75196717037228, "south": 43.72472839869377, "iso_alpha_3": "MCO", "fips_code": "MN", "population": "32965", "east": 7.439939260482788, "iso_numeric": "492", "area_in_sq_km": "1.95", "country_code": "MC", "west": 7.408962249755859, "country_name": "Monaco", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 139, "fields": {"continent": "EU", "capital": "Chi\u015fin\u0103u", "geoname_id": 617790, "languages": "ro,ru,gag,tr", "north": 48.490166, "south": 45.468887, "iso_alpha_3": "MDA", "fips_code": "MD", "population": "4324000", "east": 30.135445, "iso_numeric": "498", "area_in_sq_km": "33843.0", "country_code": "MD", "west": 26.618944, "country_name": "Moldova", "continent_name": "Europe", "currency_code": "MDL"}}, {"model": "countries.country", "pk": 140, "fields": {"continent": "EU", "capital": "Podgorica", "geoname_id": 3194884, "languages": "sr,hu,bs,sq,hr,rom", "north": 43.570137, "south": 41.850166, "iso_alpha_3": "MNE", "fips_code": "MJ", "population": "666730", "east": 20.358833, "iso_numeric": "499", "area_in_sq_km": "14026.0", "country_code": "ME", "west": 18.461306, "country_name": "Montenegro", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 141, "fields": {"continent": "NA", "capital": "Marigot", "geoname_id": 3578421, "languages": "fr", "north": 18.125295191246206, "south": 18.04717219103021, "iso_alpha_3": "MAF", "fips_code": "RN", "population": "35925", "east": -63.01059106320133, "iso_numeric": "663", "area_in_sq_km": "53.0", "country_code": "MF", "west": -63.15036103890611, "country_name": "Saint Martin", "continent_name": "North America", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 142, "fields": {"continent": "AF", "capital": "Antananarivo", "geoname_id": 1062947, "languages": "fr-MG,mg", "north": -11.945433, "south": -25.608952, "iso_alpha_3": "MDG", "fips_code": "MA", "population": "21281844", "east": 50.48378, "iso_numeric": "450", "area_in_sq_km": "587040.0", "country_code": "MG", "west": 43.224876, "country_name": "Madagascar", "continent_name": "Africa", "currency_code": "MGA"}}, {"model": "countries.country", "pk": 143, "fields": {"continent": "OC", "capital": "Majuro", "geoname_id": 2080185, "languages": "mh,en-MH", "north": 14.62, "south": 5.587639, "iso_alpha_3": "MHL", "fips_code": "RM", "population": "65859", "east": 171.931808, "iso_numeric": "584", "area_in_sq_km": "181.3", "country_code": "MH", "west": 165.524918, "country_name": "Marshall Islands", "continent_name": "Oceania", "currency_code": "USD"}}, {"model": "countries.country", "pk": 144, "fields": {"continent": "EU", "capital": "Skopje", "geoname_id": 718075, "languages": "mk,sq,tr,rmm,sr", "north": 42.361805, "south": 40.860195, "iso_alpha_3": "MKD", "fips_code": "MK", "population": "2062294", "east": 23.038139, "iso_numeric": "807", "area_in_sq_km": "25333.0", "country_code": "MK", "west": 20.464695, "country_name": "Macedonia", "continent_name": "Europe", "currency_code": "MKD"}}, {"model": "countries.country", "pk": 145, "fields": {"continent": "AF", "capital": "Bamako", "geoname_id": 2453866, "languages": "fr-ML,bm", "north": 25.000002, "south": 10.159513, "iso_alpha_3": "MLI", "fips_code": "ML", "population": "13796354", "east": 4.244968, "iso_numeric": "466", "area_in_sq_km": "1240000.0", "country_code": "ML", "west": -12.242614, "country_name": "Mali", "continent_name": "Africa", "currency_code": "XOF"}}, {"model": "countries.country", "pk": 146, "fields": {"continent": "AS", "capital": "Naypyitaw", "geoname_id": 1327865, "languages": "my", "north": 28.543249, "south": 9.784583, "iso_alpha_3": "MMR", "fips_code": "BM", "population": "53414374", "east": 101.176781, "iso_numeric": "104", "area_in_sq_km": "678500.0", "country_code": "MM", "west": 92.189278, "country_name": "Myanmar [Burma]", "continent_name": "Asia", "currency_code": "MMK"}}, {"model": "countries.country", "pk": 147, "fields": {"continent": "AS", "capital": "Ulan Bator", "geoname_id": 2029969, "languages": "mn,ru", "north": 52.154251, "south": 41.567638, "iso_alpha_3": "MNG", "fips_code": "MG", "population": "3086918", "east": 119.924309, "iso_numeric": "496", "area_in_sq_km": "1565000.0", "country_code": "MN", "west": 87.749664, "country_name": "Mongolia", "continent_name": "Asia", "currency_code": "MNT"}}, {"model": "countries.country", "pk": 148, "fields": {"continent": "AS", "capital": "Macao", "geoname_id": 1821275, "languages": "zh,zh-MO,pt", "north": 22.222334, "south": 22.180389, "iso_alpha_3": "MAC", "fips_code": "MC", "population": "449198", "east": 113.565834, "iso_numeric": "446", "area_in_sq_km": "254.0", "country_code": "MO", "west": 113.528946, "country_name": "Macao", "continent_name": "Asia", "currency_code": "MOP"}}, {"model": "countries.country", "pk": 149, "fields": {"continent": "OC", "capital": "Saipan", "geoname_id": 4041468, "languages": "fil,tl,zh,ch-MP,en-MP", "north": 20.55344, "south": 14.11023, "iso_alpha_3": "MNP", "fips_code": "CQ", "population": "53883", "east": 146.06528, "iso_numeric": "580", "area_in_sq_km": "477.0", "country_code": "MP", "west": 144.88626, "country_name": "Northern Mariana Islands", "continent_name": "Oceania", "currency_code": "USD"}}, {"model": "countries.country", "pk": 150, "fields": {"continent": "NA", "capital": "Fort-de-France", "geoname_id": 3570311, "languages": "fr-MQ", "north": 14.878819, "south": 14.392262, "iso_alpha_3": "MTQ", "fips_code": "MB", "population": "432900", "east": -60.81551, "iso_numeric": "474", "area_in_sq_km": "1100.0", "country_code": "MQ", "west": -61.230118, "country_name": "Martinique", "continent_name": "North America", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 151, "fields": {"continent": "AF", "capital": "Nouakchott", "geoname_id": 2378080, "languages": "ar-MR,fuc,snk,fr,mey,wo", "north": 27.298073, "south": 14.715547, "iso_alpha_3": "MRT", "fips_code": "MR", "population": "3205060", "east": -4.827674, "iso_numeric": "478", "area_in_sq_km": "1030700.0", "country_code": "MR", "west": -17.066521, "country_name": "Mauritania", "continent_name": "Africa", "currency_code": "MRO"}}, {"model": "countries.country", "pk": 152, "fields": {"continent": "NA", "capital": "Plymouth", "geoname_id": 3578097, "languages": "en-MS", "north": 16.824060205313184, "south": 16.674768935441556, "iso_alpha_3": "MSR", "fips_code": "MH", "population": "9341", "east": -62.144100129608205, "iso_numeric": "500", "area_in_sq_km": "102.0", "country_code": "MS", "west": -62.24138237036129, "country_name": "Montserrat", "continent_name": "North America", "currency_code": "XCD"}}, {"model": "countries.country", "pk": 153, "fields": {"continent": "EU", "capital": "Valletta", "geoname_id": 2562770, "languages": "mt,en-MT", "north": 36.0821530995456, "south": 35.8061835000002, "iso_alpha_3": "MLT", "fips_code": "MT", "population": "403000", "east": 14.5764915000002, "iso_numeric": "470", "area_in_sq_km": "316.0", "country_code": "MT", "west": 14.1834251000001, "country_name": "Malta", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 154, "fields": {"continent": "AF", "capital": "Port Louis", "geoname_id": 934292, "languages": "en-MU,bho,fr", "north": -10.319255, "south": -20.525717, "iso_alpha_3": "MUS", "fips_code": "MP", "population": "1294104", "east": 63.500179, "iso_numeric": "480", "area_in_sq_km": "2040.0", "country_code": "MU", "west": 56.512718, "country_name": "Mauritius", "continent_name": "Africa", "currency_code": "MUR"}}, {"model": "countries.country", "pk": 155, "fields": {"continent": "AS", "capital": "Mal\u00e9", "geoname_id": 1282028, "languages": "dv,en", "north": 7.091587495414767, "south": -0.692694, "iso_alpha_3": "MDV", "fips_code": "MV", "population": "395650", "east": 73.637276, "iso_numeric": "462", "area_in_sq_km": "300.0", "country_code": "MV", "west": 72.693222, "country_name": "Maldives", "continent_name": "Asia", "currency_code": "MVR"}}, {"model": "countries.country", "pk": 156, "fields": {"continent": "AF", "capital": "Lilongwe", "geoname_id": 927384, "languages": "ny,yao,tum,swk", "north": -9.367541, "south": -17.125, "iso_alpha_3": "MWI", "fips_code": "MI", "population": "15447500", "east": 35.916821, "iso_numeric": "454", "area_in_sq_km": "118480.0", "country_code": "MW", "west": 32.67395, "country_name": "Malawi", "continent_name": "Africa", "currency_code": "MWK"}}, {"model": "countries.country", "pk": 157, "fields": {"continent": "NA", "capital": "Mexico City", "geoname_id": 3996063, "languages": "es-MX", "north": 32.716759, "south": 14.532866, "iso_alpha_3": "MEX", "fips_code": "MX", "population": "112468855", "east": -86.703392, "iso_numeric": "484", "area_in_sq_km": "1972550.0", "country_code": "MX", "west": -118.453949, "country_name": "Mexico", "continent_name": "North America", "currency_code": "MXN"}}, {"model": "countries.country", "pk": 158, "fields": {"continent": "AS", "capital": "Kuala Lumpur", "geoname_id": 1733045, "languages": "ms-MY,en,zh,ta,te,ml,pa,th", "north": 7.363417, "south": 0.855222, "iso_alpha_3": "MYS", "fips_code": "MY", "population": "28274729", "east": 119.267502, "iso_numeric": "458", "area_in_sq_km": "329750.0", "country_code": "MY", "west": 99.643448, "country_name": "Malaysia", "continent_name": "Asia", "currency_code": "MYR"}}, {"model": "countries.country", "pk": 159, "fields": {"continent": "AF", "capital": "Maputo", "geoname_id": 1036973, "languages": "pt-MZ,vmw", "north": -10.471883, "south": -26.868685, "iso_alpha_3": "MOZ", "fips_code": "MZ", "population": "22061451", "east": 40.842995, "iso_numeric": "508", "area_in_sq_km": "801590.0", "country_code": "MZ", "west": 30.217319, "country_name": "Mozambique", "continent_name": "Africa", "currency_code": "MZN"}}, {"model": "countries.country", "pk": 160, "fields": {"continent": "AF", "capital": "Windhoek", "geoname_id": 3355338, "languages": "en-NA,af,de,hz,naq", "north": -16.959894, "south": -28.97143, "iso_alpha_3": "NAM", "fips_code": "WA", "population": "2128471", "east": 25.256701, "iso_numeric": "516", "area_in_sq_km": "825418.0", "country_code": "NA", "west": 11.71563, "country_name": "Namibia", "continent_name": "Africa", "currency_code": "NAD"}}, {"model": "countries.country", "pk": 161, "fields": {"continent": "OC", "capital": "Noumea", "geoname_id": 2139685, "languages": "fr-NC", "north": -19.549778, "south": -22.698, "iso_alpha_3": "NCL", "fips_code": "NC", "population": "216494", "east": 168.129135, "iso_numeric": "540", "area_in_sq_km": "19060.0", "country_code": "NC", "west": 163.564667, "country_name": "New Caledonia", "continent_name": "Oceania", "currency_code": "XPF"}}, {"model": "countries.country", "pk": 162, "fields": {"continent": "AF", "capital": "Niamey", "geoname_id": 2440476, "languages": "fr-NE,ha,kr,dje", "north": 23.525026, "south": 11.696975, "iso_alpha_3": "NER", "fips_code": "NG", "population": "15878271", "east": 15.995643, "iso_numeric": "562", "area_in_sq_km": "1267000.0", "country_code": "NE", "west": 0.16625, "country_name": "Niger", "continent_name": "Africa", "currency_code": "XOF"}}, {"model": "countries.country", "pk": 163, "fields": {"continent": "OC", "capital": "Kingston", "geoname_id": 2155115, "languages": "en-NF", "north": -28.995170686948427, "south": -29.063076742954735, "iso_alpha_3": "NFK", "fips_code": "NF", "population": "1828", "east": 167.99773740209957, "iso_numeric": "574", "area_in_sq_km": "34.6", "country_code": "NF", "west": 167.91543230151365, "country_name": "Norfolk Island", "continent_name": "Oceania", "currency_code": "AUD"}}, {"model": "countries.country", "pk": 164, "fields": {"continent": "AF", "capital": "Abuja", "geoname_id": 2328926, "languages": "en-NG,ha,yo,ig,ff", "north": 13.892007, "south": 4.277144, "iso_alpha_3": "NGA", "fips_code": "NI", "population": "154000000", "east": 14.680073, "iso_numeric": "566", "area_in_sq_km": "923768.0", "country_code": "NG", "west": 2.668432, "country_name": "Nigeria", "continent_name": "Africa", "currency_code": "NGN"}}, {"model": "countries.country", "pk": 165, "fields": {"continent": "NA", "capital": "Managua", "geoname_id": 3617476, "languages": "es-NI,en", "north": 15.025909, "south": 10.707543, "iso_alpha_3": "NIC", "fips_code": "NU", "population": "5995928", "east": -82.738289, "iso_numeric": "558", "area_in_sq_km": "129494.0", "country_code": "NI", "west": -87.690308, "country_name": "Nicaragua", "continent_name": "North America", "currency_code": "NIO"}}, {"model": "countries.country", "pk": 166, "fields": {"continent": "EU", "capital": "Amsterdam", "geoname_id": 2750405, "languages": "nl-NL,fy-NL", "north": 53.5157125645109, "south": 50.7503674993741, "iso_alpha_3": "NLD", "fips_code": "NL", "population": "16645000", "east": 7.22749859212922, "iso_numeric": "528", "area_in_sq_km": "41526.0", "country_code": "NL", "west": 3.35837827202, "country_name": "Netherlands", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 167, "fields": {"continent": "EU", "capital": "Oslo", "geoname_id": 3144096, "languages": "no,nb,nn,se,fi", "north": 71.18811, "south": 57.977917, "iso_alpha_3": "NOR", "fips_code": "NO", "population": "5009150", "east": 31.078052520751953, "iso_numeric": "578", "area_in_sq_km": "324220.0", "country_code": "NO", "west": 4.650167, "country_name": "Norway", "continent_name": "Europe", "currency_code": "NOK"}}, {"model": "countries.country", "pk": 168, "fields": {"continent": "AS", "capital": "Kathmandu", "geoname_id": 1282988, "languages": "ne,en", "north": 30.43339, "south": 26.356722, "iso_alpha_3": "NPL", "fips_code": "NP", "population": "28951852", "east": 88.199333, "iso_numeric": "524", "area_in_sq_km": "140800.0", "country_code": "NP", "west": 80.056274, "country_name": "Nepal", "continent_name": "Asia", "currency_code": "NPR"}}, {"model": "countries.country", "pk": 169, "fields": {"continent": "OC", "capital": "Yaren", "geoname_id": 2110425, "languages": "na,en-NR", "north": -0.504306, "south": -0.552333, "iso_alpha_3": "NRU", "fips_code": "NR", "population": "10065", "east": 166.945282, "iso_numeric": "520", "area_in_sq_km": "21.0", "country_code": "NR", "west": 166.899033, "country_name": "Nauru", "continent_name": "Oceania", "currency_code": "AUD"}}, {"model": "countries.country", "pk": 170, "fields": {"continent": "OC", "capital": "Alofi", "geoname_id": 4036232, "languages": "niu,en-NU", "north": -18.951069, "south": -19.152193, "iso_alpha_3": "NIU", "fips_code": "NE", "population": "2166", "east": -169.775177, "iso_numeric": "570", "area_in_sq_km": "260.0", "country_code": "NU", "west": -169.951004, "country_name": "Niue", "continent_name": "Oceania", "currency_code": "NZD"}}, {"model": "countries.country", "pk": 171, "fields": {"continent": "OC", "capital": "Wellington", "geoname_id": 2186224, "languages": "en-NZ,mi", "north": -34.389668, "south": -47.286026, "iso_alpha_3": "NZL", "fips_code": "NZ", "population": "4252277", "east": -180.0, "iso_numeric": "554", "area_in_sq_km": "268680.0", "country_code": "NZ", "west": 166.7155, "country_name": "New Zealand", "continent_name": "Oceania", "currency_code": "NZD"}}, {"model": "countries.country", "pk": 172, "fields": {"continent": "AS", "capital": "Muscat", "geoname_id": 286963, "languages": "ar-OM,en,bal,ur", "north": 26.387972, "south": 16.64575, "iso_alpha_3": "OMN", "fips_code": "MU", "population": "2967717", "east": 59.836582, "iso_numeric": "512", "area_in_sq_km": "212460.0", "country_code": "OM", "west": 51.882, "country_name": "Oman", "continent_name": "Asia", "currency_code": "OMR"}}, {"model": "countries.country", "pk": 173, "fields": {"continent": "NA", "capital": "Panama City", "geoname_id": 3703430, "languages": "es-PA,en", "north": 9.6474132494631, "south": 7.20236920646422, "iso_alpha_3": "PAN", "fips_code": "PM", "population": "3410676", "east": -77.1563637579897, "iso_numeric": "591", "area_in_sq_km": "78200.0", "country_code": "PA", "west": -83.0523988577088, "country_name": "Panama", "continent_name": "North America", "currency_code": "PAB"}}, {"model": "countries.country", "pk": 174, "fields": {"continent": "SA", "capital": "Lima", "geoname_id": 3932488, "languages": "es-PE,qu,ay", "north": -0.012977, "south": -18.349728, "iso_alpha_3": "PER", "fips_code": "PE", "population": "29907003", "east": -68.677986, "iso_numeric": "604", "area_in_sq_km": "1285220.0", "country_code": "PE", "west": -81.326744, "country_name": "Peru", "continent_name": "South America", "currency_code": "PEN"}}, {"model": "countries.country", "pk": 175, "fields": {"continent": "OC", "capital": "Papeete", "geoname_id": 4030656, "languages": "fr-PF,ty", "north": -7.903573, "south": -27.653572, "iso_alpha_3": "PYF", "fips_code": "FP", "population": "270485", "east": -134.929825, "iso_numeric": "258", "area_in_sq_km": "4167.0", "country_code": "PF", "west": -152.877167, "country_name": "French Polynesia", "continent_name": "Oceania", "currency_code": "XPF"}}, {"model": "countries.country", "pk": 176, "fields": {"continent": "OC", "capital": "Port Moresby", "geoname_id": 2088628, "languages": "en-PG,ho,meu,tpi", "north": -1.318639, "south": -11.657861, "iso_alpha_3": "PNG", "fips_code": "PP", "population": "6064515", "east": 155.96344, "iso_numeric": "598", "area_in_sq_km": "462840.0", "country_code": "PG", "west": 140.842865, "country_name": "Papua New Guinea", "continent_name": "Oceania", "currency_code": "PGK"}}, {"model": "countries.country", "pk": 177, "fields": {"continent": "AS", "capital": "Manila", "geoname_id": 1694008, "languages": "tl,en-PH,fil,ceb,tgl,ilo,hil,war,pam,bik,bcl,pag,mrw,tsg,mdh,cbk,krj,sgd,msb,akl,ibg,yka,mta,abx", "north": 21.1218854788318, "south": 4.64209796365014, "iso_alpha_3": "PHL", "fips_code": "RP", "population": "99900177", "east": 126.60497402182328, "iso_numeric": "608", "area_in_sq_km": "300000.0", "country_code": "PH", "west": 116.9288644959, "country_name": "Philippines", "continent_name": "Asia", "currency_code": "PHP"}}, {"model": "countries.country", "pk": 178, "fields": {"continent": "AS", "capital": "Islamabad", "geoname_id": 1168579, "languages": "ur-PK,en-PK,pa,sd,ps,brh", "north": 37.097, "south": 23.786722, "iso_alpha_3": "PAK", "fips_code": "PK", "population": "184404791", "east": 77.840919, "iso_numeric": "586", "area_in_sq_km": "803940.0", "country_code": "PK", "west": 60.878613, "country_name": "Pakistan", "continent_name": "Asia", "currency_code": "PKR"}}, {"model": "countries.country", "pk": 179, "fields": {"continent": "EU", "capital": "Warsaw", "geoname_id": 798544, "languages": "pl", "north": 54.8357886595169, "south": 49.0020465193443, "iso_alpha_3": "POL", "fips_code": "PL", "population": "38500000", "east": 24.1457828491313, "iso_numeric": "616", "area_in_sq_km": "312685.0", "country_code": "PL", "west": 14.1228850233809, "country_name": "Poland", "continent_name": "Europe", "currency_code": "PLN"}}, {"model": "countries.country", "pk": 180, "fields": {"continent": "NA", "capital": "Saint-Pierre", "geoname_id": 3424932, "languages": "fr-PM", "north": 47.14376802942701, "south": 46.78264970849848, "iso_alpha_3": "SPM", "fips_code": "SB", "population": "7012", "east": -56.1253298443454, "iso_numeric": "666", "area_in_sq_km": "242.0", "country_code": "PM", "west": -56.40709223087083, "country_name": "Saint Pierre and Miquelon", "continent_name": "North America", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 181, "fields": {"continent": "OC", "capital": "Adamstown", "geoname_id": 4030699, "languages": "en-PN", "north": -24.3299386198549, "south": -24.672565, "iso_alpha_3": "PCN", "fips_code": "PC", "population": "46", "east": -124.77285, "iso_numeric": "612", "area_in_sq_km": "47.0", "country_code": "PN", "west": -128.35699011119425, "country_name": "Pitcairn Islands", "continent_name": "Oceania", "currency_code": "NZD"}}, {"model": "countries.country", "pk": 182, "fields": {"continent": "NA", "capital": "San Juan", "geoname_id": 4566966, "languages": "en-PR,es-PR", "north": 18.520166, "south": 17.926405, "iso_alpha_3": "PRI", "fips_code": "RQ", "population": "3916632", "east": -65.242737, "iso_numeric": "630", "area_in_sq_km": "9104.0", "country_code": "PR", "west": -67.942726, "country_name": "Puerto Rico", "continent_name": "North America", "currency_code": "USD"}}, {"model": "countries.country", "pk": 183, "fields": {"continent": "AS", "capital": "", "geoname_id": 6254930, "languages": "ar-PS", "north": 32.54638671875, "south": 31.216541290283203, "iso_alpha_3": "PSE", "fips_code": "WE", "population": "3800000", "east": 35.5732955932617, "iso_numeric": "275", "area_in_sq_km": "5970.0", "country_code": "PS", "west": 34.21665954589844, "country_name": "Palestine", "continent_name": "Asia", "currency_code": "ILS"}}, {"model": "countries.country", "pk": 184, "fields": {"continent": "EU", "capital": "Lisbon", "geoname_id": 2264397, "languages": "pt-PT,mwl", "north": 42.154311127408, "south": 36.96125, "iso_alpha_3": "PRT", "fips_code": "PO", "population": "10676000", "east": -6.18915930748288, "iso_numeric": "620", "area_in_sq_km": "92391.0", "country_code": "PT", "west": -9.50052660716588, "country_name": "Portugal", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 185, "fields": {"continent": "OC", "capital": "Melekeok", "geoname_id": 1559582, "languages": "pau,sov,en-PW,tox,ja,fil,zh", "north": 8.46966, "south": 2.8036, "iso_alpha_3": "PLW", "fips_code": "PS", "population": "19907", "east": 134.72307, "iso_numeric": "585", "area_in_sq_km": "458.0", "country_code": "PW", "west": 131.11788, "country_name": "Palau", "continent_name": "Oceania", "currency_code": "USD"}}, {"model": "countries.country", "pk": 186, "fields": {"continent": "SA", "capital": "Asunci\u00f3n", "geoname_id": 3437598, "languages": "es-PY,gn", "north": -19.294041, "south": -27.608738, "iso_alpha_3": "PRY", "fips_code": "PA", "population": "6375830", "east": -54.259354, "iso_numeric": "600", "area_in_sq_km": "406750.0", "country_code": "PY", "west": -62.647076, "country_name": "Paraguay", "continent_name": "South America", "currency_code": "PYG"}}, {"model": "countries.country", "pk": 187, "fields": {"continent": "AS", "capital": "Doha", "geoname_id": 289688, "languages": "ar-QA,es", "north": 26.154722, "south": 24.482944, "iso_alpha_3": "QAT", "fips_code": "QA", "population": "840926", "east": 51.636639, "iso_numeric": "634", "area_in_sq_km": "11437.0", "country_code": "QA", "west": 50.757221, "country_name": "Qatar", "continent_name": "Asia", "currency_code": "QAR"}}, {"model": "countries.country", "pk": 188, "fields": {"continent": "AF", "capital": "Saint-Denis", "geoname_id": 935317, "languages": "fr-RE", "north": -20.868391324576944, "south": -21.383747301469107, "iso_alpha_3": "REU", "fips_code": "RE", "population": "776948", "east": 55.838193901930026, "iso_numeric": "638", "area_in_sq_km": "2517.0", "country_code": "RE", "west": 55.21219224792685, "country_name": "R\u00e9union", "continent_name": "Africa", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 189, "fields": {"continent": "EU", "capital": "Bucharest", "geoname_id": 798549, "languages": "ro,hu,rom", "north": 48.2653912058468, "south": 43.6190166499833, "iso_alpha_3": "ROU", "fips_code": "RO", "population": "21959278", "east": 29.7152986907701, "iso_numeric": "642", "area_in_sq_km": "237500.0", "country_code": "RO", "west": 20.2619949052262, "country_name": "Romania", "continent_name": "Europe", "currency_code": "RON"}}, {"model": "countries.country", "pk": 190, "fields": {"continent": "EU", "capital": "Belgrade", "geoname_id": 6290252, "languages": "sr,hu,bs,rom", "north": 46.18138885498047, "south": 42.232215881347656, "iso_alpha_3": "SRB", "fips_code": "RI", "population": "7344847", "east": 23.00499725341797, "iso_numeric": "688", "area_in_sq_km": "88361.0", "country_code": "RS", "west": 18.817020416259766, "country_name": "Serbia", "continent_name": "Europe", "currency_code": "RSD"}}, {"model": "countries.country", "pk": 191, "fields": {"continent": "EU", "capital": "Moscow", "geoname_id": 2017370, "languages": "ru,tt,xal,cau,ady,kv,ce,tyv,cv,udm,tut,mns,bua,myv,mdf,chm,ba,inh,tut,kbd,krc,av,sah,nog", "north": 81.857361, "south": 41.188862, "iso_alpha_3": "RUS", "fips_code": "RS", "population": "140702000", "east": -169.05, "iso_numeric": "643", "area_in_sq_km": "1.71E7", "country_code": "RU", "west": 19.25, "country_name": "Russia", "continent_name": "Europe", "currency_code": "RUB"}}, {"model": "countries.country", "pk": 192, "fields": {"continent": "AF", "capital": "Kigali", "geoname_id": 49518, "languages": "rw,en-RW,fr-RW,sw", "north": -1.04716670707785, "south": -2.84023010213747, "iso_alpha_3": "RWA", "fips_code": "RW", "population": "11055976", "east": 30.8997466415943, "iso_numeric": "646", "area_in_sq_km": "26338.0", "country_code": "RW", "west": 28.8617308206209, "country_name": "Rwanda", "continent_name": "Africa", "currency_code": "RWF"}}, {"model": "countries.country", "pk": 193, "fields": {"continent": "AS", "capital": "Riyadh", "geoname_id": 102358, "languages": "ar-SA", "north": 32.158333, "south": 15.61425, "iso_alpha_3": "SAU", "fips_code": "SA", "population": "25731776", "east": 55.666584, "iso_numeric": "682", "area_in_sq_km": "1960582.0", "country_code": "SA", "west": 34.495693, "country_name": "Saudi Arabia", "continent_name": "Asia", "currency_code": "SAR"}}, {"model": "countries.country", "pk": 194, "fields": {"continent": "OC", "capital": "Honiara", "geoname_id": 2103350, "languages": "en-SB,tpi", "north": -6.589611, "south": -11.850555, "iso_alpha_3": "SLB", "fips_code": "BP", "population": "559198", "east": 166.980865, "iso_numeric": "090", "area_in_sq_km": "28450.0", "country_code": "SB", "west": 155.508606, "country_name": "Solomon Islands", "continent_name": "Oceania", "currency_code": "SBD"}}, {"model": "countries.country", "pk": 195, "fields": {"continent": "AF", "capital": "Victoria", "geoname_id": 241170, "languages": "en-SC,fr-SC", "north": -4.283717, "south": -9.753867, "iso_alpha_3": "SYC", "fips_code": "SE", "population": "88340", "east": 56.29770287937299, "iso_numeric": "690", "area_in_sq_km": "455.0", "country_code": "SC", "west": 46.204769, "country_name": "Seychelles", "continent_name": "Africa", "currency_code": "SCR"}}, {"model": "countries.country", "pk": 196, "fields": {"continent": "AF", "capital": "Khartoum", "geoname_id": 366755, "languages": "ar-SD,en,fia", "north": 22.232219696044922, "south": 8.684720993041992, "iso_alpha_3": "SDN", "fips_code": "SU", "population": "35000000", "east": 38.60749816894531, "iso_numeric": "729", "area_in_sq_km": "1861484.0", "country_code": "SD", "west": 21.827774047851562, "country_name": "Sudan", "continent_name": "Africa", "currency_code": "SDG"}}, {"model": "countries.country", "pk": 197, "fields": {"continent": "EU", "capital": "Stockholm", "geoname_id": 2661886, "languages": "sv-SE,se,sma,fi-SE", "north": 69.0599672666879, "south": 55.3374438220002, "iso_alpha_3": "SWE", "fips_code": "SW", "population": "9828655", "east": 24.155245238099, "iso_numeric": "752", "area_in_sq_km": "449964.0", "country_code": "SE", "west": 11.109499387126, "country_name": "Sweden", "continent_name": "Europe", "currency_code": "SEK"}}, {"model": "countries.country", "pk": 198, "fields": {"continent": "AS", "capital": "Singapore", "geoname_id": 1880251, "languages": "cmn,en-SG,ms-SG,ta-SG,zh-SG", "north": 1.471278, "south": 1.258556, "iso_alpha_3": "SGP", "fips_code": "SN", "population": "4701069", "east": 104.007469, "iso_numeric": "702", "area_in_sq_km": "692.7", "country_code": "SG", "west": 103.638275, "country_name": "Singapore", "continent_name": "Asia", "currency_code": "SGD"}}, {"model": "countries.country", "pk": 199, "fields": {"continent": "AF", "capital": "Jamestown", "geoname_id": 3370751, "languages": "en-SH", "north": -7.887815, "south": -16.019543, "iso_alpha_3": "SHN", "fips_code": "SH", "population": "7460", "east": -5.638753, "iso_numeric": "654", "area_in_sq_km": "410.0", "country_code": "SH", "west": -14.42123, "country_name": "Saint Helena", "continent_name": "Africa", "currency_code": "SHP"}}, {"model": "countries.country", "pk": 200, "fields": {"continent": "EU", "capital": "Ljubljana", "geoname_id": 3190538, "languages": "sl,sh", "north": 46.8766275518195, "south": 45.421812998164, "iso_alpha_3": "SVN", "fips_code": "SI", "population": "2007000", "east": 16.6106311807, "iso_numeric": "705", "area_in_sq_km": "20273.0", "country_code": "SI", "west": 13.3753342064709, "country_name": "Slovenia", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 201, "fields": {"continent": "EU", "capital": "Longyearbyen", "geoname_id": 607072, "languages": "no,ru", "north": 80.762085, "south": 79.220306, "iso_alpha_3": "SJM", "fips_code": "SV", "population": "2550", "east": 33.287334, "iso_numeric": "744", "area_in_sq_km": "62049.0", "country_code": "SJ", "west": 17.699389, "country_name": "Svalbard and Jan Mayen", "continent_name": "Europe", "currency_code": "NOK"}}, {"model": "countries.country", "pk": 202, "fields": {"continent": "EU", "capital": "Bratislava", "geoname_id": 3057568, "languages": "sk,hu", "north": 49.603168, "south": 47.728111, "iso_alpha_3": "SVK", "fips_code": "LO", "population": "5455000", "east": 22.570444, "iso_numeric": "703", "area_in_sq_km": "48845.0", "country_code": "SK", "west": 16.84775, "country_name": "Slovakia", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 203, "fields": {"continent": "AF", "capital": "Freetown", "geoname_id": 2403846, "languages": "en-SL,men,tem", "north": 10.0, "south": 6.929611, "iso_alpha_3": "SLE", "fips_code": "SL", "population": "5245695", "east": -10.284238, "iso_numeric": "694", "area_in_sq_km": "71740.0", "country_code": "SL", "west": -13.307631, "country_name": "Sierra Leone", "continent_name": "Africa", "currency_code": "SLL"}}, {"model": "countries.country", "pk": 204, "fields": {"continent": "EU", "capital": "San Marino", "geoname_id": 3168068, "languages": "it-SM", "north": 43.9920929668161, "south": 43.8937002210188, "iso_alpha_3": "SMR", "fips_code": "SM", "population": "31477", "east": 12.5158490454421, "iso_numeric": "674", "area_in_sq_km": "61.2", "country_code": "SM", "west": 12.403605260165, "country_name": "San Marino", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 205, "fields": {"continent": "AF", "capital": "Dakar", "geoname_id": 2245662, "languages": "fr-SN,wo,fuc,mnk", "north": 16.691633, "south": 12.307275, "iso_alpha_3": "SEN", "fips_code": "SG", "population": "12323252", "east": -11.355887, "iso_numeric": "686", "area_in_sq_km": "196190.0", "country_code": "SN", "west": -17.535236, "country_name": "Senegal", "continent_name": "Africa", "currency_code": "XOF"}}, {"model": "countries.country", "pk": 206, "fields": {"continent": "AF", "capital": "Mogadishu", "geoname_id": 51537, "languages": "so-SO,ar-SO,it,en-SO", "north": 11.979166, "south": -1.674868, "iso_alpha_3": "SOM", "fips_code": "SO", "population": "10112453", "east": 51.412636, "iso_numeric": "706", "area_in_sq_km": "637657.0", "country_code": "SO", "west": 40.986595, "country_name": "Somalia", "continent_name": "Africa", "currency_code": "SOS"}}, {"model": "countries.country", "pk": 207, "fields": {"continent": "SA", "capital": "Paramaribo", "geoname_id": 3382998, "languages": "nl-SR,en,srn,hns,jv", "north": 6.004546, "south": 1.831145, "iso_alpha_3": "SUR", "fips_code": "NS", "population": "492829", "east": -53.977493, "iso_numeric": "740", "area_in_sq_km": "163270.0", "country_code": "SR", "west": -58.086563, "country_name": "Suriname", "continent_name": "South America", "currency_code": "SRD"}}, {"model": "countries.country", "pk": 208, "fields": {"continent": "AF", "capital": "Juba", "geoname_id": 7909807, "languages": "en", "north": 12.219148635864258, "south": 3.493394374847412, "iso_alpha_3": "SSD", "fips_code": "OD", "population": "8260490", "east": 35.9405517578125, "iso_numeric": "728", "area_in_sq_km": "644329.0", "country_code": "SS", "west": 24.140274047851562, "country_name": "South Sudan", "continent_name": "Africa", "currency_code": "SSP"}}, {"model": "countries.country", "pk": 209, "fields": {"continent": "AF", "capital": "S\u00e3o Tom\u00e9", "geoname_id": 2410758, "languages": "pt-ST", "north": 1.701323, "south": 0.024766, "iso_alpha_3": "STP", "fips_code": "TP", "population": "175808", "east": 7.466374, "iso_numeric": "678", "area_in_sq_km": "1001.0", "country_code": "ST", "west": 6.47017, "country_name": "S\u00e3o Tom\u00e9 and Pr\u00edncipe", "continent_name": "Africa", "currency_code": "STD"}}, {"model": "countries.country", "pk": 210, "fields": {"continent": "NA", "capital": "San Salvador", "geoname_id": 3585968, "languages": "es-SV", "north": 14.445067, "south": 13.148679, "iso_alpha_3": "SLV", "fips_code": "ES", "population": "6052064", "east": -87.692162, "iso_numeric": "222", "area_in_sq_km": "21040.0", "country_code": "SV", "west": -90.128662, "country_name": "El Salvador", "continent_name": "North America", "currency_code": "USD"}}, {"model": "countries.country", "pk": 211, "fields": {"continent": "NA", "capital": "Philipsburg", "geoname_id": 7609695, "languages": "nl,en", "north": 18.065188278978088, "south": 18.006632279377143, "iso_alpha_3": "SXM", "fips_code": "NN", "population": "37429", "east": -63.0104194322962, "iso_numeric": "534", "area_in_sq_km": "21.0", "country_code": "SX", "west": -63.14146165934278, "country_name": "Sint Maarten", "continent_name": "North America", "currency_code": "ANG"}}, {"model": "countries.country", "pk": 212, "fields": {"continent": "AS", "capital": "Damascus", "geoname_id": 163843, "languages": "ar-SY,ku,hy,arc,fr,en", "north": 37.319138, "south": 32.310665, "iso_alpha_3": "SYR", "fips_code": "SY", "population": "22198110", "east": 42.385029, "iso_numeric": "760", "area_in_sq_km": "185180.0", "country_code": "SY", "west": 35.727222, "country_name": "Syria", "continent_name": "Asia", "currency_code": "SYP"}}, {"model": "countries.country", "pk": 213, "fields": {"continent": "AF", "capital": "Mbabane", "geoname_id": 934841, "languages": "en-SZ,ss-SZ", "north": -25.719648, "south": -27.317101, "iso_alpha_3": "SWZ", "fips_code": "WZ", "population": "1354051", "east": 32.13726, "iso_numeric": "748", "area_in_sq_km": "17363.0", "country_code": "SZ", "west": 30.794107, "country_name": "Swaziland", "continent_name": "Africa", "currency_code": "SZL"}}, {"model": "countries.country", "pk": 214, "fields": {"continent": "NA", "capital": "Cockburn Town", "geoname_id": 3576916, "languages": "en-TC", "north": 21.961878, "south": 21.422626, "iso_alpha_3": "TCA", "fips_code": "TK", "population": "20556", "east": -71.123642, "iso_numeric": "796", "area_in_sq_km": "430.0", "country_code": "TC", "west": -72.483871, "country_name": "Turks and Caicos Islands", "continent_name": "North America", "currency_code": "USD"}}, {"model": "countries.country", "pk": 215, "fields": {"continent": "AF", "capital": "N'Djamena", "geoname_id": 2434508, "languages": "fr-TD,ar-TD,sre", "north": 23.450369, "south": 7.441068, "iso_alpha_3": "TCD", "fips_code": "CD", "population": "10543464", "east": 24.002661, "iso_numeric": "148", "area_in_sq_km": "1284000.0", "country_code": "TD", "west": 13.473475, "country_name": "Chad", "continent_name": "Africa", "currency_code": "XAF"}}, {"model": "countries.country", "pk": 216, "fields": {"continent": "AN", "capital": "Port-aux-Fran\u00e7ais", "geoname_id": 1546748, "languages": "fr", "north": -37.790722, "south": -49.735184, "iso_alpha_3": "ATF", "fips_code": "FS", "population": "140", "east": 77.598808, "iso_numeric": "260", "area_in_sq_km": "7829.0", "country_code": "TF", "west": 50.170258, "country_name": "French Southern Territories", "continent_name": "Antarctica", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 217, "fields": {"continent": "AF", "capital": "Lom\u00e9", "geoname_id": 2363686, "languages": "fr-TG,ee,hna,kbp,dag,ha", "north": 11.138977, "south": 6.104417, "iso_alpha_3": "TGO", "fips_code": "TO", "population": "6587239", "east": 1.806693, "iso_numeric": "768", "area_in_sq_km": "56785.0", "country_code": "TG", "west": -0.147324, "country_name": "Togo", "continent_name": "Africa", "currency_code": "XOF"}}, {"model": "countries.country", "pk": 218, "fields": {"continent": "AS", "capital": "Bangkok", "geoname_id": 1605651, "languages": "th,en", "north": 20.463194, "south": 5.61, "iso_alpha_3": "THA", "fips_code": "TH", "population": "67089500", "east": 105.639389, "iso_numeric": "764", "area_in_sq_km": "514000.0", "country_code": "TH", "west": 97.345642, "country_name": "Thailand", "continent_name": "Asia", "currency_code": "THB"}}, {"model": "countries.country", "pk": 219, "fields": {"continent": "AS", "capital": "Dushanbe", "geoname_id": 1220409, "languages": "tg,ru", "north": 41.042252, "south": 36.674137, "iso_alpha_3": "TJK", "fips_code": "TI", "population": "7487489", "east": 75.137222, "iso_numeric": "762", "area_in_sq_km": "143100.0", "country_code": "TJ", "west": 67.387138, "country_name": "Tajikistan", "continent_name": "Asia", "currency_code": "TJS"}}, {"model": "countries.country", "pk": 220, "fields": {"continent": "OC", "capital": "", "geoname_id": 4031074, "languages": "tkl,en-TK", "north": -8.553613662719727, "south": -9.381111145019531, "iso_alpha_3": "TKL", "fips_code": "TL", "population": "1466", "east": -171.21142578125, "iso_numeric": "772", "area_in_sq_km": "10.0", "country_code": "TK", "west": -172.50033569335938, "country_name": "Tokelau", "continent_name": "Oceania", "currency_code": "NZD"}}, {"model": "countries.country", "pk": 221, "fields": {"continent": "OC", "capital": "Dili", "geoname_id": 1966436, "languages": "tet,pt-TL,id,en", "north": -8.12687015533447, "south": -9.504650115966797, "iso_alpha_3": "TLS", "fips_code": "TT", "population": "1154625", "east": 127.34211730957031, "iso_numeric": "626", "area_in_sq_km": "15007.0", "country_code": "TL", "west": 124.04464721679688, "country_name": "East Timor", "continent_name": "Oceania", "currency_code": "USD"}}, {"model": "countries.country", "pk": 222, "fields": {"continent": "AS", "capital": "Ashgabat", "geoname_id": 1218197, "languages": "tk,ru,uz", "north": 42.795555, "south": 35.141083, "iso_alpha_3": "TKM", "fips_code": "TX", "population": "4940916", "east": 66.684303, "iso_numeric": "795", "area_in_sq_km": "488100.0", "country_code": "TM", "west": 52.441444, "country_name": "Turkmenistan", "continent_name": "Asia", "currency_code": "TMT"}}, {"model": "countries.country", "pk": 223, "fields": {"continent": "AF", "capital": "Tunis", "geoname_id": 2464461, "languages": "ar-TN,fr", "north": 37.543915, "south": 30.240417, "iso_alpha_3": "TUN", "fips_code": "TS", "population": "10589025", "east": 11.598278, "iso_numeric": "788", "area_in_sq_km": "163610.0", "country_code": "TN", "west": 7.524833, "country_name": "Tunisia", "continent_name": "Africa", "currency_code": "TND"}}, {"model": "countries.country", "pk": 224, "fields": {"continent": "OC", "capital": "Nuku'alofa", "geoname_id": 4032283, "languages": "to,en-TO", "north": -15.562988, "south": -21.455057, "iso_alpha_3": "TON", "fips_code": "TN", "population": "122580", "east": -173.907578, "iso_numeric": "776", "area_in_sq_km": "748.0", "country_code": "TO", "west": -175.682266, "country_name": "Tonga", "continent_name": "Oceania", "currency_code": "TOP"}}, {"model": "countries.country", "pk": 225, "fields": {"continent": "AS", "capital": "Ankara", "geoname_id": 298795, "languages": "tr-TR,ku,diq,az,av", "north": 42.107613, "south": 35.815418, "iso_alpha_3": "TUR", "fips_code": "TU", "population": "77804122", "east": 44.834999, "iso_numeric": "792", "area_in_sq_km": "780580.0", "country_code": "TR", "west": 25.668501, "country_name": "Turkey", "continent_name": "Asia", "currency_code": "TRY"}}, {"model": "countries.country", "pk": 226, "fields": {"continent": "NA", "capital": "Port of Spain", "geoname_id": 3573591, "languages": "en-TT,hns,fr,es,zh", "north": 11.338342, "south": 10.036105, "iso_alpha_3": "TTO", "fips_code": "TD", "population": "1328019", "east": -60.517933, "iso_numeric": "780", "area_in_sq_km": "5128.0", "country_code": "TT", "west": -61.923771, "country_name": "Trinidad and Tobago", "continent_name": "North America", "currency_code": "TTD"}}, {"model": "countries.country", "pk": 227, "fields": {"continent": "OC", "capital": "Funafuti", "geoname_id": 2110297, "languages": "tvl,en,sm,gil", "north": -5.641972, "south": -10.801169, "iso_alpha_3": "TUV", "fips_code": "TV", "population": "10472", "east": 179.863281, "iso_numeric": "798", "area_in_sq_km": "26.0", "country_code": "TV", "west": 176.064865, "country_name": "Tuvalu", "continent_name": "Oceania", "currency_code": "AUD"}}, {"model": "countries.country", "pk": 228, "fields": {"continent": "AS", "capital": "Taipei", "geoname_id": 1668284, "languages": "zh-TW,zh,nan,hak", "north": 25.3002899036181, "south": 21.896606934717, "iso_alpha_3": "TWN", "fips_code": "TW", "population": "22894384", "east": 122.006739823315, "iso_numeric": "158", "area_in_sq_km": "35980.0", "country_code": "TW", "west": 119.534691, "country_name": "Taiwan", "continent_name": "Asia", "currency_code": "TWD"}}, {"model": "countries.country", "pk": 229, "fields": {"continent": "AF", "capital": "Dodoma", "geoname_id": 149590, "languages": "sw-TZ,en,ar", "north": -0.990736, "south": -11.745696, "iso_alpha_3": "TZA", "fips_code": "TZ", "population": "41892895", "east": 40.443222, "iso_numeric": "834", "area_in_sq_km": "945087.0", "country_code": "TZ", "west": 29.327168, "country_name": "Tanzania", "continent_name": "Africa", "currency_code": "TZS"}}, {"model": "countries.country", "pk": 230, "fields": {"continent": "EU", "capital": "Kiev", "geoname_id": 690791, "languages": "uk,ru-UA,rom,pl,hu", "north": 52.369362, "south": 44.390415, "iso_alpha_3": "UKR", "fips_code": "UP", "population": "45415596", "east": 40.20739, "iso_numeric": "804", "area_in_sq_km": "603700.0", "country_code": "UA", "west": 22.128889, "country_name": "Ukraine", "continent_name": "Europe", "currency_code": "UAH"}}, {"model": "countries.country", "pk": 231, "fields": {"continent": "AF", "capital": "Kampala", "geoname_id": 226074, "languages": "en-UG,lg,sw,ar", "north": 4.23136926690327, "south": -1.48153052848838, "iso_alpha_3": "UGA", "fips_code": "UG", "population": "33398682", "east": 35.0010535324228, "iso_numeric": "800", "area_in_sq_km": "236040.0", "country_code": "UG", "west": 29.573465338129, "country_name": "Uganda", "continent_name": "Africa", "currency_code": "UGX"}}, {"model": "countries.country", "pk": 232, "fields": {"continent": "OC", "capital": "", "geoname_id": 5854968, "languages": "en-UM", "north": 28.219814, "south": -0.389006, "iso_alpha_3": "UMI", "fips_code": "", "population": "0", "east": 166.654526, "iso_numeric": "581", "area_in_sq_km": "0.0", "country_code": "UM", "west": -177.392029, "country_name": "U.S. Minor Outlying Islands", "continent_name": "Oceania", "currency_code": "USD"}}, {"model": "countries.country", "pk": 233, "fields": {"continent": "NA", "capital": "Washington", "geoname_id": 6252001, "languages": "en-US,es-US,haw,fr", "north": 49.388611, "south": 24.544245, "iso_alpha_3": "USA", "fips_code": "US", "population": "310232863", "east": -66.954811, "iso_numeric": "840", "area_in_sq_km": "9629091.0", "country_code": "US", "west": -124.733253, "country_name": "United States", "continent_name": "North America", "currency_code": "USD"}}, {"model": "countries.country", "pk": 234, "fields": {"continent": "SA", "capital": "Montevideo", "geoname_id": 3439705, "languages": "es-UY", "north": -30.082224, "south": -34.980816, "iso_alpha_3": "URY", "fips_code": "UY", "population": "3477000", "east": -53.073933, "iso_numeric": "858", "area_in_sq_km": "176220.0", "country_code": "UY", "west": -58.442722, "country_name": "Uruguay", "continent_name": "South America", "currency_code": "UYU"}}, {"model": "countries.country", "pk": 235, "fields": {"continent": "AS", "capital": "Tashkent", "geoname_id": 1512440, "languages": "uz,ru,tg", "north": 45.575001, "south": 37.184444, "iso_alpha_3": "UZB", "fips_code": "UZ", "population": "27865738", "east": 73.132278, "iso_numeric": "860", "area_in_sq_km": "447400.0", "country_code": "UZ", "west": 55.996639, "country_name": "Uzbekistan", "continent_name": "Asia", "currency_code": "UZS"}}, {"model": "countries.country", "pk": 236, "fields": {"continent": "EU", "capital": "Vatican City", "geoname_id": 3164670, "languages": "la,it,fr", "north": 41.90743830885576, "south": 41.90027960306854, "iso_alpha_3": "VAT", "fips_code": "VT", "population": "921", "east": 12.45837546629481, "iso_numeric": "336", "area_in_sq_km": "0.44", "country_code": "VA", "west": 12.44570678169205, "country_name": "Vatican City", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 237, "fields": {"continent": "NA", "capital": "Kingstown", "geoname_id": 3577815, "languages": "en-VC,fr", "north": 13.377834, "south": 12.583984810969037, "iso_alpha_3": "VCT", "fips_code": "VC", "population": "104217", "east": -61.11388, "iso_numeric": "670", "area_in_sq_km": "389.0", "country_code": "VC", "west": -61.46090317727658, "country_name": "Saint Vincent and the Grenadines", "continent_name": "North America", "currency_code": "XCD"}}, {"model": "countries.country", "pk": 238, "fields": {"continent": "SA", "capital": "Caracas", "geoname_id": 3625428, "languages": "es-VE", "north": 12.201903, "south": 0.626311, "iso_alpha_3": "VEN", "fips_code": "VE", "population": "27223228", "east": -59.80378, "iso_numeric": "862", "area_in_sq_km": "912050.0", "country_code": "VE", "west": -73.354073, "country_name": "Venezuela", "continent_name": "South America", "currency_code": "VEF"}}, {"model": "countries.country", "pk": 239, "fields": {"continent": "NA", "capital": "Road Town", "geoname_id": 3577718, "languages": "en-VG", "north": 18.757221, "south": 18.383710898211305, "iso_alpha_3": "VGB", "fips_code": "VI", "population": "21730", "east": -64.268768, "iso_numeric": "092", "area_in_sq_km": "153.0", "country_code": "VG", "west": -64.71312752730364, "country_name": "British Virgin Islands", "continent_name": "North America", "currency_code": "USD"}}, {"model": "countries.country", "pk": 240, "fields": {"continent": "NA", "capital": "Charlotte Amalie", "geoname_id": 4796775, "languages": "en-VI", "north": 18.415382, "south": 17.673931, "iso_alpha_3": "VIR", "fips_code": "VQ", "population": "108708", "east": -64.565193, "iso_numeric": "850", "area_in_sq_km": "352.0", "country_code": "VI", "west": -65.101333, "country_name": "U.S. Virgin Islands", "continent_name": "North America", "currency_code": "USD"}}, {"model": "countries.country", "pk": 241, "fields": {"continent": "AS", "capital": "Hanoi", "geoname_id": 1562822, "languages": "vi,en,fr,zh,km", "north": 23.388834, "south": 8.559611, "iso_alpha_3": "VNM", "fips_code": "VM", "population": "89571130", "east": 109.464638, "iso_numeric": "704", "area_in_sq_km": "329560.0", "country_code": "VN", "west": 102.148224, "country_name": "Vietnam", "continent_name": "Asia", "currency_code": "VND"}}, {"model": "countries.country", "pk": 242, "fields": {"continent": "OC", "capital": "Port Vila", "geoname_id": 2134431, "languages": "bi,en-VU,fr-VU", "north": -13.073444, "south": -20.248945, "iso_alpha_3": "VUT", "fips_code": "NH", "population": "221552", "east": 169.904785, "iso_numeric": "548", "area_in_sq_km": "12200.0", "country_code": "VU", "west": 166.524979, "country_name": "Vanuatu", "continent_name": "Oceania", "currency_code": "VUV"}}, {"model": "countries.country", "pk": 243, "fields": {"continent": "OC", "capital": "Mata-Utu", "geoname_id": 4034749, "languages": "wls,fud,fr-WF", "north": -13.216758181061444, "south": -14.314559989820843, "iso_alpha_3": "WLF", "fips_code": "WF", "population": "16025", "east": -176.16174317718253, "iso_numeric": "876", "area_in_sq_km": "274.0", "country_code": "WF", "west": -178.1848112896414, "country_name": "Wallis and Futuna", "continent_name": "Oceania", "currency_code": "XPF"}}, {"model": "countries.country", "pk": 244, "fields": {"continent": "OC", "capital": "Apia", "geoname_id": 4034894, "languages": "sm,en-WS", "north": -13.432207, "south": -14.040939, "iso_alpha_3": "WSM", "fips_code": "WS", "population": "192001", "east": -171.415741, "iso_numeric": "882", "area_in_sq_km": "2944.0", "country_code": "WS", "west": -172.798599, "country_name": "Samoa", "continent_name": "Oceania", "currency_code": "WST"}}, {"model": "countries.country", "pk": 245, "fields": {"continent": "EU", "capital": "Pristina", "geoname_id": 831053, "languages": "sq,sr", "north": 43.2682495807952, "south": 41.856369601859925, "iso_alpha_3": "XKX", "fips_code": "KV", "population": "1800000", "east": 21.80335088694943, "iso_numeric": "0", "area_in_sq_km": "10908.0", "country_code": "XK", "west": 19.977481504492914, "country_name": "Kosovo", "continent_name": "Europe", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 246, "fields": {"continent": "AS", "capital": "Sanaa", "geoname_id": 69543, "languages": "ar-YE", "north": 18.9999989031009, "south": 12.1110910264462, "iso_alpha_3": "YEM", "fips_code": "YM", "population": "23495361", "east": 54.5305388163283, "iso_numeric": "887", "area_in_sq_km": "527970.0", "country_code": "YE", "west": 42.5325394314234, "country_name": "Yemen", "continent_name": "Asia", "currency_code": "YER"}}, {"model": "countries.country", "pk": 247, "fields": {"continent": "AF", "capital": "Mamoudzou", "geoname_id": 1024031, "languages": "fr-YT", "north": -12.648891, "south": -13.000132, "iso_alpha_3": "MYT", "fips_code": "MF", "population": "159042", "east": 45.29295, "iso_numeric": "175", "area_in_sq_km": "374.0", "country_code": "YT", "west": 45.03796, "country_name": "Mayotte", "continent_name": "Africa", "currency_code": "EUR"}}, {"model": "countries.country", "pk": 248, "fields": {"continent": "AF", "capital": "Pretoria", "geoname_id": 953987, "languages": "zu,xh,af,nso,en-ZA,tn,st,ts,ss,ve,nr", "north": -22.1250300579999, "south": -34.8341700029999, "iso_alpha_3": "ZAF", "fips_code": "SF", "population": "49000000", "east": 32.944984945, "iso_numeric": "710", "area_in_sq_km": "1219912.0", "country_code": "ZA", "west": 16.45189, "country_name": "South Africa", "continent_name": "Africa", "currency_code": "ZAR"}}, {"model": "countries.country", "pk": 249, "fields": {"continent": "AF", "capital": "Lusaka", "geoname_id": 895949, "languages": "en-ZM,bem,loz,lun,lue,ny,toi", "north": -8.22436, "south": -18.079473, "iso_alpha_3": "ZMB", "fips_code": "ZA", "population": "13460305", "east": 33.705704, "iso_numeric": "894", "area_in_sq_km": "752614.0", "country_code": "ZM", "west": 21.999371, "country_name": "Zambia", "continent_name": "Africa", "currency_code": "ZMW"}}, {"model": "countries.country", "pk": 250, "fields": {"continent": "AF", "capital": "Harare", "geoname_id": 878675, "languages": "en-ZW,sn,nr,nd", "north": -15.608835, "south": -22.417738, "iso_alpha_3": "ZWE", "fips_code": "ZI", "population": "13061000", "east": 33.056305, "iso_numeric": "716", "area_in_sq_km": "390580.0", "country_code": "ZW", "west": 25.237028, "country_name": "Zimbabwe", "continent_name": "Africa", "currency_code": "ZWL"}}] -------------------------------------------------------------------------------- /example/countries/fixtures/token.json: -------------------------------------------------------------------------------- 1 | [{"model": "authtoken.token", "pk": "4fd264e3afc459bc1e0971aa2982a90d3aa191bb", "fields": {"user": 1, "created": "2017-10-28T17:34:22.698Z"}}] -------------------------------------------------------------------------------- /example/countries/fixtures/user.json: -------------------------------------------------------------------------------- 1 | [{"model": "auth.user", "pk": 1, "fields": {"password": "pbkdf2_sha256$36000$FC0fvOJYhVnM$uzzyL7aGiHZaajGq+Cy/FmIqDE8AUp3W4Qz1QRr1WKo=", "last_login": null, "is_superuser": true, "username": "george", "first_name": "", "last_name": "", "email": "george@vandelay.com", "is_staff": true, "is_active": true, "date_joined": "2017-10-28T17:17:15.054Z", "groups": [], "user_permissions": []}}] -------------------------------------------------------------------------------- /example/countries/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.6 on 2017-10-25 20:00 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Country', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('continent', models.CharField(max_length=4)), 21 | ('capital', models.CharField(max_length=255)), 22 | ('geoname_id', models.IntegerField()), 23 | ('languages', models.CharField(max_length=255)), 24 | ('north', models.FloatField()), 25 | ('south', models.FloatField()), 26 | ('iso_alpha_3', models.CharField(max_length=3)), 27 | ('fips_code', models.CharField(max_length=3)), 28 | ('population', models.CharField(max_length=255)), 29 | ('east', models.FloatField()), 30 | ('iso_numeric', models.CharField(max_length=255)), 31 | ('area_in_sq_km', models.CharField(max_length=255)), 32 | ('country_code', models.CharField(max_length=2)), 33 | ('west', models.FloatField()), 34 | ('country_name', models.CharField(max_length=255)), 35 | ('continent_name', models.CharField(max_length=255)), 36 | ('currency_code', models.CharField(max_length=4)), 37 | ], 38 | ), 39 | ] 40 | -------------------------------------------------------------------------------- /example/countries/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcgibbons/drf_signed_auth/39bdd3ea01a09a00fd3c9bf7c6d2ad6bb4dabba6/example/countries/migrations/__init__.py -------------------------------------------------------------------------------- /example/countries/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Country(models.Model): 5 | country_name = models.CharField(max_length=255) 6 | continent_name = models.CharField(max_length=255) 7 | capital = models.CharField(max_length=255) 8 | languages = models.CharField(max_length=255) 9 | continent = models.CharField(max_length=4) 10 | geoname_id = models.IntegerField() 11 | north = models.FloatField() 12 | south = models.FloatField() 13 | east = models.FloatField() 14 | west = models.FloatField() 15 | iso_alpha_3 = models.CharField(max_length=3) 16 | fips_code = models.CharField(max_length=3) 17 | population = models.CharField(max_length=255) 18 | iso_numeric = models.CharField(max_length=255) 19 | area_in_sq_km = models.CharField(max_length=255) 20 | country_code = models.CharField(max_length=2) 21 | currency_code = models.CharField(max_length=4) 22 | 23 | class Meta: 24 | ordering = ['country_name'] 25 | -------------------------------------------------------------------------------- /example/countries/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from . import models 4 | 5 | 6 | class CountrySerializer(serializers.ModelSerializer): 7 | class Meta: 8 | model = models.Country 9 | fields = '__all__' 10 | -------------------------------------------------------------------------------- /example/countries/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /example/countries/urls.py: -------------------------------------------------------------------------------- 1 | from rest_framework.routers import SimpleRouter 2 | 3 | from . import views 4 | 5 | 6 | router = SimpleRouter() 7 | router.register('countries', views.CountryViewSet) 8 | 9 | urlpatterns = router.urls 10 | -------------------------------------------------------------------------------- /example/countries/views.py: -------------------------------------------------------------------------------- 1 | from drf_signed_auth.authentication import SignedURLAuthentication 2 | from rest_framework import viewsets 3 | from rest_framework.settings import api_settings 4 | 5 | from . import models, serializers 6 | 7 | 8 | class CountryViewSet(viewsets.ReadOnlyModelViewSet): 9 | authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES + [ 10 | SignedURLAuthentication 11 | ] 12 | serializer_class = serializers.CountrySerializer 13 | queryset = models.Country.objects.all() 14 | -------------------------------------------------------------------------------- /example/example/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcgibbons/drf_signed_auth/39bdd3ea01a09a00fd3c9bf7c6d2ad6bb4dabba6/example/example/__init__.py -------------------------------------------------------------------------------- /example/example/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcgibbons/drf_signed_auth/39bdd3ea01a09a00fd3c9bf7c6d2ad6bb4dabba6/example/example/models.py -------------------------------------------------------------------------------- /example/example/renderers.py: -------------------------------------------------------------------------------- 1 | from rest_framework_csv.renderers import CSVRenderer 2 | 3 | 4 | class PaginatedCSVRenderer(CSVRenderer): 5 | results_field = 'results' 6 | 7 | def render( 8 | self, 9 | data, 10 | media_type=None, 11 | renderer_context=None, 12 | writer_opts=None 13 | ): 14 | if not isinstance(data, list): 15 | data = data.get(self.results_field, []) 16 | 17 | filename = renderer_context['view'] \ 18 | .get_view_name().replace(' ', '_').lower() 19 | 20 | renderer_context['response']['Content-Disposition'] = \ 21 | f'attachment; filename="{filename}.csv"' 22 | 23 | return super().render(data, media_type, renderer_context, writer_opts) 24 | -------------------------------------------------------------------------------- /example/example/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for example project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.11.6. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.11/ref/settings/ 11 | """ 12 | 13 | from distutils.util import strtobool 14 | import os 15 | import dj_database_url 16 | 17 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 18 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 19 | 20 | 21 | # Quick-start development settings - unsuitable for production 22 | # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ 23 | 24 | # SECURITY WARNING: keep the secret key used in production secret! 25 | SECRET_KEY = 'kn^t*y!9lz5@p%3bcqeq8k+9irtme4hh9%!kzr&r9wual1o4%s' 26 | 27 | # SECURITY WARNING: don't run with debug turned on in production! 28 | DEBUG = strtobool(os.environ.get('DEBUG', '1')) 29 | 30 | ALLOWED_HOSTS = ['*'] 31 | 32 | 33 | # Application definition 34 | 35 | INSTALLED_APPS = [ 36 | 'django.contrib.auth', 37 | 'django.contrib.contenttypes', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'rest_framework', 41 | 'rest_framework.authtoken', 42 | 'countries' 43 | ] 44 | 45 | MIDDLEWARE = [ 46 | 'django.middleware.security.SecurityMiddleware', 47 | 'whitenoise.middleware.WhiteNoiseMiddleware', 48 | 'django.contrib.sessions.middleware.SessionMiddleware', 49 | 'django.middleware.common.CommonMiddleware', 50 | 'django.middleware.csrf.CsrfViewMiddleware', 51 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 52 | 'django.contrib.messages.middleware.MessageMiddleware', 53 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 54 | ] 55 | 56 | ROOT_URLCONF = 'example.urls' 57 | 58 | TEMPLATES = [ 59 | { 60 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 61 | 'DIRS': [ 62 | os.path.join(BASE_DIR, 'templates') 63 | ], 64 | 'APP_DIRS': True, 65 | 'OPTIONS': { 66 | 'context_processors': [ 67 | 'django.template.context_processors.debug', 68 | 'django.template.context_processors.request', 69 | 'django.contrib.auth.context_processors.auth', 70 | 'django.contrib.messages.context_processors.messages', 71 | ], 72 | }, 73 | }, 74 | ] 75 | 76 | WSGI_APPLICATION = 'example.wsgi.application' 77 | 78 | 79 | # Database 80 | # https://docs.djangoproject.com/en/1.11/ref/settings/#databases 81 | DATABASES = { 82 | 'default': dj_database_url.config() or { 83 | 'ENGINE': 'django.db.backends.sqlite3', 84 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 85 | } 86 | } 87 | 88 | 89 | # Password validation 90 | # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators 91 | 92 | AUTH_PASSWORD_VALIDATORS = [ 93 | { 94 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 95 | }, 96 | { 97 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 98 | }, 99 | { 100 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 101 | }, 102 | { 103 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 104 | }, 105 | ] 106 | 107 | 108 | # Internationalization 109 | # https://docs.djangoproject.com/en/1.11/topics/i18n/ 110 | 111 | LANGUAGE_CODE = 'en-us' 112 | 113 | TIME_ZONE = 'UTC' 114 | 115 | USE_I18N = True 116 | 117 | USE_L10N = True 118 | 119 | USE_TZ = True 120 | 121 | 122 | # Static files (CSS, JavaScript, Images) 123 | # https://docs.djangoproject.com/en/1.11/howto/static-files/ 124 | 125 | STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' 126 | STATIC_URL = '/static/' 127 | STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') 128 | STATICFILES_DIRS = [ 129 | os.path.join(BASE_DIR, 'static') 130 | ] 131 | 132 | 133 | REST_FRAMEWORK = { 134 | 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 135 | 'DEFAULT_PERMISSION_CLASSES': ( 136 | 'rest_framework.permissions.IsAuthenticated', 137 | ), 138 | 'DEFAULT_AUTHENTICATION_CLASSES': ( 139 | 'rest_framework.authentication.TokenAuthentication', 140 | ), 141 | 'DEFAULT_RENDERER_CLASSES': ( 142 | 'rest_framework.renderers.JSONRenderer', 143 | 'rest_framework.renderers.BrowsableAPIRenderer', 144 | 'example.renderers.PaginatedCSVRenderer', 145 | ), 146 | 'PAGE_SIZE': 10 147 | } 148 | -------------------------------------------------------------------------------- /example/example/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import include, url 2 | from drf_signed_auth.views import SignUrlView 3 | 4 | from . import views 5 | 6 | 7 | urlpatterns = [ 8 | url(r'', include('countries.urls')), 9 | url(r'^sign_url/', SignUrlView.as_view(), name='url-signer'), 10 | url(r'^$', views.index) 11 | ] 12 | -------------------------------------------------------------------------------- /example/example/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from django.shortcuts import render 3 | from rest_framework.authtoken.models import Token 4 | 5 | 6 | def index(request): 7 | # Get an access token for George. Depends on fixtures. 8 | george = get_user_model().objects.get(username='george') 9 | token = Token.objects.get(user=george) 10 | 11 | # We'll pass the token to the template and set it as a 12 | # global XHR authorization header. 13 | return render(request, 'index.html', {'token': token.key}) 14 | -------------------------------------------------------------------------------- /example/example/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for example project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /example/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | except ImportError: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) 21 | raise 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /example/static/js/countries.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function () { 2 | var apiUrl = window.urls.countryList; 3 | var fields = [ 4 | 'country_name', 5 | 'continent_name', 6 | 'capital', 7 | 'area_in_sq_km', 8 | 'population' 9 | ]; 10 | 11 | var $next = $('#next'); 12 | var $prev = $('#prev'); 13 | // Fetch data to load table 14 | function getData(url) { 15 | $.ajax({ 16 | url: url, 17 | type: 'GET', 18 | }).then(function (data) { 19 | $next.toggleClass('disabled', !data.next); 20 | $next.data('url', data.next); 21 | 22 | $prev.toggleClass('disabled', !data.previous); 23 | $prev.data('url', data.previous); 24 | 25 | var els = $.map(data.results, function (obj) { 26 | var tr = $(''); 27 | $.each(fields, function (idx, field) { 28 | var td = $(''); 29 | td.html(obj[field]); 30 | $(tr).append(td); 31 | }); 32 | return tr; 33 | }); 34 | $('#table-data').html(els); 35 | }); 36 | }; 37 | 38 | $next.on('click', function (e) { 39 | e.preventDefault(); 40 | var url = $next.data('url'); 41 | if (!url) { 42 | return; 43 | } 44 | getData(url); 45 | }); 46 | 47 | $prev.on('click', function (e) { 48 | e.preventDefault(); 49 | var url = $prev.data('url'); 50 | if (!url) { 51 | return; 52 | } 53 | getData(url); 54 | }) 55 | 56 | getData(apiUrl); 57 | }); 58 | 59 | 60 | -------------------------------------------------------------------------------- /example/static/js/download-countries.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function () { 2 | var countriesUrl = window.urls.countryList + '?format=csv'; 3 | var signer = window.urls.signer; 4 | 5 | $('#download-countries').on('click', function (e) { 6 | e.preventDefault(); 7 | console.log('Download button clicked, fetching signed URL'); 8 | 9 | // Sign URL 10 | $.post(signer, {url: countriesUrl}).then(function (data) { 11 | console.log('Redirecting to ', data); 12 | window.location.assign(data); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /example/templates/index.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | Example direct download 5 | 6 | 7 | 19 | 20 | 21 |
22 |

Countries of the World

23 |

Data obtained from GeoNames.org

24 | 25 |
26 | Download as CSV 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 48 | 49 | 50 |
CountryContinentCapitalArea (km2)Population
43 |
    44 | 45 | 46 |
47 |
51 |
52 |

53 | This example app illustrates direct download functionality of a protected 54 | API resource. By clicking the download button, a temporary URL is generated 55 | which the browser then uses to access the resource. 56 |

57 |

58 | An authentication token has been hardcoded for all XHR requests made on 59 | on this page. If you try to access the 60 | /countries resource 61 | directly, a 401 response should be returned. 62 |

63 |
64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | persistent=yes 3 | ignore=migrations 4 | cache-size=500 5 | 6 | [MESSAGES CONTROL] 7 | # C0111 Missing docstring 8 | # I0011 Warning locally suppressed using disable-msg 9 | # I0012 Warning locally suppressed using disable-msg 10 | # W0704 Except doesn't do anything Used when an except clause does nothing but "pass" and there is no "else" clause 11 | # W0142 Used * or * magic* Used when a function or method is called using *args or **kwargs to dispatch arguments. 12 | # W0212 Access to a protected member %s of a client class 13 | # W0232 Class has no __init__ method Used when a class has no __init__ method, neither its parent classes. 14 | # W0613 Unused argument %r Used when a function or method argument is not used. 15 | # W0702 No exception's type specified Used when an except clause doesn't specify exceptions type to catch. 16 | # R0201 Method could be a function 17 | # C1001 Used when a class is defined that does not inherit from anotherclass and does not inherit explicitly from “object”. 18 | # C0103 Invalid module name 19 | # R0901 Used when class has too many parent classes, try to reduce this to get a simpler (and so easier to use) class. 20 | # E1101 Used when an object (variable, function, …) is accessed for a non-existent member 21 | # W0223 Used when an abstract method (i.e. one that raises NotImplementedError) is not overridden in concrete class 22 | disable=C0111,I0011,I0012,W0704,W0142,W0212,W0232,W0613,W0702,R0201,C1001,C0103,R0901,E1101,W0223 23 | 24 | [REPORTS] 25 | msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg} 26 | 27 | 28 | [BASIC] 29 | no-docstring-rgx=__.*__|_.* 30 | class-rgx=[A-Z_][a-zA-Z0-9_]+$ 31 | function-rgx=[a-zA_][a-zA-Z0-9_]{2,70}$ 32 | method-rgx=[a-z_][a-zA-Z0-9_]{2,70}$ 33 | const-rgx=(([A-Z_][A-Z0-9_]*)|([a-z_][a-z0-9_]*)|(__.*__)|register|urlpatterns)$ 34 | good-names=_,i,j,k,e,qs,pk,setUp,tearDown 35 | 36 | [TYPECHECK] 37 | 38 | # Tells whether missing members accessed in mixin class should be ignored. A 39 | # mixin class is detected if its name ends with "mixin" (case insensitive). 40 | ignore-mixin-members=yes 41 | 42 | # List of classes names for which member attributes should not be checked 43 | # (useful for classes with attributes dynamically set). 44 | ignored-classes=SQLObject,WSGIRequest 45 | 46 | # List of members which are set dynamically and missed by pylint inference 47 | # system, and so shouldn't trigger E0201 when accessed. 48 | generated-members=objects,DoesNotExist,id,pk,_meta,base_fields,context 49 | 50 | # List of method names used to declare (i.e. assign) instance attributes 51 | defining-attr-methods=__init__,__new__,setUp 52 | 53 | 54 | [VARIABLES] 55 | init-import=no 56 | dummy-variables-rgx=_|dummy 57 | 58 | [SIMILARITIES] 59 | min-similarity-lines=8 60 | ignore-comments=yes 61 | ignore-docstrings=yes 62 | 63 | 64 | [MISCELLANEOUS] 65 | notes=FIXME,XXX 66 | 67 | 68 | [FORMAT] 69 | max-line-length=79 70 | max-module-lines=500 71 | indent-string=' ' 72 | 73 | 74 | [DESIGN] 75 | max-args=10 76 | max-locals=15 77 | max-returns=6 78 | max-branches=12 79 | max-statements=50 80 | max-parents=7 81 | max-attributes=10 82 | min-public-methods=0 83 | max-public-methods=50 84 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | -r requirements/example_app.txt 2 | -------------------------------------------------------------------------------- /requirements/example_app.txt: -------------------------------------------------------------------------------- 1 | -e . 2 | Django==1.11.6 3 | dj-database-url==0.4.2 4 | djangorestframework-csv==2.0.0 5 | djangorestframework==3.7.1 6 | gunicorn==19.7.1 7 | psycopg2==2.7.3.2 8 | whitenoise==3.3.1 9 | -------------------------------------------------------------------------------- /requirements/packaging.txt: -------------------------------------------------------------------------------- 1 | # Wheel for PyPI installs. 2 | wheel==0.29.0 3 | 4 | # Twine for secured PyPI uploads. 5 | twine==1.9.1 6 | 7 | # Pandoc to have a nice pypi page 8 | pypandoc==1.4 9 | -------------------------------------------------------------------------------- /requirements/testing.txt: -------------------------------------------------------------------------------- 1 | mock==2.0.0 # Backport for Python2 2 | model_mommy==1.4.0 3 | pep8==1.7.0 4 | pylint==1.7.2 5 | coverage==4.4.1 6 | -------------------------------------------------------------------------------- /runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | import coverage 6 | import django 7 | from django.conf import settings 8 | from django.test.utils import get_runner 9 | 10 | 11 | if __name__ == '__main__': 12 | cov = coverage.Coverage(auto_data=True, include='drf_signed_auth/*.py') 13 | cov.start() 14 | os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.settings' 15 | django.setup() 16 | TestRunner = get_runner(settings) 17 | test_runner = TestRunner() 18 | failures = test_runner.run_tests(["tests"]) 19 | cov.stop() 20 | sys.exit(bool(failures)) 21 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6.2 2 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # setup.py taken from https://github.com/encode/django-rest-framework/blob/master/setup.py 5 | from io import open 6 | import os 7 | import re 8 | import shutil 9 | import sys 10 | 11 | from setuptools import setup 12 | 13 | 14 | try: 15 | from pypandoc import convert 16 | 17 | def read_md(f): 18 | return convert(f, 'rst') 19 | except ImportError: 20 | print("warning: pypandoc module not found, could not convert Markdown to RST") 21 | 22 | def read_md(f): 23 | return open(f, 'r', encoding='utf-8').read() 24 | 25 | 26 | def get_version(package): 27 | """ 28 | Return package version as listed in `__version__` in `init.py`. 29 | """ 30 | init_py = open(os.path.join(package, '__init__.py')).read() 31 | return re.search("__version__ = ['\"]([^'\"]+)['\"]", init_py).group(1) 32 | 33 | 34 | def get_packages(package): 35 | """ 36 | Return root package and all sub-packages. 37 | """ 38 | return [dirpath 39 | for dirpath, dirnames, filenames in os.walk(package) 40 | if os.path.exists(os.path.join(dirpath, '__init__.py'))] 41 | 42 | 43 | def get_package_data(package): 44 | """ 45 | Return all files under the root package, that are not in a 46 | package themselves. 47 | """ 48 | walk = [(dirpath.replace(package + os.sep, '', 1), filenames) 49 | for dirpath, dirnames, filenames in os.walk(package) 50 | if not os.path.exists(os.path.join(dirpath, '__init__.py'))] 51 | 52 | filepaths = [] 53 | for base, filenames in walk: 54 | filepaths.extend([os.path.join(base, filename) 55 | for filename in filenames]) 56 | return {package: filepaths} 57 | 58 | 59 | version = get_version('drf_signed_auth') 60 | 61 | 62 | if sys.argv[-1] == 'publish': 63 | try: 64 | import pypandoc 65 | except ImportError: 66 | print("pypandoc not installed.\nUse `pip install pypandoc`.\nExiting.") 67 | if os.system("pip freeze | grep twine"): 68 | print("twine not installed.\nUse `pip install twine`.\nExiting.") 69 | sys.exit() 70 | os.system("python setup.py sdist bdist_wheel") 71 | os.system("twine upload dist/*") 72 | print("You probably want to also tag the version now:") 73 | print(" git tag -a %s -m 'version %s'" % (version, version)) 74 | print(" git push --tags") 75 | shutil.rmtree('dist') 76 | shutil.rmtree('build') 77 | shutil.rmtree('drf_signed_auth.egg-info') 78 | sys.exit() 79 | 80 | 81 | setup( 82 | name='drf_signed_auth', 83 | version=version, 84 | url='https://github.com/marcgibbons/drf_signed_auth', 85 | license='BSD', 86 | description='Sign and authenticate signed URLs for one-time use', 87 | long_description=read_md('README.md'), 88 | author='Marc Gibbons', 89 | author_email='marc_gibbons@rogers.com', 90 | packages=get_packages('drf_signed_auth'), 91 | package_data=get_package_data('drf_signed_auth'), 92 | install_requires=[ 93 | 'furl>=1.0.1' 94 | ], 95 | zip_safe=False, 96 | classifiers=[ 97 | 'Development Status :: 4 - Beta', 98 | 'Environment :: Web Environment', 99 | 'Framework :: Django', 100 | 'Framework :: Django :: 1.8', 101 | 'Framework :: Django :: 1.9', 102 | 'Framework :: Django :: 1.10', 103 | 'Framework :: Django :: 1.11', 104 | 'Intended Audience :: Developers', 105 | 'License :: OSI Approved :: BSD License', 106 | 'Operating System :: OS Independent', 107 | 'Programming Language :: Python', 108 | 'Programming Language :: Python :: 2', 109 | 'Programming Language :: Python :: 2.7', 110 | 'Programming Language :: Python :: 3', 111 | 'Programming Language :: Python :: 3.6', 112 | 'Topic :: Internet :: WWW/HTTP', 113 | ] 114 | ) 115 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcgibbons/drf_signed_auth/39bdd3ea01a09a00fd3c9bf7c6d2ad6bb4dabba6/tests/__init__.py -------------------------------------------------------------------------------- /tests/integration/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcgibbons/drf_signed_auth/39bdd3ea01a09a00fd3c9bf7c6d2ad6bb4dabba6/tests/integration/__init__.py -------------------------------------------------------------------------------- /tests/integration/test_signing.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from django.contrib.auth import get_user_model 4 | from drf_signed_auth.compat import mock 5 | from drf_signed_auth.compat import reverse 6 | from drf_signed_auth.views import SignUrlView 7 | from model_mommy import mommy 8 | from rest_framework.test import APITestCase 9 | 10 | 11 | class SignUrlViewTest(APITestCase): 12 | def setUp(self): 13 | self.url = reverse('sign-url') 14 | self.client.force_authenticate(mommy.make(get_user_model())) 15 | 16 | def test_when_no_data_is_present(self): 17 | """ 18 | Given that the `url` is missing in the request POST data, 19 | a 400 error should be returned. 20 | """ 21 | response = self.client.post(self.url) 22 | self.assertEqual(400, response.status_code) 23 | 24 | self.assertEqual(['`url` must be provided'], response.data) 25 | 26 | @mock.patch.object(SignUrlView, 'get_signed_url') 27 | def test_when_url_is_present(self, sign_mock): 28 | """ 29 | Given that the `url` has been provided, the response 30 | should have a status code of 200 and a signed URL should 31 | be returned. 32 | """ 33 | url = '/relative-path' 34 | sign_mock.return_value = str(uuid.uuid4()) # Something random 35 | response = self.client.post(self.url, data={'url': url}) 36 | self.assertEqual(200, response.status_code) 37 | sign_mock.assert_called_once_with(url) 38 | 39 | self.assertEqual(sign_mock.return_value, response.data) 40 | 41 | 42 | class AuthWorklfowTest(APITestCase): 43 | """ 44 | Executes E2E workflow where: 45 | 1) A URL is signed 46 | 2) The returned URI is followed 47 | 3) The protected resource is rendered 48 | """ 49 | def setUp(self): 50 | self.sign_url = reverse('sign-url') # Signature provider view 51 | self.me_url = reverse('me') # Protected view 52 | 53 | def test_me_view_is_not_accessible_if_not_authenticated(self): 54 | """ 55 | Given that an AnonymousUser requests the Me view, 56 | a 401 status code should be returned. 57 | """ 58 | response = self.client.get(self.me_url) 59 | self.assertEqual(403, response.status_code) 60 | 61 | def test_me_view_is_accessible_given_a_sign_request(self): 62 | """ 63 | Given that an AnonymousUser requests the Me view, 64 | and provides a valid signature, a 200 status code 65 | should be returned with an expected payload. 66 | """ 67 | user = mommy.make(get_user_model()) 68 | self.client.force_authenticate(user) 69 | 70 | # Get the signature 71 | response = self.client.post(self.sign_url, data={'url': self.me_url}) 72 | signed_url = response.data 73 | 74 | # Log the user out; access signed URL without state 75 | self.client.logout() 76 | 77 | # Get protected content 78 | response = self.client.get(signed_url) 79 | self.assertEqual(200, response.status_code) 80 | self.assertEqual( 81 | { 82 | 'username': user.username, 83 | 'full_name': user.get_full_name() 84 | }, 85 | response.data 86 | ) 87 | -------------------------------------------------------------------------------- /tests/integration/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from drf_signed_auth.views import SignUrlView 3 | 4 | from .views import MeView 5 | 6 | 7 | urlpatterns = [ 8 | url(r'sign-url/$', SignUrlView.as_view(), name='sign-url'), 9 | url(r'me/$', MeView.as_view(), name='me'), 10 | ] 11 | -------------------------------------------------------------------------------- /tests/integration/views.py: -------------------------------------------------------------------------------- 1 | from drf_signed_auth.authentication import SignedURLAuthentication 2 | from rest_framework.permissions import IsAuthenticated 3 | from rest_framework.response import Response 4 | from rest_framework.views import APIView 5 | 6 | 7 | class MeView(APIView): 8 | """ 9 | Returns currently logged in user. 10 | 11 | This view 12 | """ 13 | authentication_classes = [SignedURLAuthentication] 14 | permission_classes = [IsAuthenticated] 15 | 16 | def get(self, request): 17 | return Response({ 18 | 'username': request.user.username, 19 | 'full_name': request.user.get_full_name() 20 | }) 21 | -------------------------------------------------------------------------------- /tests/settings.py: -------------------------------------------------------------------------------- 1 | SECRET_KEY = 'fake-key' 2 | INSTALLED_APPS = [ 3 | 'django.contrib.contenttypes', 4 | 'django.contrib.auth', 5 | 'django.contrib.sessions', 6 | 'tests', 7 | 'tests.unit' 8 | ] 9 | 10 | ROOT_URLCONF = 'tests.integration.urls' 11 | DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3'}} 12 | -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcgibbons/drf_signed_auth/39bdd3ea01a09a00fd3c9bf7c6d2ad6bb4dabba6/tests/unit/__init__.py -------------------------------------------------------------------------------- /tests/unit/models.py: -------------------------------------------------------------------------------- 1 | """ 2 | Stores models used in unit tests. 3 | """ 4 | from django.contrib.auth.models import AbstractBaseUser 5 | from django.db import models 6 | 7 | 8 | class CustomUser(AbstractBaseUser): 9 | """ 10 | Custom user model for the unit tests. 11 | 12 | This model is used to demonstrate functionality when the 13 | primary key and username fields are modified. 14 | """ 15 | USERNAME_FIELD = 'email' 16 | 17 | my_random_id_field = models.PositiveIntegerField( 18 | primary_key=True, 19 | unique=True, 20 | ) 21 | email = models.EmailField() 22 | 23 | def get_full_name(self): 24 | return self.email 25 | 26 | def get_short_name(self): 27 | return self.pk 28 | -------------------------------------------------------------------------------- /tests/unit/test_authentication.py: -------------------------------------------------------------------------------- 1 | 2 | from django.contrib.auth import get_user_model 3 | from django.core import signing 4 | from django.test import TestCase 5 | from drf_signed_auth.authentication import SignedURLAuthentication 6 | from drf_signed_auth import settings 7 | from drf_signed_auth.compat import mock 8 | from model_mommy import mommy 9 | from rest_framework import exceptions 10 | from rest_framework.request import Request 11 | from rest_framework.test import APIRequestFactory 12 | 13 | 14 | class AuthenticateTest(TestCase): 15 | def setUp(self): 16 | self.factory = APIRequestFactory() 17 | self.user = mommy.make(get_user_model()) 18 | 19 | self.sut = SignedURLAuthentication().authenticate 20 | 21 | def test_call_without_signature_param(self): 22 | """ 23 | Given that the signature is not provided, 24 | None should be returned. 25 | """ 26 | request = Request(self.factory.get('/fizz')) 27 | self.assertIsNone(self.sut(request)) 28 | 29 | @mock.patch('drf_signed_auth.signing.UserSigner.unsign') 30 | def test_signature_unsiged_using_user_signer(self, unsign_mock): 31 | """ 32 | Given that a signature is provided, the UserSigner 33 | should decrypt this signature, and if a valid user, 34 | return. 35 | """ 36 | signature = 'serenityNow!' 37 | params = {settings.SIGNED_URL_QUERY_PARAM: signature} 38 | request = Request(self.factory.get('/', data=params)) 39 | unsign_mock.return_value = self.user 40 | result = self.sut(request) 41 | 42 | unsign_mock.assert_called_once_with(signature) 43 | expected = (self.user, None) 44 | 45 | self.assertEqual(expected, result) 46 | 47 | @mock.patch('drf_signed_auth.signing.UserSigner.unsign') 48 | def test_inactive_user(self, unsign_mock): 49 | """ 50 | Given that the unsigned user is inactive, 51 | an AuthenticationFailed exception should be raised. 52 | """ 53 | self.user.is_active = False 54 | self.user.save() 55 | 56 | params = {settings.SIGNED_URL_QUERY_PARAM: 'henniganScotch'} 57 | request = Request(self.factory.get('/', data=params)) 58 | unsign_mock.return_value = self.user 59 | 60 | with self.assertRaises(exceptions.AuthenticationFailed) as cm: 61 | self.sut(request) 62 | 63 | self.assertEqual('User inactive or deleted.', str(cm.exception)) 64 | 65 | @mock.patch('drf_signed_auth.signing.UserSigner.unsign') 66 | def test_expired_url(self, unsign_mock): 67 | """ 68 | Given that the signature has expired, an AuthenticationFailed 69 | exception should be raised. 70 | """ 71 | unsign_mock.side_effect = signing.SignatureExpired 72 | params = {settings.SIGNED_URL_QUERY_PARAM: 'puffyShirt'} 73 | request = Request(self.factory.get('/', data=params)) 74 | 75 | with self.assertRaises(exceptions.AuthenticationFailed) as cm: 76 | self.sut(request) 77 | 78 | self.assertEqual('This URL has expired.', str(cm.exception)) 79 | 80 | @mock.patch('drf_signed_auth.signing.UserSigner.unsign') 81 | def test_bad_signature(self, unsign_mock): 82 | """ 83 | Given that the signature was malformed, an AuthenticationFailed 84 | exception should be raised. 85 | """ 86 | unsign_mock.side_effect = signing.BadSignature 87 | params = {settings.SIGNED_URL_QUERY_PARAM: 'spongeWorthySignature'} 88 | request = Request(self.factory.get('/', data=params)) 89 | 90 | with self.assertRaises(exceptions.AuthenticationFailed) as cm: 91 | self.sut(request) 92 | 93 | self.assertEqual('Invalid signature.', str(cm.exception)) 94 | -------------------------------------------------------------------------------- /tests/unit/test_settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests application settings are connected to Django 3 | settings and have sensible default values. 4 | """ 5 | from django.test import TestCase, override_settings 6 | from drf_signed_auth import settings 7 | from drf_signed_auth.compat import reload 8 | from rest_framework import permissions 9 | 10 | 11 | class SettingsTest(TestCase): 12 | def setUp(self): 13 | self.sut = settings 14 | self.addCleanup(lambda: reload(settings)) 15 | 16 | def test_default_ttl(self): 17 | self.assertEqual(30, settings.SIGNED_URL_TTL) 18 | 19 | def test_ttl_set_from_django_settings(self): 20 | expected = 9999 21 | with override_settings(SIGNED_URL_TTL=expected): 22 | reload(settings) 23 | self.assertEqual(expected, settings.SIGNED_URL_TTL) 24 | 25 | def test_default_signature_param(self): 26 | self.assertEqual('sig', settings.SIGNED_URL_QUERY_PARAM) 27 | 28 | def test_signature_param_from_django_settings(self): 29 | expected = 'serenity' 30 | with override_settings(SIGNED_URL_QUERY_PARAM=expected): 31 | reload(settings) 32 | self.assertEqual(expected, settings.SIGNED_URL_QUERY_PARAM) 33 | 34 | def test_default_permission_classes(self): 35 | expected = [permissions.IsAuthenticated] 36 | self.assertEqual(expected, settings.SIGNED_URL_PERMISSION_CLASSES) 37 | 38 | def test_permission_classes_from_django_settings(self): 39 | expected = ['some', 'other', 'classes'] 40 | with override_settings(SIGNED_URL_PERMISSION_CLASSES=expected): 41 | reload(settings) 42 | self.assertEqual(expected, settings.SIGNED_URL_PERMISSION_CLASSES) 43 | -------------------------------------------------------------------------------- /tests/unit/test_signing.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from django.core import signing 3 | from django.test import TestCase, override_settings 4 | from drf_signed_auth import settings 5 | from drf_signed_auth.compat import mock 6 | from drf_signed_auth.signing import UserSigner 7 | from model_mommy import mommy 8 | 9 | 10 | class SignTest(TestCase): 11 | """ 12 | Covers the UserSigner.sign method. 13 | """ 14 | def setUp(self): 15 | self.sut = UserSigner().sign 16 | 17 | @mock.patch('django.core.signing.dumps') 18 | def test_dumps_user_pk_and_user_name(self, dumps_mock): 19 | """ 20 | Asserts that the signature is created using the user's 21 | primary key and username. 22 | """ 23 | user = mock.MagicMock() 24 | self.sut(user) 25 | 26 | dumps_mock.assert_called_once_with({ 27 | 'user_id': user.pk, 28 | 'username': user.get_username() 29 | }) 30 | 31 | @mock.patch('django.core.signing.dumps') 32 | @mock.patch('django.core.signing.TimestampSigner.sign') 33 | def test_signs_result_of_dump(self, sign_mock, dumps_mock): 34 | """ 35 | Asserts that the django TimestampSigner sign method 36 | is called using the dumped user data and returned. 37 | """ 38 | user = mock.MagicMock() 39 | result = self.sut(user) 40 | sign_mock.assert_called_once_with(dumps_mock.return_value) 41 | 42 | self.assertEqual(sign_mock.return_value, result) 43 | 44 | 45 | class UnsignTest(TestCase): 46 | def setUp(self): 47 | self.sut = UserSigner().unsign 48 | 49 | def test_unsign_none(self): 50 | """ 51 | Given a None signature, a BadSignature exception should be 52 | raised. 53 | """ 54 | with self.assertRaises(signing.BadSignature): 55 | self.sut(None) 56 | 57 | def test_unsign_an_invalid_structure(self): 58 | """ 59 | Given an invalid data type (not a dict), a BadSignature 60 | exception should be raised. 61 | """ 62 | signer = signing.TimestampSigner() 63 | signature = signer.sign(signing.dumps('this is not a dict')) 64 | 65 | with self.assertRaises(signing.BadSignature): 66 | self.sut(signature) 67 | 68 | def test_unsigns_an_empty_dict(self): 69 | """ 70 | Given that a dict is returned, but does not have 71 | appropriate keys, a BadSignature exception should 72 | raised. 73 | """ 74 | signer = signing.TimestampSigner() 75 | signature = signer.sign(signing.dumps({'fizz': 'buzz'})) 76 | 77 | with self.assertRaises(signing.BadSignature): 78 | self.sut(signature) 79 | 80 | def test_user_does_not_exist(self): 81 | """ 82 | Given that primary key and username do not match 83 | an existing user, a BadSignature exception should be 84 | raised. 85 | """ 86 | signer = signing.TimestampSigner() 87 | signature = signer.sign( 88 | signing.dumps({ 89 | 'user_id': 1234, 90 | 'username': 'crazyjoedavaola' 91 | }) 92 | ) 93 | 94 | with self.assertRaises(signing.BadSignature): 95 | self.sut(signature) 96 | 97 | def test_valid_user_signature(self): 98 | """ 99 | Given a valid user signature, the user object should 100 | be returned. 101 | """ 102 | user = mommy.make(get_user_model()) 103 | signature = UserSigner().sign(user) 104 | result = self.sut(signature) 105 | 106 | self.assertEqual(user, result) 107 | 108 | @mock.patch('django.core.signing.loads') 109 | @mock.patch('django.core.signing.TimestampSigner.unsign') 110 | def test_django_signers_called(self, unsign_mock, loads_mock): 111 | """ 112 | Asserts that the method calls Django's signers using 113 | the SIGNED_URL_TTL for the expiry. 114 | """ 115 | user = mommy.make(get_user_model()) 116 | signature = UserSigner().sign(user) 117 | loads_mock.return_value = { 118 | 'user_id': user.pk, 119 | 'username': user.get_username() 120 | } 121 | self.sut(signature) 122 | 123 | unsign_mock.assert_called_once_with(signature, settings.SIGNED_URL_TTL) 124 | 125 | def test_signing_with_custom_user_model(self): 126 | """ 127 | Given a custom user model, the signature should sign 128 | and unsign the user object correctly 129 | """ 130 | user = mommy.make('unit.CustomUser') 131 | 132 | with override_settings(AUTH_USER_MODEL='unit.CustomUser'): 133 | signature = UserSigner().sign(user) 134 | result = self.sut(signature) 135 | 136 | self.assertEqual(user, result) 137 | -------------------------------------------------------------------------------- /tests/unit/test_views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from drf_signed_auth import settings, views 3 | from drf_signed_auth.compat import mock 4 | from model_mommy import mommy 5 | from rest_framework.test import APIRequestFactory, APITestCase 6 | 7 | 8 | class GetSignedUrlTest(APITestCase): 9 | def setUp(self): 10 | factory = APIRequestFactory() 11 | self.request = factory.post('/') 12 | self.request.user = mommy.make(get_user_model()) 13 | 14 | self.sut = views.SignUrlView(request=self.request).get_signed_url 15 | 16 | @mock.patch('drf_signed_auth.signing.UserSigner.sign') 17 | def test_fixx(self, sign_mock): 18 | sign_mock.return_value = 'the-returned-signature' 19 | url = '/my-protected=path?param1=fizz¶m2=buzz' 20 | result = self.sut(url) 21 | expected = 'http://testserver{url}&{param}={sig}'.format( 22 | url=url, 23 | param=settings.SIGNED_URL_QUERY_PARAM, 24 | sig=sign_mock.return_value 25 | ) 26 | 27 | self.assertEqual(expected, result) 28 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | py36-django111-drf37 4 | py36-django110-drf37 5 | py36-django111-drf36 6 | py36-django110-drf36 7 | py36-django19-drf36 8 | py36-django18-drf36 9 | py27-django111-drf37 10 | py27-django110-drf37 11 | py27-django111-drf36 12 | py27-django18-drf36 13 | lint 14 | 15 | 16 | [testenv] 17 | commands = ./runtests.py 18 | deps = 19 | # Django 20 | django111: Django>=1.11,<2 21 | django110: Django>=1.10,<1.11 22 | django19: Django>=1.9,<1.10 23 | django18: Django>=1.8,<1.9 24 | 25 | # DRF 26 | drf36: djangorestframework>=3.6,<3.7 27 | drf37: djangorestframework>=3.7,<3.8 28 | 29 | -rrequirements/testing.txt 30 | 31 | 32 | [testenv:lint] 33 | commands = pylint drf_signed_auth 34 | deps = 35 | Django 36 | djangorestframework 37 | -rrequirements/testing.txt 38 | 39 | --------------------------------------------------------------------------------