Skip to main content

A Django app to manage postcode/home number address forms

Project description

postcodes

A dutch postcode lookup app for Django.

By default we use the free (but rate limited) API from postcode.go-dev.nl.

You must create an account and get an API key to use this package (in the default configuration).

This API key must be set in the settings.

Features

  • Lookup addresses by postcode and home number
  • Customizable form fields
  • Customizable API endpoint
  • Autofill form fields with the address data
  • Validation of form fields when the address is not found
  • Validation of form fields when user input was given (regex, min, max.)

Quick start

  1. Install the package via pip:

    pip install django-postcodes
    
  2. Add 'postcodes' to your INSTALLED_APPS setting like this:

    INSTALLED_APPS = [
    ...,
       'postcodes',
    ]
    
  3. Add the postcodes URL to your project's urls.py:

    path('postcodes/', include('postcodes.urls', namespace='postcodes')),
    

Example usage

First we must define the appropriate form.

This is an example of a form that uses the default API endpoint (custom ones can be defined in the settings):

Every attribute can be customized, except for the postcode and home number fields, which are required for the lookup.

These are hard-coded.

All other elements provided in the bind object will be filled in with the data from the API.

class AddressForm(forms.Form):
    # These are required for the lookup
    postcode = forms.CharField(max_length=10, widget=forms.TextInput(attrs={"placeholder": "1234 AB", "class": "postcode", "pattern": "^[0-9]{4}(\s+|)[A-Z]{2}$"}))
    home_number = forms.CharField(max_length=10, widget=forms.TextInput(attrs={"placeholder": "123", "class": "home_number"}))
    
    # Custom fields
    street = forms.CharField(max_length=255, widget=forms.TextInput(attrs={"placeholder": "Main street", "class": "street"}))
    city = forms.CharField(max_length=255, widget=forms.TextInput(attrs={"placeholder": "Amsterdam", "class": "city"}))
    municipality = forms.CharField(max_length=255, widget=forms.TextInput(attrs={"placeholder": "Amsterdam", "class": "municipality"}))
    province = forms.CharField(max_length=255, widget=forms.TextInput(attrs={"placeholder": "Noord-Holland", "class": "province"}))
    build_year = forms.IntegerField(widget=forms.NumberInput(attrs={"placeholder": "1990", "class": "build_year", "pattern": "^[0-9]{4}$", "min": "1900", "max": "2022"}))
    floor_area = forms.DecimalField(widget=forms.NumberInput(attrs={"placeholder": "100", "class": "floor_area", "pattern": "^[0-9]{1,3}$"})) # "pattern": "^[0-9]{1,3}$"
    geo_x = forms.DecimalField(widget=forms.NumberInput(attrs={"placeholder": "52.123456", "class": "geo_x"}))
    geo_y = forms.DecimalField(widget=forms.NumberInput(attrs={"placeholder": "4.123456", "class": "geo_y"}))
    rd_x = forms.DecimalField(widget=forms.NumberInput(attrs={"placeholder": "123456", "class": "rd_x"})) # Rijksdriehoek
    rd_y = forms.DecimalField(widget=forms.NumberInput(attrs={"placeholder": "123456", "class": "rd_y"})) # Rijksdriehoek

Then we can define our template

{% extends 'base.html' %}

{% block content %}
   <link rel="stylesheet" href="{% static 'postcodes/css/postcodes.css' %}">
   <script src="{% static 'postcodes/js/postcodes.js' %}" data-api-url="{% url "postcodes:api" %}"></script>

   <form method="post">
       {% csrf_token %}
       {{ form.as_p }}
       <button type="submit">Submit</button>
   </form>

   ...

   <script>
       document.addEventListener('DOMContentLoaded', function() {
           lookupPostcode({
               bind: {
                   // These are required for the lookup
                    postcode: document.querySelector('#id_postcode'),
                    home_number: document.querySelector('#id_home_number'),

                    // Custom fields returned by the API
                    straat: document.querySelector("#id_street"),
                    woonplaats: document.querySelector("#id_city"),
                    gemeente: document.querySelector("#id_municipality"),
                    provincie: document.querySelector("#id_province"),
                    bouwjaar: document.querySelector("#id_build_year"),
                    vloeroppervlakte: document.querySelector("#id_floor_area"),

                    // Or optionally as a queryselector
                    // If everything is a string - it is safe to omit the DOMContentLoaded eventListener
                    latitude: "#id_geo_x",
                    longitude: "#id_geo_y",
                    rd_x: "#id_rd_x",
                    rd_y: "#id_rd_y",
               },
               success: function(addr) {
                   console.log(addr);
               },
               error: function(error) {
                   console.log(error);
               }
           })
       });
   </script>

{% endblock %}

The form will now automatically fill in the address fields when a valid postcode and home number is entered.

If it is invalid or not found, the error callback will be called.

Settings

ADDR_VALIDATOR_API_KEY

The API key to use for the postcode lookup.

This will be used by the postcodes.postcode.address_check function.

ADDR_VALIDATOR_API_URL

The API URL to use for the postcode lookup.

This will be used by the postcodes.postcode.address_check function.

It should contain the {postcode} and {home_number} placeholders.

ADDR_VALIDATOR_PARAMETER_FORMAT

An actual function that formats the parameters for the API URL.

This may not be a path to a function, but the function itself.

Example:

def default_parameter_formatter(**kwargs):
    return "&".join([f"{key}={value}" for key, value in kwargs.items()])

ADDR_VALIDATOR_ERROR_ATTRIBUTE

The attribute in the response that contains the error message.

This will be used by the postcodes.postcode.address_check function.

AddressValidationError exceptions will return the appropriate error message if the attribute is found.

ADDR_VALIDATOR_CACHE_TIMEOUT

The timeout for the cache in seconds.

This will be used by the postcodes.postcode.address_check function.

It is highly recommended to set this to a high number; by default it caches for a week.

The default endpoint is free to use, but has a rate limit.

ADDR_VALIDATOR_API_KEY_ATTRIBUTE

The attribute in the response that contains the API key.

This will be used by the postcodes.postcode.address_check function.

It is the header that should be sent with the request.

Defaults to X-API-Token.

ADDR_VALIDATOR_REQUIRES_AUTH

Whether the API requires authentication.

This will be used by the internal view to check if the user is authenticated.

If the user is authenticated; the view will return the address data.

This does not matter if you use the postcodes.postcode.address_check function.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_postcodes-1.0.5.tar.gz (21.7 kB view details)

Uploaded Source

File details

Details for the file django_postcodes-1.0.5.tar.gz.

File metadata

  • Download URL: django_postcodes-1.0.5.tar.gz
  • Upload date:
  • Size: 21.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.0.0 CPython/3.11.6

File hashes

Hashes for django_postcodes-1.0.5.tar.gz
Algorithm Hash digest
SHA256 35f7d35f427a8510086050590aac6c402b4fb3508e1ab5ae81442c18d3fe6636
MD5 2fe9ea2e42e88243ec04a0e5ade9a42c
BLAKE2b-256 ced1688a2b3fa6f5ac874dd9c15a1c3fcee35d39970038d1e5caaca48bda86b8

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page